diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55066aa72..f4bfd33b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,10 +1,8 @@ name: Tests -# Until now nothing ran the test suite on a change: the release workflows -# build the app, web-check.yml type-checks the wasm target, and pull requests -# reported their own local runs. Run the workspace tests on every push to -# main and on every pull request. There is deliberately no formatter check: -# the tree is not rustfmt-clean and is not going to be reformatted. +# Runs the workspace tests on every push to main and on every pull request, +# on Linux, Windows and macOS. A separate lint job reports rustfmt and clippy +# results without blocking merges until the existing tree has been cleaned up. on: push: @@ -20,13 +18,19 @@ concurrency: jobs: test: - runs-on: ubuntu-22.04 + name: test (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04, windows-latest, macos-14] steps: - uses: actions/checkout@v4 # The same libraries the Linux release build needs (windowing, GL, # fonts); the packaging tools are left out. - - name: System libraries + - name: System libraries (Linux) + if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y \ @@ -37,8 +41,38 @@ jobs: - uses: Swatinem/rust-cache@v2 - # LC_ALL=C: a few tests compare English message text. + # LC_ALL=C: a few tests compare English message text. The GPU-backed + # tests are #[ignore]d and need an adapter, so they do not run here. - name: cargo test env: LC_ALL: C run: cargo test --workspace --locked + + lint: + name: lint (informational) + runs-on: ubuntu-22.04 + # Non-blocking until the existing tree is cleaned up. The tree currently has + # thousands of rustfmt diffs and several clippy warnings and errors, so + # these checks report status without failing the workflow. + continue-on-error: true + steps: + - uses: actions/checkout@v4 + + - name: System libraries + run: | + sudo apt-get update + sudo apt-get install -y \ + libgl1-mesa-dev libx11-dev libxcursor-dev libxi-dev libxrandr-dev \ + libxkbcommon-dev libwayland-dev libfontconfig1-dev libfreetype6-dev + + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - uses: Swatinem/rust-cache@v2 + + - name: cargo fmt --check + run: cargo fmt --all -- --check + + - name: cargo clippy + run: cargo clippy --workspace --all-targets --locked diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 79e07fb89..55a752b0c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -511,6 +511,12 @@ jobs: codesign -d --entitlements - --xml OpenCADStudio.app/Contents/PlugIns/DWGThumbnail.appex \ | python3 -c 'import io, plistlib, sys; ent = plistlib.load(io.BytesIO(sys.stdin.buffer.read())); print("entitlements:", ent); assert ent.get("com.apple.security.app-sandbox") is True' + # Exercise CFBundleExecutable, not just the inner application: the + # Finder launcher must preserve --mcp and the client's stdio pipes. + - name: Verify packaged MCP handshake + timeout-minutes: 2 + run: python3 docs/automation/mcp_handshake.py OpenCADStudio.app/Contents/MacOS/OpenCADStudio + - name: Create .dmg run: | set +e diff --git a/Cargo.lock b/Cargo.lock index 78908a489..eb9ef6385 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "OpenCADStudio" -version = "2026.40.1" +version = "2026.41.0" dependencies = [ "allsorts", "ashpd", @@ -4205,7 +4205,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.6.0" -source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=937745d#937745d9ec1a62d252074ff41b140b92303d0dc0" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=8189f83#8189f833574c544fc57a1d773591014c3a5907c9" dependencies = [ "anyhow", "bitflags 2.13.1", @@ -4228,7 +4228,7 @@ dependencies = [ [[package]] name = "opencadgraph" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=81bf45f#81bf45f57cbab5ff3784f293365279bf2d93f758" +source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=f4fdb80#f4fdb800292f53a1454466b638ae0610a2b5b7f0" dependencies = [ "opencadkernel", "serde_json", @@ -4237,7 +4237,7 @@ dependencies = [ [[package]] name = "opencadkernel" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=86497ae#86497ae923b7d9297707aaedb712a8678815a238" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=71770e1#71770e10687f2f5b96745502315f83ca90c5c83f" dependencies = [ "cavalier_contours", "opencadcodec", @@ -4248,7 +4248,7 @@ dependencies = [ [[package]] name = "opencadkernel-constraints" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=86497ae#86497ae923b7d9297707aaedb712a8678815a238" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=71770e1#71770e10687f2f5b96745502315f83ca90c5c83f" dependencies = [ "nalgebra", ] diff --git a/Cargo.toml b/Cargo.toml index 0d21f6f45..992eee086 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "OpenCADStudio" -version = "2026.40.1" +version = "2026.41.0" edition = "2021" build = "build.rs" default-run = "OpenCADStudio" @@ -34,10 +34,10 @@ rfd = "0.17" clap = { version = "4", features = ["derive", "string"] } env_logger = "0.11" log = "0.4" -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "937745d", features = ["serde"] } -kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "86497ae", features = ["acis", "offset"] } -kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "86497ae" } -graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "81bf45f" } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8189f83", features = ["serde"] } +kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "71770e1", features = ["acis", "offset"] } +kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "71770e1" } +graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "f4fdb80" } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } flate2 = "1" image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] } diff --git a/README.md b/README.md index 5759a2bd1..9152258e3 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Italiano · 日本語 · 한국어 · + ລາວ · Polski · Русский · 简体中文 · @@ -136,9 +137,9 @@ The application is ad-hoc signed but is not currently notarized by Apple. ## Languages -Open CAD Studio can follow the system language or use any of these 21 interface languages: +Open CAD Studio can follow the system language or use any of these 22 interface languages: -> Arabic · Brazilian Portuguese · Bulgarian · Czech · Dutch · English · Finnish · French · German · Greek · Hindi · Hungarian · Italian · Japanese · Korean · Polish · Russian · Simplified Chinese · Spanish · Traditional Chinese · Turkish +> Arabic · Brazilian Portuguese · Bulgarian · Czech · Dutch · English · Finnish · French · German · Greek · Hindi · Hungarian · Italian · Japanese · Korean · Lao · Polish · Russian · Simplified Chinese · Spanish · Traditional Chinese · Turkish Change the language from the application settings. The browser version also uses the browser's preferred locale when **System** is selected. diff --git a/assets/ocad.pgp b/assets/ocad.pgp index cf8ba9bc6..bde60d84f 100644 --- a/assets/ocad.pgp +++ b/assets/ocad.pgp @@ -56,6 +56,7 @@ LEN, *LENGTHEN AL, *ALIGN AR, *ARRAY MA, *MATCHPROP +VV, *QLAYER ATE, *ATTEDIT ; ── Clipboard ───────────────────────────────────────────────────────────── diff --git a/benches/command_benchmarks.rs b/benches/command_benchmarks.rs index 3cd183a34..c65439c4a 100644 --- a/benches/command_benchmarks.rs +++ b/benches/command_benchmarks.rs @@ -16,9 +16,7 @@ use codec::types::{Vector2, Vector3}; use codec::{CadDocument, EntityType, Handle}; use harness::BenchmarkRunner; -use OpenCADStudio::modules::draw::modify::explode::{ - apply_explode_replacements, explode_batch, -}; +use OpenCADStudio::modules::draw::modify::explode::{apply_explode_replacements, explode_batch}; use OpenCADStudio::modules::draw::modify::flatten; use OpenCADStudio::scene::Scene; @@ -79,10 +77,8 @@ fn push_fixture_entities(doc: &mut CadDocument, quick: bool) -> Vec { let mut pl = LwPolyline::new(); pl.vertices = (0..VERTS_PER_POLYLINE) .map(|k| { - let mut v = LwVertex::new(Vector2::new( - base + k as f64 * 2.0, - (k % 5) as f64 * 0.75, - )); + let mut v = + LwVertex::new(Vector2::new(base + k as f64 * 2.0, (k % 5) as f64 * 0.75)); if k % 2 == 1 { v.bulge = 0.5; } @@ -101,13 +97,13 @@ fn push_fixture_entities(doc: &mut CadDocument, quick: bool) -> Vec { for i in 0..n_dim { let x = (i % 100) as f64 * 12.0; let y = (i / 100) as f64 * 12.0; - let mut d = DimensionLinear::new( - Vector3::new(x, y, 0.0), - Vector3::new(x + 10.0, y, 0.0), - ); + let mut d = DimensionLinear::new(Vector3::new(x, y, 0.0), Vector3::new(x + 10.0, y, 0.0)); d.definition_point = Vector3::new(x + 5.0, y + 3.0, 0.0); d.base.text_middle_point = Vector3::new(x + 5.0, y + 4.0, 0.0); - handles.push(doc.add_entity(EntityType::Dimension(Dimension::Linear(d))).unwrap()); + handles.push( + doc.add_entity(EntityType::Dimension(Dimension::Linear(d))) + .unwrap(), + ); } for i in 0..n_ml { @@ -121,7 +117,10 @@ fn push_fixture_entities(doc: &mut CadDocument, quick: bool) -> Vec { Vector3::new(x + 8.0, y + 8.0, 0.0), Vector3::new(x + 12.0, y + 8.0, 0.0), ]; - handles.push(doc.add_entity(EntityType::MLine(MLine::from_points(&pts))).unwrap()); + handles.push( + doc.add_entity(EntityType::MLine(MLine::from_points(&pts))) + .unwrap(), + ); } handles @@ -274,7 +273,8 @@ fn push_flatten_fixture(doc: &mut CadDocument, quick: bool) -> Vec { handles.push(doc.add_entity(EntityType::Line(line)).unwrap()); } for i in 0..n_circle { - let mut c = Circle::from_center_radius(Vector3::new(i as f64, 0.0, 1.0 + (i % 3) as f64), 2.0); + let mut c = + Circle::from_center_radius(Vector3::new(i as f64, 0.0, 1.0 + (i % 3) as f64), 2.0); if i % 2 == 1 { c.normal = Vector3::new(0.0, 0.6, 0.8); // tilted → ellipse } @@ -296,7 +296,8 @@ fn push_flatten_fixture(doc: &mut CadDocument, quick: bool) -> Vec { let mut pl = Polyline2D::new(); pl.elevation = 3.0 + (i % 5) as f64; for k in 0..6 { - pl.vertices.push(Vertex2D::new(Vector3::new(k as f64, i as f64, 0.0))); + pl.vertices + .push(Vertex2D::new(Vector3::new(k as f64, i as f64, 0.0))); } handles.push(doc.add_entity(EntityType::Polyline2D(pl)).unwrap()); } @@ -321,7 +322,10 @@ fn push_flatten_fixture(doc: &mut CadDocument, quick: bool) -> Vec { handles.push(doc.add_entity(EntityType::Solid(s)).unwrap()); } for i in 0..n_ray { - let r = Ray::new(Vector3::new(i as f64, 0.0, 5.0), Vector3::new(0.0, 0.0, 1.0)); + let r = Ray::new( + Vector3::new(i as f64, 0.0, 5.0), + Vector3::new(0.0, 0.0, 1.0), + ); handles.push(doc.add_entity(EntityType::Ray(r)).unwrap()); } for i in 0..n_flat { diff --git a/benches/performance_benchmarks.rs b/benches/performance_benchmarks.rs index dede3f7c8..4fc63a86e 100644 --- a/benches/performance_benchmarks.rs +++ b/benches/performance_benchmarks.rs @@ -59,8 +59,8 @@ use OpenCADStudio::scene::{ChangeKind, Scene}; use OpenCADStudio::snap::Snapper; use OpenCADStudio::ui::icons::{self, CHECK}; use OpenCADStudio::ui::overlay::{ - grid_segments, selection_overlay, should_reuse, CrosshairOptions, GridCanvasState, - GridKey, GridParams, GridStyle, GripMarker, NavCursor, OstTrackPoint, SelectionVisualOptions, + grid_segments, selection_overlay, should_reuse, CrosshairOptions, GridCanvasState, GridKey, + GridParams, GridStyle, GripMarker, NavCursor, OstTrackPoint, SelectionVisualOptions, UcsIconParams, }; use OpenCADStudio::ui::properties::LinetypeItem; @@ -714,7 +714,7 @@ fn bench_selection_cloning(runner: &mut BenchmarkRunner) { return; } - let mut state = SelectionState { + let state = SelectionState { view: SelectionView { vp_size: (1920.0, 1080.0), }, @@ -914,10 +914,26 @@ fn bench_ui_grid_geometry(runner: &mut BenchmarkRunner) { if runner.should_run("ui_grid_geometry_uncached") { for _ in 0..10 { let _ = black_box(grid_segments( - view_rot1, eye1, bounds1, step1, step1, major_every, origin1, axes1, limits1, + view_rot1, + eye1, + bounds1, + step1, + step1, + major_every, + origin1, + axes1, + limits1, )); let _ = black_box(grid_segments( - view_rot2, eye2, bounds2, step2, step2, major_every, origin2, axes2, limits2, + view_rot2, + eye2, + bounds2, + step2, + step2, + major_every, + origin2, + axes2, + limits2, )); } @@ -928,8 +944,28 @@ fn bench_ui_grid_geometry(runner: &mut BenchmarkRunner) { for _ in 0..runs { let t0 = Instant::now(); for _ in 0..n { - let g1 = grid_segments(view_rot1, eye1, bounds1, step1, step1, major_every, origin1, axes1, limits1); - let g2 = grid_segments(view_rot2, eye2, bounds2, step2, step2, major_every, origin2, axes2, limits2); + let g1 = grid_segments( + view_rot1, + eye1, + bounds1, + step1, + step1, + major_every, + origin1, + axes1, + limits1, + ); + let g2 = grid_segments( + view_rot2, + eye2, + bounds2, + step2, + step2, + major_every, + origin2, + axes2, + limits2, + ); black_box(g1); black_box(g2); } @@ -1302,7 +1338,10 @@ fn bench_ui_grip_budget(runner: &mut BenchmarkRunner) { ), "µs", samples, - Some(((capped_grips.len() as f64) / (median_us / 1_000_000.0), "grips/s")), + Some(( + (capped_grips.len() as f64) / (median_us / 1_000_000.0), + "grips/s", + )), Some(500.0), // Target threshold < 500 µs (measured ~132 µs quick / ~147 µs full) ); @@ -1337,7 +1376,10 @@ fn bench_ui_grip_budget(runner: &mut BenchmarkRunner) { ), "µs", build_samples, - Some(((n_fixture_grips as f64) / (build_median_us / 1_000_000.0), "grips/s")), + Some(( + (n_fixture_grips as f64) / (build_median_us / 1_000_000.0), + "grips/s", + )), Some(7_000.0), // Target < 7 ms (selection-change hitch budget; measured ~2.36 ms full / ~0.30 ms quick, ~3x headroom) ); } @@ -1386,18 +1428,13 @@ fn bench_ui_constraint_glyphs(runner: &mut BenchmarkRunner) { let vp = (1920.0_f32, 1080.0_f32); // Prime the cache so every timed call below is a hit (same key, same Arc). - let primed = - scene.cached_glyph_placements(ParametricScope::ModelSpace, vp, true, 3, 4095); - assert!( - !primed.is_empty(), - "glyph fixture must yield placements" - ); + let primed = scene.cached_glyph_placements(ParametricScope::ModelSpace, vp, true, 3, 4095); + assert!(!primed.is_empty(), "glyph fixture must yield placements"); let n_glyphs = primed.len(); // Warm-up for allocator settling. for _ in 0..10 { - let hit = - scene.cached_glyph_placements(ParametricScope::ModelSpace, vp, true, 3, 4095); + let hit = scene.cached_glyph_placements(ParametricScope::ModelSpace, vp, true, 3, 4095); black_box(hit); } @@ -1499,7 +1536,12 @@ fn build_selection_overlay_element( .collect(); let ucs_icons = vec![UcsIconParams { view_proj: Mat4::IDENTITY, - bounds: Rectangle { x: 0.0, y: 0.0, width: 1920.0, height: 1080.0 }, + bounds: Rectangle { + x: 0.0, + y: 0.0, + width: 1920.0, + height: 1080.0, + }, axes: (Vec3::X, Vec3::Y, Vec3::Z), origin_screen: None, hover: false, @@ -1516,7 +1558,9 @@ fn build_selection_overlay_element( None, None, ucs_icons, - vec![OstTrackPoint { screen: Point::new(500.0, 500.0) }], + vec![OstTrackPoint { + screen: Point::new(500.0, 500.0), + }], vec![(Point::new(0.0, 0.0), Point::new(100.0, 100.0))], None, true, @@ -2025,6 +2069,177 @@ fn bench_view_render_viewport_construction(runner: &mut BenchmarkRunner) { ); } +// ── 19. Viewport Navigation & Spatial Candidate Scaling ───────────────────── + +fn bench_navigation_latency_and_coalescing(runner: &mut BenchmarkRunner) { + if !runner.should_run("navigation") + && !runner.should_run("nav_") + && !runner.should_run("quadtree") + { + return; + } + + // Fixture: populated scene with mixed entities + let n_entities = if runner.quick_mode { 1_000 } else { 5_000 }; + let mut scene = Scene::new(); + for i in 0..n_entities { + let x = (i % 100) as f64 * 20.0; + let y = (i / 100) as f64 * 20.0; + let mut line = Line::new(); + line.start = Vector3::new(x, y, 0.0); + line.end = Vector3::new(x + 15.0, y + 15.0, 0.0); + scene.add_entity(EntityType::Line(line)); + } + + let bounds = Rectangle { + x: 0.0, + y: 0.0, + width: 1920.0, + height: 1080.0, + }; + let mut cam = Camera::default(); + + // Warm up viewports + let _ = scene.build_viewports( + bounds, + codec::entities::ViewportRenderMode::Wireframe2D, + None, + false, + false, + [1.0, 1.0, 1.0, 1.0], + ); + + let n_pans = if runner.quick_mode { 2_000 } else { 10_000 }; + let runs = 5; + + // 1. Pure Camera Pan Calculation + Viewport update (matrix update & generation bump) + let mut pan_samples = Vec::with_capacity(runs); + for _ in 0..runs { + let t0 = Instant::now(); + for step in 0..n_pans { + let dx = (step % 10) as f32 * 2.0; + let dy = (step % 5) as f32 * 2.0; + cam.pan_screen(dx, dy, bounds.height); + let primitive = scene.build_viewports( + bounds, + codec::entities::ViewportRenderMode::Wireframe2D, + None, + false, + false, + [1.0, 1.0, 1.0, 1.0], + ); + black_box(primitive); + } + let per_us = (t0.elapsed().as_micros() as f64) / (n_pans as f64); + pan_samples.push(per_us); + } + let median_pan_us = pan_samples[pan_samples.len() / 2]; + runner.record( + "nav_camera_pan_viewport_update", + "Camera pan_screen + build_viewports (pure GPU uniform update)", + "µs", + pan_samples, + Some((1_000_000.0 / median_pan_us, "pans/s")), + Some(50.0), // Target < 50 µs (< 0.05 ms) + ); + + // 2. Spatial Hover Candidate Search (R-tree spatial query during idle cursor sweep) + let wires = scene.entity_wires(); + let (view_rot, eye) = (cam.view_proj_rte(bounds), cam.eye()); + let n_hover = if runner.quick_mode { 500 } else { 2_000 }; + let mut hover_samples = Vec::with_capacity(runs); + for _ in 0..runs { + let t0 = Instant::now(); + for i in 0..n_hover { + let cursor = DVec3::new((i % 200) as f64 * 10.0, ((i / 2) % 200) as f64 * 10.0, 0.0); + let candidates = scene.interaction_hover_candidates_near( + Arc::clone(&wires), + cursor, + view_rot, + eye, + bounds, + 10.0, + ); + black_box(candidates); + } + let per_us = (t0.elapsed().as_micros() as f64) / (n_hover as f64); + hover_samples.push(per_us); + } + let median_hover_us = hover_samples[hover_samples.len() / 2]; + runner.record( + "nav_spatial_hover_candidate_search", + "Spatial hover candidate search (R-tree candidate culling for cursor movement)", + "µs", + hover_samples, + Some((1_000_000.0 / median_hover_us, "queries/s")), + Some(30.0), // Target < 30 µs (< 0.03 ms) + ); + + // 3. Viewport Assembly Scaling (build_viewports on active camera_generation bump) + let n_frames = if runner.quick_mode { 200 } else { 1_000 }; + let mut assemble_samples = Vec::with_capacity(runs); + for _ in 0..runs { + let t0 = Instant::now(); + for _ in 0..n_frames { + scene.camera_generation += 1; + let prim = scene.build_viewports( + bounds, + codec::entities::ViewportRenderMode::Wireframe2D, + None, + true, + true, + [1.0, 1.0, 1.0, 1.0], + ); + black_box(prim); + } + let per_us = (t0.elapsed().as_micros() as f64) / (n_frames as f64); + assemble_samples.push(per_us); + } + let median_assemble_us = assemble_samples[assemble_samples.len() / 2]; + runner.record( + "nav_viewport_assembly_scaling", + "ViewportData assembly per camera_generation change (live navigation frame prep)", + "µs", + assemble_samples, + Some((1_000_000.0 / median_assemble_us, "frames/s")), + Some(500.0), // Target < 500 µs (0.5 ms) for 5k entities + ); + + // 4. QuadTree Spatial Index Build + let n_tree_items = if runner.quick_mode { 2_000 } else { 100_000 }; + let tree_items: Vec<(codec::Handle, [f64; 4])> = (0..n_tree_items) + .map(|i| { + // Clustered distribution: 80% of entities share tight bounding clusters + let cluster = (i % 10) as f64 * 50.0; + let offset = (i as f64 * 0.05) % 10.0; + let x = cluster + offset; + let y = cluster + offset; + (codec::Handle::from(i as u64), [x, y, x + 5.0, y + 5.0]) + }) + .collect(); + let tree_bounds = [0.0, 0.0, 1000.0, 1000.0]; + + let mut inc_samples = Vec::with_capacity(runs); + for _ in 0..runs { + let t0 = Instant::now(); + let mut tree = OpenCADStudio::scene::pick::quadtree::QuadTree::new(tree_bounds); + for &(handle, aabb) in &tree_items { + tree.insert(handle, aabb); + } + black_box(tree); + inc_samples.push(t0.elapsed().as_secs_f64() * 1000.0); + } + let median_inc_ms = inc_samples[inc_samples.len() / 2]; + runner.record( + "nav_spatial_quadtree_build", + "QuadTree spatial index construction across 100k clustered entities", + "ms", + inc_samples, + Some(((n_tree_items as f64) / (median_inc_ms / 1000.0), "items/s")), + Some(100.0), + ); +} + // ── Main Entrypoint ───────────────────────────────────────────────────────── fn main() { @@ -2062,6 +2277,7 @@ fn main() { bench_draworder_evaluation(&mut runner); bench_undo_delta_recording(&mut runner); bench_view_render_viewport_construction(&mut runner); + bench_navigation_latency_and_coalescing(&mut runner); runner.finish(); } diff --git a/build.rs b/build.rs index 6c69b391a..50cd80b6e 100644 --- a/build.rs +++ b/build.rs @@ -26,8 +26,8 @@ fn main() { println!("cargo:rerun-if-changed=.git/{reference}"); } } - let revision = git_output(&["rev-parse", "--short=12", "HEAD"]) - .unwrap_or_else(|| "unknown".to_string()); + let revision = + git_output(&["rev-parse", "--short=12", "HEAD"]).unwrap_or_else(|| "unknown".to_string()); // diff-index exits 1 for differences and 128 when there is no repository, // so only a real difference marks the build dirty. let dirty = std::process::Command::new("git") @@ -59,8 +59,8 @@ fn main() { "cargo:rustc-env=OCS_TAG_DISTANCE={}", tag_distance.map_or_else(|| "unknown".to_string(), |n| n.to_string()) ); - let commit_date = git_output(&["log", "-1", "--format=%cs", "HEAD"]) - .unwrap_or_else(|| "unknown".to_string()); + let commit_date = + git_output(&["log", "-1", "--format=%cs", "HEAD"]).unwrap_or_else(|| "unknown".to_string()); println!("cargo:rustc-env=OCS_COMMIT_DATE={commit_date}"); // Semver-style build metadata: empty for a clean build on the release diff --git a/crates/dwg-thumbnailer-win/src/lib.rs b/crates/dwg-thumbnailer-win/src/lib.rs index 3d294726d..6c481d0c9 100644 --- a/crates/dwg-thumbnailer-win/src/lib.rs +++ b/crates/dwg-thumbnailer-win/src/lib.rs @@ -40,7 +40,7 @@ use windows::Win32::System::SystemServices::{DLL_PROCESS_ATTACH, DLL_PROCESS_DET use windows::Win32::UI::Shell::PropertiesSystem::{IInitializeWithFile, IInitializeWithFile_Impl}; use windows::Win32::UI::Shell::{ IThumbnailProvider, IThumbnailProvider_Impl, SHChangeNotify, SHCNE_ASSOCCHANGED, SHCNF_IDLIST, - WTS_ALPHATYPE, WTSAT_ARGB, + WTSAT_ARGB, WTS_ALPHATYPE, }; /// CLSID of this thumbnail provider (stable; used in the registry keys). @@ -72,7 +72,8 @@ struct DwgThumbProvider { impl IInitializeWithFile_Impl for DwgThumbProvider_Impl { fn Initialize(&self, pszfilepath: &PCWSTR, _grfmode: u32) -> windows::core::Result<()> { - let path = unsafe { pszfilepath.to_string() }.map_err(|_| windows::core::Error::from(E_INVALIDARG))?; + let path = unsafe { pszfilepath.to_string() } + .map_err(|_| windows::core::Error::from(E_INVALIDARG))?; *self.path.borrow_mut() = Some(path); Ok(()) } @@ -85,7 +86,11 @@ impl IThumbnailProvider_Impl for DwgThumbProvider_Impl { phbmp: *mut HBITMAP, pdwalpha: *mut WTS_ALPHATYPE, ) -> windows::core::Result<()> { - let path = self.path.borrow().clone().ok_or(windows::core::Error::from(E_FAIL))?; + let path = self + .path + .borrow() + .clone() + .ok_or(windows::core::Error::from(E_FAIL))?; let mut img = dwg_thumbnailer::extract(std::path::Path::new(&path), cx.max(1)) .ok_or(windows::core::Error::from(E_FAIL))?; dwg_thumbnailer::badge_dwg(&mut img); // full-width "DWG" band @@ -248,13 +253,14 @@ fn set_value(sub: &str, name: Option<&str>, value: &str) -> windows::core::Resul return Err(E_FAIL.into()); } let val_w = wide(value); - let bytes = - unsafe { std::slice::from_raw_parts(val_w.as_ptr() as *const u8, val_w.len() * 2) }; + let bytes = unsafe { std::slice::from_raw_parts(val_w.as_ptr() as *const u8, val_w.len() * 2) }; let name_w = name.map(wide); let rc = unsafe { RegSetValueExW( hkey, - name_w.as_ref().map_or(PCWSTR::null(), |n| PCWSTR(n.as_ptr())), + name_w + .as_ref() + .map_or(PCWSTR::null(), |n| PCWSTR(n.as_ptr())), 0, REG_SZ, Some(bytes), diff --git a/crates/dwg-thumbnailer/src/lib.rs b/crates/dwg-thumbnailer/src/lib.rs index 28deb0399..a94186049 100644 --- a/crates/dwg-thumbnailer/src/lib.rs +++ b/crates/dwg-thumbnailer/src/lib.rs @@ -74,14 +74,14 @@ pub fn badge_dwg(img: &mut RgbaImage) { let label = label.to_rgba8(); if label.width() > 0 && label.height() > 0 { let mut target_h = ((band_h as f32 * 0.72) as u32).max(1); - let mut target_w = ((label.width() as f32 * target_h as f32 / label.height() as f32) - as u32) - .max(1); + let mut target_w = + ((label.width() as f32 * target_h as f32 / label.height() as f32) as u32).max(1); // Don't let a wide wordmark spill past the thumbnail edges. let max_w = ((w as f32 * 0.90) as u32).max(1); if target_w > max_w { target_w = max_w; - target_h = ((label.height() as f32 * target_w as f32 / label.width() as f32) as u32) + target_h = ((label.height() as f32 * target_w as f32 / label.width() as f32) + as u32) .max(1); } let label = image::imageops::thumbnail(&label, target_w, target_h); @@ -197,7 +197,9 @@ fn parse_preview_container(buf: &[u8], base: u64) -> Option<(Fmt, &[u8])> { fn decode(format: Fmt, data: &[u8]) -> Option { let img = match format { Fmt::Png => image::load_from_memory_with_format(data, ImageFormat::Png).ok()?, - Fmt::Bmp => image::load_from_memory_with_format(&dib_to_bmp(data), ImageFormat::Bmp).ok()?, + Fmt::Bmp => { + image::load_from_memory_with_format(&dib_to_bmp(data), ImageFormat::Bmp).ok()? + } }; Some(img.to_rgba8()) } @@ -235,7 +237,9 @@ pub unsafe extern "C" fn dwg_thumbnail_png( return false; } let cstr = std::ffi::CStr::from_ptr(path_utf8); - let Ok(path) = cstr.to_str() else { return false }; + let Ok(path) = cstr.to_str() else { + return false; + }; let Some(mut img) = extract(Path::new(path), max_dim) else { return false; }; @@ -271,7 +275,11 @@ fn dib_to_bmp(dib: &[u8]) -> Vec { } let bi_size = u32::from_le_bytes([dib[0], dib[1], dib[2], dib[3]]) as usize; let bpp = u16::from_le_bytes([dib[14], dib[15]]) as usize; - let palette = if (1..=8).contains(&bpp) { (1usize << bpp) * 4 } else { 0 }; + let palette = if (1..=8).contains(&bpp) { + (1usize << bpp) * 4 + } else { + 0 + }; let mut v = Vec::with_capacity(14 + dib.len()); v.extend_from_slice(b"BM"); v.extend_from_slice(&((14 + dib.len()) as u32).to_le_bytes()); diff --git a/crates/ocs_plugin_api/Cargo.toml b/crates/ocs_plugin_api/Cargo.toml index 9225013d5..6667920d1 100644 --- a/crates/ocs_plugin_api/Cargo.toml +++ b/crates/ocs_plugin_api/Cargo.toml @@ -15,7 +15,7 @@ serde_json = { version = "1", optional = true } # Pulled in only by the `host` feature, which adds the `opencadcodec`-typed # `HostApi` runtime surface. The default crate stays dependency-free so engine # crates and external tooling can depend on the manifest/ribbon contract cheaply. -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "937745d", optional = true, features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8189f83", optional = true, features = ["serde"] } # Runtime IPC and serialization (host feature only). interprocess = { version = "2", optional = true } @@ -39,7 +39,7 @@ serde_json = "1" serde = { version = "1", features = ["derive"] } cargo-lock = "11" # opencadcodec is scanned at build time to generate the embedded type registry. -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "937745d", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8189f83", features = ["serde"] } [dev-dependencies] serde_json = "1" diff --git a/crates/ocs_plugin_api/build.rs b/crates/ocs_plugin_api/build.rs index dda5ba997..0c1e31246 100644 --- a/crates/ocs_plugin_api/build.rs +++ b/crates/ocs_plugin_api/build.rs @@ -68,10 +68,7 @@ fn generate_type_registry(out_dir: &Path) { let types: Vec<(&str, TraceFn)> = vec![ ("EntityType", trace::), ("ObjectType", trace::), - ( - "HeaderVariables", - trace::, - ), + ("HeaderVariables", trace::), ("SummaryInfo", trace::), ("LineType", trace::), ("TextStyle", trace::), @@ -88,10 +85,7 @@ fn generate_type_registry(out_dir: &Path) { trace::, ), ("FieldDef", trace::), - ( - "DgnLsDefinition", - trace::, - ), + ("DgnLsDefinition", trace::), ("DgnLsComponent", trace::), ( "NotificationCollection", @@ -182,10 +176,7 @@ fn generate_type_registry(out_dir: &Path) { "AssocViewObjectActionParamKind", trace_simple::, ), - ( - "AcisVersion", - trace_simple::, - ), + ("AcisVersion", trace_simple::), ( "AssociativeData", trace_simple::, @@ -365,10 +356,7 @@ fn generate_type_registry(out_dir: &Path) { "PlotPaperUnits", trace_simple::, ), - ( - "PlotRotation", - trace_simple::, - ), + ("PlotRotation", trace_simple::), ("PlotType", trace_simple::), ( "PreviewFormat", @@ -403,14 +391,8 @@ fn generate_type_registry(out_dir: &Path) { "SolidHistoryOperation", trace_simple::, ), - ( - "SurfaceData", - trace_simple::, - ), - ( - "SurfaceKind", - trace_simple::, - ), + ("SurfaceData", trace_simple::), + ("SurfaceKind", trace_simple::), ( "SurfaceSmoothType", trace_simple::, @@ -525,7 +507,7 @@ fn generate_entity_coverage(out_dir: &Path, registry: &TypeRegistry) { use std::collections::HashSet; let policy: EntityCoveragePolicy = serde_json::from_str(include_str!("entity_coverage_policy.json")) - .expect("valid entity coverage policy"); + .expect("valid entity coverage policy"); let variants = ®istry.types[&TypeId::new("EntityType")].variants; let variant_names: HashSet<&str> = variants.iter().map(|v| v.name.as_str()).collect(); for name in policy @@ -814,7 +796,7 @@ fn generate_entity_coverage(out_dir: &Path, registry: &TypeRegistry) { | "vertices" ) ) { - "transaction_geometry" + "transaction_geometry" } else { "type_conversion_only" }; @@ -842,8 +824,17 @@ fn generate_entity_coverage(out_dir: &Path, registry: &TypeRegistry) { optional: false, is_sequence: false, snapshot_readable: true, - validation: if writable { "transaction_geometry" } else { "none" }.into(), - model_access: if writable { ModelAccess::ReadWrite } else { ModelAccess::ReadOnly }, + validation: if writable { + "transaction_geometry" + } else { + "none" + } + .into(), + model_access: if writable { + ModelAccess::ReadWrite + } else { + ModelAccess::ReadOnly + }, }); } assert!( @@ -864,7 +855,7 @@ fn generate_entity_coverage(out_dir: &Path, registry: &TypeRegistry) { out_dir.join("entity_coverage.json"), serde_json::to_string_pretty(&catalog).unwrap(), ) - .expect("write entity coverage"); + .expect("write entity coverage"); } fn trace(tracer: &mut Tracer, samples: &Samples) @@ -910,10 +901,7 @@ fn add_enum_samples(tracer: &mut Tracer, samples: &mut Samples) { samples, &codec::entities::SmoothSurfaceType::QuadraticBSpline, ); - let _ = tracer.trace_value( - samples, - &codec::entities::SmoothSurfaceType::CubicBSpline, - ); + let _ = tracer.trace_value(samples, &codec::entities::SmoothSurfaceType::CubicBSpline); let _ = tracer.trace_value(samples, &codec::entities::SmoothSurfaceType::Bezier); let _ = tracer.trace_value(samples, &codec::entities::AttachmentPoint::TopLeft); @@ -938,14 +926,8 @@ fn add_enum_samples(tracer: &mut Tracer, samples: &mut Samples) { samples, &codec::xdata::XDataValue::ControlString(String::new()), ); - let _ = tracer.trace_value( - samples, - &codec::xdata::XDataValue::LayerName(String::new()), - ); - let _ = tracer.trace_value( - samples, - &codec::xdata::XDataValue::BinaryData(Vec::new()), - ); + let _ = tracer.trace_value(samples, &codec::xdata::XDataValue::LayerName(String::new())); + let _ = tracer.trace_value(samples, &codec::xdata::XDataValue::BinaryData(Vec::new())); let _ = tracer.trace_value( samples, &codec::xdata::XDataValue::Handle(codec::Handle::default()), @@ -995,14 +977,8 @@ fn add_enum_samples(tracer: &mut Tracer, samples: &mut Samples) { // Proxy reference kinds pulled in by XRecord.object_references let _ = tracer.trace_value(samples, &codec::objects::ProxyReferenceKind::Undefined); - let _ = tracer.trace_value( - samples, - &codec::objects::ProxyReferenceKind::SoftOwnership, - ); - let _ = tracer.trace_value( - samples, - &codec::objects::ProxyReferenceKind::HardOwnership, - ); + let _ = tracer.trace_value(samples, &codec::objects::ProxyReferenceKind::SoftOwnership); + let _ = tracer.trace_value(samples, &codec::objects::ProxyReferenceKind::HardOwnership); let _ = tracer.trace_value(samples, &codec::objects::ProxyReferenceKind::SoftPointer); let _ = tracer.trace_value(samples, &codec::objects::ProxyReferenceKind::HardPointer); diff --git a/crates/ocs_plugin_api/src/entity_coverage.rs b/crates/ocs_plugin_api/src/entity_coverage.rs index b04b14ace..aa2901297 100644 --- a/crates/ocs_plugin_api/src/entity_coverage.rs +++ b/crates/ocs_plugin_api/src/entity_coverage.rs @@ -62,7 +62,9 @@ fn insert_scale(name: &str, value: f64) -> Result<(), String> { } #[cfg(feature = "host")] -fn validate_hatch_paths(paths: &[crate::host::codec::entities::BoundaryPath]) -> Result<(), String> { +fn validate_hatch_paths( + paths: &[crate::host::codec::entities::BoundaryPath], +) -> Result<(), String> { use crate::host::codec::entities::BoundaryEdge; use crate::host::codec::types::Vector2; let close = |a: Vector2, b: Vector2| (a.x - b.x).hypot(a.y - b.y) <= 1e-8; @@ -89,11 +91,19 @@ fn validate_hatch_paths(paths: &[crate::host::codec::entities::BoundaryPath]) -> for (ei, edge) in path.edges.iter().enumerate() { let pair = match edge { BoundaryEdge::Line(line) => { - if !line.start.x.is_finite() || !line.start.y.is_finite() || !line.end.x.is_finite() || !line.end.y.is_finite() { - return Err(format!("Hatch boundary path {pi} edge {ei} line has non-finite coordinates")); + if !line.start.x.is_finite() + || !line.start.y.is_finite() + || !line.end.x.is_finite() + || !line.end.y.is_finite() + { + return Err(format!( + "Hatch boundary path {pi} edge {ei} line has non-finite coordinates" + )); } if (line.start.x - line.end.x).hypot(line.start.y - line.end.y) < 1e-12 { - return Err(format!("Hatch boundary path {pi} edge {ei} line has zero length")); + return Err(format!( + "Hatch boundary path {pi} edge {ei} line has zero length" + )); } (line.start, line.end) } @@ -105,34 +115,65 @@ fn validate_hatch_paths(paths: &[crate::host::codec::entities::BoundaryPath]) -> return Err(format!("Hatch boundary path {pi} edge {ei} arc radius must be finite and positive")); } if !arc.start_angle.is_finite() || !arc.end_angle.is_finite() { - return Err(format!("Hatch boundary path {pi} edge {ei} arc angles must be finite")); + return Err(format!( + "Hatch boundary path {pi} edge {ei} arc angles must be finite" + )); } - let point = |angle: f64| Vector2::new( - arc.center.x + arc.radius * angle.cos(), - arc.center.y + arc.radius * angle.sin(), - ); + let point = |angle: f64| { + Vector2::new( + arc.center.x + arc.radius * angle.cos(), + arc.center.y + arc.radius * angle.sin(), + ) + }; let (start, end) = (point(arc.start_angle), point(arc.end_angle)); - if arc.counter_clockwise { (start, end) } else { (end, start) } + if arc.counter_clockwise { + (start, end) + } else { + (end, start) + } } BoundaryEdge::EllipticArc(arc) => { if !arc.center.x.is_finite() || !arc.center.y.is_finite() { return Err(format!("Hatch boundary path {pi} edge {ei} ellipse center has non-finite coordinates")); } - if !arc.major_axis_endpoint.x.is_finite() || !arc.major_axis_endpoint.y.is_finite() { + if !arc.major_axis_endpoint.x.is_finite() + || !arc.major_axis_endpoint.y.is_finite() + { return Err(format!("Hatch boundary path {pi} edge {ei} ellipse major axis has non-finite coordinates")); } if arc.major_axis_endpoint.x.hypot(arc.major_axis_endpoint.y) < 1e-12 { - return Err(format!("Hatch boundary path {pi} edge {ei} ellipse major axis has zero length")); + return Err(format!( + "Hatch boundary path {pi} edge {ei} ellipse major axis has zero length" + )); } - if !arc.minor_axis_ratio.is_finite() || !(0.0..=1.0).contains(&arc.minor_axis_ratio) || arc.minor_axis_ratio == 0.0 { + if !arc.minor_axis_ratio.is_finite() + || !(0.0..=1.0).contains(&arc.minor_axis_ratio) + || arc.minor_axis_ratio == 0.0 + { return Err(format!("Hatch boundary path {pi} edge {ei} ellipse ratio must be within (0, 1]")); } if !arc.start_angle.is_finite() || !arc.end_angle.is_finite() { - return Err(format!("Hatch boundary path {pi} edge {ei} ellipse angles must be finite")); + return Err(format!( + "Hatch boundary path {pi} edge {ei} ellipse angles must be finite" + )); + } + let start = ellipse_point( + arc.center, + arc.major_axis_endpoint, + arc.minor_axis_ratio, + arc.start_angle, + ); + let end = ellipse_point( + arc.center, + arc.major_axis_endpoint, + arc.minor_axis_ratio, + arc.end_angle, + ); + if arc.counter_clockwise { + (start, end) + } else { + (end, start) } - let start = ellipse_point(arc.center, arc.major_axis_endpoint, arc.minor_axis_ratio, arc.start_angle); - let end = ellipse_point(arc.center, arc.major_axis_endpoint, arc.minor_axis_ratio, arc.end_angle); - if arc.counter_clockwise { (start, end) } else { (end, start) } } BoundaryEdge::Polyline(poly) => { if poly.vertices.len() < 3 { @@ -144,14 +185,21 @@ fn validate_hatch_paths(paths: &[crate::host::codec::entities::BoundaryPath]) -> } } if !poly.is_closed { - return Err(format!("Hatch boundary path {pi} edge {ei} polyline is open")); + return Err(format!( + "Hatch boundary path {pi} edge {ei} polyline is open" + )); } let first = poly.vertices.first().unwrap(); - (Vector2::new(first.x, first.y), Vector2::new(first.x, first.y)) + ( + Vector2::new(first.x, first.y), + Vector2::new(first.x, first.y), + ) } BoundaryEdge::Spline(spline) => { if spline.degree < 1 { - return Err(format!("Hatch boundary path {pi} edge {ei} spline degree must be at least 1")); + return Err(format!( + "Hatch boundary path {pi} edge {ei} spline degree must be at least 1" + )); } if spline.control_points.len() < spline.degree as usize + 1 { return Err(format!("Hatch boundary path {pi} edge {ei} spline has too few control points for its degree")); @@ -171,7 +219,9 @@ fn validate_hatch_paths(paths: &[crate::host::codec::entities::BoundaryPath]) -> return Err(format!("Hatch boundary path {pi} edge {ei} fit point {fi} has non-finite coordinates")); } } - for (name, tangent) in [("start", spline.start_tangent), ("end", spline.end_tangent)] { + for (name, tangent) in + [("start", spline.start_tangent), ("end", spline.end_tangent)] + { if !tangent.x.is_finite() || !tangent.y.is_finite() { return Err(format!("Hatch boundary path {pi} edge {ei} spline {name} tangent is non-finite")); } @@ -179,10 +229,16 @@ fn validate_hatch_paths(paths: &[crate::host::codec::entities::BoundaryPath]) -> let points = if spline.fit_points.len() >= 2 { spline.fit_points.clone() } else { - spline.control_points.iter().map(|p| Vector2::new(p.x, p.y)).collect() + spline + .control_points + .iter() + .map(|p| Vector2::new(p.x, p.y)) + .collect() }; if points.len() < 2 { - return Err(format!("Hatch boundary path {pi} edge {ei} spline has too few points")); + return Err(format!( + "Hatch boundary path {pi} edge {ei} spline has too few points" + )); } (*points.first().unwrap(), *points.last().unwrap()) } @@ -192,7 +248,9 @@ fn validate_hatch_paths(paths: &[crate::host::codec::entities::BoundaryPath]) -> for ei in 0..endpoints.len() { let next = (ei + 1) % endpoints.len(); if !close(endpoints[ei].1, endpoints[next].0) { - return Err(format!("Hatch boundary path {pi} is not closed between edges {ei} and {next}")); + return Err(format!( + "Hatch boundary path {pi} is not closed between edges {ei} and {next}" + )); } } } @@ -218,14 +276,20 @@ fn validate_hatch_pattern(value: &crate::host::codec::entities::Hatch) -> Result } for (index, line) in value.pattern.lines.iter().enumerate() { if !line.angle.is_finite() - || !line.base_point.x.is_finite() || !line.base_point.y.is_finite() - || !line.offset.x.is_finite() || !line.offset.y.is_finite() + || !line.base_point.x.is_finite() + || !line.base_point.y.is_finite() + || !line.offset.x.is_finite() + || !line.offset.y.is_finite() || line.dash_lengths.iter().any(|dash| !dash.is_finite()) { - return Err(format!("Hatch.pattern.lines[{index}] contains non-finite values")); + return Err(format!( + "Hatch.pattern.lines[{index}] contains non-finite values" + )); } if line.offset.x.hypot(line.offset.y) < 1e-12 { - return Err(format!("Hatch.pattern.lines[{index}].offset must be nonzero")); + return Err(format!( + "Hatch.pattern.lines[{index}].offset must be nonzero" + )); } } Ok(()) @@ -303,7 +367,11 @@ fn validate_leader( { return Err(format!( "Leader.{name} must be finite and {}", - if name == "text_height" { "greater than zero" } else { "non-negative" } + if name == "text_height" { + "greater than zero" + } else { + "non-negative" + } )); } } @@ -346,7 +414,10 @@ fn validate_mline( } for (vi, vertex) in new.vertices.iter().enumerate() { finite_vector(&format!("MLine.vertices[{vi}].position"), &vertex.position)?; - finite_vector(&format!("MLine.vertices[{vi}].direction"), &vertex.direction)?; + finite_vector( + &format!("MLine.vertices[{vi}].direction"), + &vertex.direction, + )?; finite_vector(&format!("MLine.vertices[{vi}].miter"), &vertex.miter)?; for (si, segment) in vertex.segments.iter().enumerate() { if segment @@ -369,7 +440,9 @@ fn validate_mline( return Err("MLine.vertices must not all coincide".into()); } } - if new.flags.contains(crate::host::codec::entities::MLineFlags::CLOSED) + if new + .flags + .contains(crate::host::codec::entities::MLineFlags::CLOSED) && new.vertices.len() < 3 { return Err("closed MLine requires at least 3 vertices".into()); @@ -398,49 +471,103 @@ fn validate_dimension( if base.style_name.trim().is_empty() { return Err("Dimension.style_name is empty".into()); } - let (points, scalars, distinct): (Vec<(&str, &crate::host::codec::types::Vector3)>, Vec<(&str, f64)>, Vec<(&str, &str)>) = match new { + let (points, scalars, distinct): ( + Vec<(&str, &crate::host::codec::types::Vector3)>, + Vec<(&str, f64)>, + Vec<(&str, &str)>, + ) = match new { D::Aligned(d) => ( - vec![("definition_point", &d.definition_point), ("first_point", &d.first_point), ("second_point", &d.second_point)], + vec![ + ("definition_point", &d.definition_point), + ("first_point", &d.first_point), + ("second_point", &d.second_point), + ], vec![("ext_line_rotation", d.ext_line_rotation)], vec![("first_point", "second_point")], ), D::Linear(d) => ( - vec![("definition_point", &d.definition_point), ("first_point", &d.first_point), ("second_point", &d.second_point)], - vec![("rotation", d.rotation), ("ext_line_rotation", d.ext_line_rotation)], + vec![ + ("definition_point", &d.definition_point), + ("first_point", &d.first_point), + ("second_point", &d.second_point), + ], + vec![ + ("rotation", d.rotation), + ("ext_line_rotation", d.ext_line_rotation), + ], vec![("first_point", "second_point")], ), D::Radius(d) => ( - vec![("definition_point", &d.definition_point), ("angle_vertex", &d.angle_vertex)], + vec![ + ("definition_point", &d.definition_point), + ("angle_vertex", &d.angle_vertex), + ], vec![("leader_length", d.leader_length)], vec![("definition_point", "angle_vertex")], ), D::Diameter(d) => ( - vec![("definition_point", &d.definition_point), ("angle_vertex", &d.angle_vertex)], + vec![ + ("definition_point", &d.definition_point), + ("angle_vertex", &d.angle_vertex), + ], vec![("leader_length", d.leader_length)], vec![("definition_point", "angle_vertex")], ), D::Angular2Ln(d) => ( - vec![("definition_point", &d.definition_point), ("dimension_arc", &d.dimension_arc), ("first_point", &d.first_point), ("second_point", &d.second_point), ("angle_vertex", &d.angle_vertex)], + vec![ + ("definition_point", &d.definition_point), + ("dimension_arc", &d.dimension_arc), + ("first_point", &d.first_point), + ("second_point", &d.second_point), + ("angle_vertex", &d.angle_vertex), + ], vec![], vec![], ), D::Angular3Pt(d) => ( - vec![("definition_point", &d.definition_point), ("first_point", &d.first_point), ("second_point", &d.second_point), ("angle_vertex", &d.angle_vertex)], + vec![ + ("definition_point", &d.definition_point), + ("first_point", &d.first_point), + ("second_point", &d.second_point), + ("angle_vertex", &d.angle_vertex), + ], vec![], - vec![("first_point", "angle_vertex"), ("second_point", "angle_vertex")], + vec![ + ("first_point", "angle_vertex"), + ("second_point", "angle_vertex"), + ], ), D::Ordinate(d) => ( - vec![("definition_point", &d.definition_point), ("feature_location", &d.feature_location), ("leader_endpoint", &d.leader_endpoint)], + vec![ + ("definition_point", &d.definition_point), + ("feature_location", &d.feature_location), + ("leader_endpoint", &d.leader_endpoint), + ], vec![], vec![], ), D::Arc(d) => ( - vec![("definition_point", &d.definition_point), ("first_extension_point", &d.first_extension_point), ("second_extension_point", &d.second_extension_point), ("center_point", &d.center_point), ("first_leader_point", &d.first_leader_point), ("second_leader_point", &d.second_leader_point)], - vec![("arc_start_parameter", d.arc_start_parameter), ("arc_end_parameter", d.arc_end_parameter)], + vec![ + ("definition_point", &d.definition_point), + ("first_extension_point", &d.first_extension_point), + ("second_extension_point", &d.second_extension_point), + ("center_point", &d.center_point), + ("first_leader_point", &d.first_leader_point), + ("second_leader_point", &d.second_leader_point), + ], + vec![ + ("arc_start_parameter", d.arc_start_parameter), + ("arc_end_parameter", d.arc_end_parameter), + ], vec![("first_extension_point", "second_extension_point")], ), D::LargeRadial(d) => ( - vec![("definition_point", &d.definition_point), ("chord_point", &d.chord_point), ("override_center", &d.override_center), ("jog_point", &d.jog_point)], + vec![ + ("definition_point", &d.definition_point), + ("chord_point", &d.chord_point), + ("override_center", &d.override_center), + ("jog_point", &d.jog_point), + ], vec![("jog_angle", d.jog_angle)], vec![("definition_point", "chord_point")], ), @@ -484,7 +611,11 @@ fn validate_multileader( if !value.is_finite() || value < 0.0 || (!allow_zero && value == 0.0) { return Err(format!( "MultiLeader.{name} must be finite and {}", - if allow_zero { "non-negative" } else { "greater than zero" } + if allow_zero { + "non-negative" + } else { + "greater than zero" + } )); } } @@ -515,21 +646,38 @@ fn validate_multileader( ] { finite_vector(&format!("MultiLeader.context.{name}"), point)?; } - if context.transform_matrix.iter().any(|value| !value.is_finite()) { + if context + .transform_matrix + .iter() + .any(|value| !value.is_finite()) + { return Err("MultiLeader.context.transform_matrix must be finite".into()); } for (ri, root) in context.leader_roots.iter().enumerate() { - finite_vector(&format!("MultiLeader.context.leader_roots[{ri}].connection_point"), &root.connection_point)?; - finite_vector(&format!("MultiLeader.context.leader_roots[{ri}].direction"), &root.direction)?; + finite_vector( + &format!("MultiLeader.context.leader_roots[{ri}].connection_point"), + &root.connection_point, + )?; + finite_vector( + &format!("MultiLeader.context.leader_roots[{ri}].direction"), + &root.direction, + )?; if !root.landing_distance.is_finite() { - return Err(format!("MultiLeader.context.leader_roots[{ri}].landing_distance must be finite")); + return Err(format!( + "MultiLeader.context.leader_roots[{ri}].landing_distance must be finite" + )); } for (li, line) in root.lines.iter().enumerate() { if line.points.is_empty() { - return Err(format!("MultiLeader.context.leader_roots[{ri}].lines[{li}] has no points")); + return Err(format!( + "MultiLeader.context.leader_roots[{ri}].lines[{li}] has no points" + )); } for (pi, point) in line.points.iter().enumerate() { - finite_vector(&format!("MultiLeader.context.leader_roots[{ri}].lines[{li}].points[{pi}]"), point)?; + finite_vector( + &format!("MultiLeader.context.leader_roots[{ri}].lines[{li}].points[{pi}]"), + point, + )?; } } } @@ -568,12 +716,16 @@ fn validate_table( } for (ci, column) in new.columns.iter().enumerate() { if !column.width.is_finite() || column.width <= 0.0 { - return Err(format!("Table.columns[{ci}].width must be finite and greater than zero")); + return Err(format!( + "Table.columns[{ci}].width must be finite and greater than zero" + )); } } for (ri, row) in new.rows.iter().enumerate() { if !row.height.is_finite() || row.height <= 0.0 { - return Err(format!("Table.rows[{ri}].height must be finite and greater than zero")); + return Err(format!( + "Table.rows[{ri}].height must be finite and greater than zero" + )); } if row.cells.len() != new.columns.len() { return Err(format!( @@ -583,11 +735,20 @@ fn validate_table( )); } for (ci, cell) in row.cells.iter().enumerate() { - if !cell.rotation.is_finite() || !cell.geometry_scale.is_finite() || !cell.block_scale.is_finite() { - return Err(format!("Table.rows[{ri}].cells[{ci}] has non-finite rotation or scale")); + if !cell.rotation.is_finite() + || !cell.geometry_scale.is_finite() + || !cell.block_scale.is_finite() + { + return Err(format!( + "Table.rows[{ri}].cells[{ci}] has non-finite rotation or scale" + )); } for (ki, content) in cell.contents.iter().enumerate() { - if !content.rotation.is_finite() || !content.scale.is_finite() || !content.text_height.is_finite() || content.text_height < 0.0 { + if !content.rotation.is_finite() + || !content.scale.is_finite() + || !content.text_height.is_finite() + || content.text_height < 0.0 + { return Err(format!( "Table.rows[{ri}].cells[{ci}].contents[{ki}] has invalid rotation, scale or text height" )); @@ -607,7 +768,9 @@ fn validate_table( || range.bottom_row >= rows || range.right_col >= columns { - return Err(format!("Table.merged_ranges[{mi}] lies outside the {rows}x{columns} grid")); + return Err(format!( + "Table.merged_ranges[{mi}] lies outside the {rows}x{columns} grid" + )); } for (mj, other) in new.merged_ranges.iter().enumerate().skip(mi + 1) { let separate = range.bottom_row < other.top_row @@ -615,7 +778,9 @@ fn validate_table( || range.right_col < other.left_col || other.right_col < range.left_col; if !separate { - return Err(format!("Table.merged_ranges[{mi}] overlaps merged_ranges[{mj}]")); + return Err(format!( + "Table.merged_ranges[{mi}] overlaps merged_ranges[{mj}]" + )); } } } @@ -651,7 +816,10 @@ fn validate_polygon_mesh( } solid_normal("PolygonMesh.normal", &new.normal)?; for (index, vertex) in new.vertices.iter().enumerate() { - finite_vector(&format!("PolygonMesh.vertices[{index}].location"), &vertex.location)?; + finite_vector( + &format!("PolygonMesh.vertices[{index}].location"), + &vertex.location, + )?; } Ok(()) } @@ -666,7 +834,11 @@ fn validate_polyface_mesh( if old == Some(new) { return Ok(()); } - if !new.elevation.is_finite() || !new.thickness.is_finite() || !new.start_width.is_finite() || !new.end_width.is_finite() { + if !new.elevation.is_finite() + || !new.thickness.is_finite() + || !new.start_width.is_finite() + || !new.end_width.is_finite() + { return Err("PolyfaceMesh elevation, thickness and widths must be finite".into()); } solid_normal("PolyfaceMesh.normal", &new.normal)?; @@ -674,9 +846,18 @@ fn validate_polyface_mesh( return Err("PolyfaceMesh.vertices requires at least 3 vertices".into()); } for (index, vertex) in new.vertices.iter().enumerate() { - finite_vector(&format!("PolyfaceMesh.vertices[{index}].location"), &vertex.location)?; - if !vertex.bulge.is_finite() || !vertex.start_width.is_finite() || !vertex.end_width.is_finite() || !vertex.curve_tangent.is_finite() { - return Err(format!("PolyfaceMesh.vertices[{index}] has non-finite bulge, width or tangent")); + finite_vector( + &format!("PolyfaceMesh.vertices[{index}].location"), + &vertex.location, + )?; + if !vertex.bulge.is_finite() + || !vertex.start_width.is_finite() + || !vertex.end_width.is_finite() + || !vertex.curve_tangent.is_finite() + { + return Err(format!( + "PolyfaceMesh.vertices[{index}] has non-finite bulge, width or tangent" + )); } } if new.faces.is_empty() { @@ -694,7 +875,9 @@ fn validate_polyface_mesh( } } if indices.iter().filter(|value| **value != 0).count() < 3 { - return Err(format!("PolyfaceMesh.faces[{index}] needs at least 3 vertex indices")); + return Err(format!( + "PolyfaceMesh.faces[{index}] needs at least 3 vertex indices" + )); } } Ok(()) @@ -722,7 +905,11 @@ fn validate_mesh( if face.vertices.len() < 3 { return Err(format!("Mesh.faces[{index}] needs at least 3 vertices")); } - if let Some(bad) = face.vertices.iter().find(|vertex| **vertex >= new.vertices.len()) { + if let Some(bad) = face + .vertices + .iter() + .find(|vertex| **vertex >= new.vertices.len()) + { return Err(format!( "Mesh.faces[{index}] references vertex {bad} but only {} exist", new.vertices.len() @@ -731,13 +918,20 @@ fn validate_mesh( } for (index, edge) in new.edges.iter().enumerate() { if edge.start >= new.vertices.len() || edge.end >= new.vertices.len() { - return Err(format!("Mesh.edges[{index}] references a vertex out of range")); + return Err(format!( + "Mesh.edges[{index}] references a vertex out of range" + )); } if edge.start == edge.end { return Err(format!("Mesh.edges[{index}] has identical endpoints")); } - if edge.crease.is_some_and(|crease| !crease.is_finite() || crease < 0.0) { - return Err(format!("Mesh.edges[{index}].crease must be finite and non-negative")); + if edge + .crease + .is_some_and(|crease| !crease.is_finite() || crease < 0.0) + { + return Err(format!( + "Mesh.edges[{index}].crease must be finite and non-negative" + )); } } Ok(()) @@ -765,7 +959,11 @@ fn validate_helix( finite_vector("Helix.axis_base_point", &new.axis_base_point)?; finite_vector("Helix.start_point", &new.start_point)?; solid_normal("Helix.axis_vector", &new.axis_vector)?; - for (name, value) in [("radius", new.radius), ("turns", new.turns), ("turn_height", new.turn_height)] { + for (name, value) in [ + ("radius", new.radius), + ("turns", new.turns), + ("turn_height", new.turn_height), + ] { if !value.is_finite() || value <= 0.0 { return Err(format!("Helix.{name} must be finite and greater than zero")); } @@ -774,7 +972,9 @@ fn validate_helix( let length = axis.x.hypot(axis.y).hypot(axis.z); let delta = new.start_point - new.axis_base_point; let along = (delta.x * axis.x + delta.y * axis.y + delta.z * axis.z) / length; - let radial = ((delta.x * delta.x + delta.y * delta.y + delta.z * delta.z) - along * along).max(0.0).sqrt(); + let radial = ((delta.x * delta.x + delta.y * delta.y + delta.z * delta.z) - along * along) + .max(0.0) + .sqrt(); if radial <= 1.0e-9 { return Err("Helix.start_point must not lie on the axis".into()); } @@ -793,14 +993,19 @@ fn validate_raster_image( } if let Some(old) = old { if old.file_path != new.file_path { - return Err("RasterImage.file_path is fixed at creation; the image definition owns it".into()); + return Err( + "RasterImage.file_path is fixed at creation; the image definition owns it".into(), + ); } } else if new.file_path.trim().is_empty() { return Err("RasterImage.file_path is empty".into()); } else if new.definition_handle.is_none_or(|handle| handle.is_null()) && !std::path::Path::new(&new.file_path).is_file() { - return Err(format!("RasterImage.file_path {:?} is not a readable file", new.file_path)); + return Err(format!( + "RasterImage.file_path {:?} is not a readable file", + new.file_path + )); } finite_vector("RasterImage.insertion_point", &new.insertion_point)?; finite_vector("RasterImage.u_vector", &new.u_vector)?; @@ -820,7 +1025,9 @@ fn validate_raster_image( } for (index, vertex) in new.clip_boundary.vertices.iter().enumerate() { if !vertex.x.is_finite() || !vertex.y.is_finite() { - return Err(format!("RasterImage.clip_boundary.vertices[{index}] must be finite")); + return Err(format!( + "RasterImage.clip_boundary.vertices[{index}] must be finite" + )); } } if new.clipping_enabled && new.clip_boundary.vertices.len() < 2 { @@ -847,7 +1054,8 @@ fn validate_wipeout( if cross.x.hypot(cross.y).hypot(cross.z) <= 1.0e-12 { return Err("Wipeout.u_vector and v_vector must be nonzero and not parallel".into()); } - if !new.size.x.is_finite() || !new.size.y.is_finite() || new.size.x <= 0.0 || new.size.y <= 0.0 { + if !new.size.x.is_finite() || !new.size.y.is_finite() || new.size.x <= 0.0 || new.size.y <= 0.0 + { return Err("Wipeout.size must be finite and greater than zero".into()); } if new.brightness > 100 || new.contrast > 100 || new.fade > 100 { @@ -858,7 +1066,9 @@ fn validate_wipeout( } for (index, vertex) in new.clip_boundary_vertices.iter().enumerate() { if !vertex.x.is_finite() || !vertex.y.is_finite() { - return Err(format!("Wipeout.clip_boundary_vertices[{index}] must be finite")); + return Err(format!( + "Wipeout.clip_boundary_vertices[{index}] must be finite" + )); } } let count = new.clip_boundary_vertices.len(); @@ -894,7 +1104,11 @@ fn validate_underlay( } finite_vector("Underlay.insertion_point", &new.insertion_point)?; solid_normal("Underlay.normal", &new.normal)?; - for (name, value) in [("x_scale", new.x_scale), ("y_scale", new.y_scale), ("z_scale", new.z_scale)] { + for (name, value) in [ + ("x_scale", new.x_scale), + ("y_scale", new.y_scale), + ("z_scale", new.z_scale), + ] { if !value.is_finite() || value == 0.0 { return Err(format!("Underlay.{name} must be finite and nonzero")); } @@ -910,7 +1124,9 @@ fn validate_underlay( } for (index, vertex) in new.clip_boundary_vertices.iter().enumerate() { if !vertex.x.is_finite() || !vertex.y.is_finite() { - return Err(format!("Underlay.clip_boundary_vertices[{index}] must be finite")); + return Err(format!( + "Underlay.clip_boundary_vertices[{index}] must be finite" + )); } } if !new.clip_boundary_vertices.is_empty() && new.clip_boundary_vertices.len() < 2 { @@ -933,12 +1149,24 @@ fn validate_viewport( finite_vector("Viewport.view_center", &new.view_center)?; finite_vector("Viewport.view_target", &new.view_target)?; solid_normal("Viewport.view_direction", &new.view_direction)?; - for (name, value) in [("width", new.width), ("height", new.height), ("view_height", new.view_height), ("lens_length", new.lens_length), ("custom_scale", new.custom_scale)] { + for (name, value) in [ + ("width", new.width), + ("height", new.height), + ("view_height", new.view_height), + ("lens_length", new.lens_length), + ("custom_scale", new.custom_scale), + ] { if !value.is_finite() || value <= 0.0 { - return Err(format!("Viewport.{name} must be finite and greater than zero")); + return Err(format!( + "Viewport.{name} must be finite and greater than zero" + )); } } - for (name, value) in [("front_clip_z", new.front_clip_z), ("back_clip_z", new.back_clip_z), ("twist_angle", new.twist_angle)] { + for (name, value) in [ + ("front_clip_z", new.front_clip_z), + ("back_clip_z", new.back_clip_z), + ("twist_angle", new.twist_angle), + ] { if !value.is_finite() { return Err(format!("Viewport.{name} must be finite")); } @@ -1077,7 +1305,10 @@ fn basic_entity_errors(new: &crate::host::EntityType) -> Vec { EntityType::Ellipse(v) => { chk!(finite_vector("Ellipse.center", &v.center)); chk!(solid_normal("Ellipse.major_axis", &v.major_axis)); - if !v.minor_axis_ratio.is_finite() || v.minor_axis_ratio <= 0.0 || v.minor_axis_ratio > 1.0 { + if !v.minor_axis_ratio.is_finite() + || v.minor_axis_ratio <= 0.0 + || v.minor_axis_ratio > 1.0 + { errs.push("Ellipse.minor_axis_ratio must be within (0, 1]".into()); } chk!(finite("Ellipse.start_parameter", v.start_parameter)); @@ -1119,8 +1350,13 @@ fn basic_entity_errors(new: &crate::host::EntityType) -> Vec { if !vertex.location.x.is_finite() || !vertex.location.y.is_finite() { errs.push(format!("LwPolyline.vertices[{i}].location must be finite")); } - if !vertex.bulge.is_finite() || !vertex.start_width.is_finite() || !vertex.end_width.is_finite() { - errs.push(format!("LwPolyline.vertices[{i}] has a non-finite bulge or width")); + if !vertex.bulge.is_finite() + || !vertex.start_width.is_finite() + || !vertex.end_width.is_finite() + { + errs.push(format!( + "LwPolyline.vertices[{i}] has a non-finite bulge or width" + )); } } if !v.constant_width.is_finite() || v.constant_width < 0.0 { @@ -1135,12 +1371,25 @@ fn basic_entity_errors(new: &crate::host::EntityType) -> Vec { errs.push("Polyline2D.vertices requires at least 2 vertices".into()); } for (i, vertex) in v.vertices.iter().enumerate() { - chk!(finite_vector(&format!("Polyline2D.vertices[{i}].location"), &vertex.location)); - if !vertex.bulge.is_finite() || !vertex.start_width.is_finite() || !vertex.end_width.is_finite() { - errs.push(format!("Polyline2D.vertices[{i}] has a non-finite bulge or width")); + chk!(finite_vector( + &format!("Polyline2D.vertices[{i}].location"), + &vertex.location + )); + if !vertex.bulge.is_finite() + || !vertex.start_width.is_finite() + || !vertex.end_width.is_finite() + { + errs.push(format!( + "Polyline2D.vertices[{i}] has a non-finite bulge or width" + )); } } - for (name, value) in [("start_width", v.start_width), ("end_width", v.end_width), ("thickness", v.thickness), ("elevation", v.elevation)] { + for (name, value) in [ + ("start_width", v.start_width), + ("end_width", v.end_width), + ("thickness", v.thickness), + ("elevation", v.elevation), + ] { chk!(finite(&format!("Polyline2D.{name}"), value)); } chk!(solid_normal("Polyline2D.normal", &v.normal)); @@ -1150,7 +1399,10 @@ fn basic_entity_errors(new: &crate::host::EntityType) -> Vec { errs.push("Polyline.vertices requires at least 2 vertices".into()); } for (i, vertex) in v.vertices.iter().enumerate() { - chk!(finite_vector(&format!("Polyline.vertices[{i}].location"), &vertex.location)); + chk!(finite_vector( + &format!("Polyline.vertices[{i}].location"), + &vertex.location + )); } } EntityType::Polyline3D(v) => { @@ -1158,9 +1410,16 @@ fn basic_entity_errors(new: &crate::host::EntityType) -> Vec { errs.push("Polyline3D.vertices requires at least 2 vertices".into()); } for (i, vertex) in v.vertices.iter().enumerate() { - chk!(finite_vector(&format!("Polyline3D.vertices[{i}].position"), &vertex.position)); + chk!(finite_vector( + &format!("Polyline3D.vertices[{i}].position"), + &vertex.position + )); } - for (name, value) in [("default_start_width", v.default_start_width), ("default_end_width", v.default_end_width), ("elevation", v.elevation)] { + for (name, value) in [ + ("default_start_width", v.default_start_width), + ("default_end_width", v.default_end_width), + ("elevation", v.elevation), + ] { chk!(finite(&format!("Polyline3D.{name}"), value)); } chk!(solid_normal("Polyline3D.normal", &v.normal)); @@ -1255,7 +1514,10 @@ fn validate_surface( if old.is_some_and(|old| old.u_isolines == new.u_isolines && old.v_isolines == new.v_isolines) { return Ok(()); } - for (name, value) in [("u_isolines", new.u_isolines), ("v_isolines", new.v_isolines)] { + for (name, value) in [ + ("u_isolines", new.u_isolines), + ("v_isolines", new.v_isolines), + ] { if !(0..=200).contains(&value) { return Err(format!("Surface.{name} must be between 0 and 200")); } @@ -1271,7 +1533,9 @@ fn validate_section_symbol( old: Option<&crate::host::codec::entities::SectionSymbol>, new: &crate::host::codec::entities::SectionSymbol, ) -> Result<(), String> { - if old.is_some_and(|old| old.points == new.points && old.symbol_scale.to_bits() == new.symbol_scale.to_bits()) { + if old.is_some_and(|old| { + old.points == new.points && old.symbol_scale.to_bits() == new.symbol_scale.to_bits() + }) { return Ok(()); } if !new.symbol_scale.is_finite() || new.symbol_scale <= 0.0 { @@ -1281,10 +1545,18 @@ fn validate_section_symbol( return Err("SectionSymbol.points requires at least 2 points".into()); } for (index, point) in new.points.iter().enumerate() { - finite_vector(&format!("SectionSymbol.points[{index}].point"), &point.point)?; - finite_vector(&format!("SectionSymbol.points[{index}].label_offset"), &point.label_offset)?; + finite_vector( + &format!("SectionSymbol.points[{index}].point"), + &point.point, + )?; + finite_vector( + &format!("SectionSymbol.points[{index}].label_offset"), + &point.label_offset, + )?; if !point.bulge.is_finite() { - return Err(format!("SectionSymbol.points[{index}].bulge must be finite")); + return Err(format!( + "SectionSymbol.points[{index}].bulge must be finite" + )); } } Ok(()) @@ -1403,10 +1675,18 @@ pub fn validate_new_canvas_entity(entity: &crate::host::EntityType) -> Result<() } finite_vector("Insert.insert_point", &value.insert_point)?; solid_normal("Insert.normal", &value.normal)?; - for (name, scale) in [("x_scale", value.x_scale()), ("y_scale", value.y_scale()), ("z_scale", value.z_scale())] { + for (name, scale) in [ + ("x_scale", value.x_scale()), + ("y_scale", value.y_scale()), + ("z_scale", value.z_scale()), + ] { insert_scale(&format!("Insert.{name}"), scale)?; } - for (name, number) in [("rotation", value.rotation), ("column_spacing", value.column_spacing), ("row_spacing", value.row_spacing)] { + for (name, number) in [ + ("rotation", value.rotation), + ("column_spacing", value.column_spacing), + ("row_spacing", value.row_spacing), + ] { if !number.is_finite() { return Err(format!("Insert.{name} must be finite")); } @@ -1512,7 +1792,9 @@ pub fn validate_new_canvas_entity(entity: &crate::host::EntityType) -> Result<() } for (si, sp) in value.seed_points.iter().enumerate() { if !sp.x.is_finite() || !sp.y.is_finite() { - return Err(format!("Hatch.seed_points[{si}] has non-finite coordinates")); + return Err(format!( + "Hatch.seed_points[{si}] has non-finite coordinates" + )); } } validate_hatch_paths(&value.paths)?; @@ -1553,8 +1835,7 @@ pub fn validate_entity_mutation( Err(format!("{name} must contain finite coordinates")) } }; - let same3 = |a: &crate::host::codec::types::Vector3, - b: &crate::host::codec::types::Vector3| { + let same3 = |a: &crate::host::codec::types::Vector3, b: &crate::host::codec::types::Vector3| { a.x.to_bits() == b.x.to_bits() && a.y.to_bits() == b.y.to_bits() && a.z.to_bits() == b.z.to_bits() @@ -1773,18 +2054,34 @@ pub fn validate_entity_mutation( return Err("Shape.style_handle is read-only".into()); } } - (EntityType::Dimension(old), EntityType::Dimension(new)) => validate_dimension(Some(old), new)?, - (EntityType::MultiLeader(old), EntityType::MultiLeader(new)) => validate_multileader(Some(old), new)?, + (EntityType::Dimension(old), EntityType::Dimension(new)) => { + validate_dimension(Some(old), new)? + } + (EntityType::MultiLeader(old), EntityType::MultiLeader(new)) => { + validate_multileader(Some(old), new)? + } (EntityType::Table(old), EntityType::Table(new)) => validate_table(Some(old), new)?, - (EntityType::PolygonMesh(old), EntityType::PolygonMesh(new)) => validate_polygon_mesh(Some(old), new)?, - (EntityType::PolyfaceMesh(old), EntityType::PolyfaceMesh(new)) => validate_polyface_mesh(Some(old), new)?, + (EntityType::PolygonMesh(old), EntityType::PolygonMesh(new)) => { + validate_polygon_mesh(Some(old), new)? + } + (EntityType::PolyfaceMesh(old), EntityType::PolyfaceMesh(new)) => { + validate_polyface_mesh(Some(old), new)? + } (EntityType::Mesh(old), EntityType::Mesh(new)) => validate_mesh(Some(old), new)?, (EntityType::Helix(old), EntityType::Helix(new)) => validate_helix(Some(old), new)?, - (EntityType::RasterImage(old), EntityType::RasterImage(new)) => validate_raster_image(Some(old), new)?, + (EntityType::RasterImage(old), EntityType::RasterImage(new)) => { + validate_raster_image(Some(old), new)? + } (EntityType::Wipeout(old), EntityType::Wipeout(new)) => validate_wipeout(Some(old), new)?, - (EntityType::Underlay(old), EntityType::Underlay(new)) => validate_underlay(Some(old), new)?, - (EntityType::Viewport(old), EntityType::Viewport(new)) => validate_viewport(Some(old), new)?, - (EntityType::ViewBorder(old), EntityType::ViewBorder(new)) => validate_view_border(Some(old), new)?, + (EntityType::Underlay(old), EntityType::Underlay(new)) => { + validate_underlay(Some(old), new)? + } + (EntityType::Viewport(old), EntityType::Viewport(new)) => { + validate_viewport(Some(old), new)? + } + (EntityType::ViewBorder(old), EntityType::ViewBorder(new)) => { + validate_view_border(Some(old), new)? + } (EntityType::Light(old), EntityType::Light(new)) => validate_light(Some(old), new)?, (EntityType::Ole2Frame(old), EntityType::Ole2Frame(new)) => { validate_ole_frame(Some(old), new)?; @@ -1793,7 +2090,9 @@ pub fn validate_entity_mutation( } } (EntityType::Surface(old), EntityType::Surface(new)) => validate_surface(Some(old), new)?, - (EntityType::SectionSymbol(old), EntityType::SectionSymbol(new)) => validate_section_symbol(Some(old), new)?, + (EntityType::SectionSymbol(old), EntityType::SectionSymbol(new)) => { + validate_section_symbol(Some(old), new)? + } (EntityType::MLine(old), EntityType::MLine(new)) => validate_mline(Some(old), new)?, (EntityType::Leader(old), EntityType::Leader(new)) => validate_leader(Some(old), new)?, (EntityType::AttributeDefinition(old), EntityType::AttributeDefinition(new)) => { @@ -1948,7 +2247,9 @@ pub fn validate_entity_mutation( if old.seed_points != new.seed_points { for (si, sp) in new.seed_points.iter().enumerate() { if !sp.x.is_finite() || !sp.y.is_finite() { - return Err(format!("Hatch.seed_points[{si}] has non-finite coordinates")); + return Err(format!( + "Hatch.seed_points[{si}] has non-finite coordinates" + )); } } } @@ -2022,7 +2323,7 @@ pub fn validate_canvas_entity_references( .iter() .any(|style| style.handle == handle) }, - ); + ); if !found { return Err(format!( "Tolerance dimension style {:?} does not exist", @@ -2069,9 +2370,10 @@ pub fn validate_canvas_entity_references( _ => None, } { let name = value.block_name.trim(); - let record = document.block_records.get(name).ok_or_else(|| { - format!("Insert block {:?} does not exist", value.block_name) - })?; + let record = document + .block_records + .get(name) + .ok_or_else(|| format!("Insert block {:?} does not exist", value.block_name))?; if record.is_model_space() || record.is_paper_space() { return Err("Insert cannot reference a model-space or paper-space block".into()); } @@ -2122,19 +2424,30 @@ pub fn validate_canvas_entity_references( _ => false, }; if !matches { - return Err(format!("SectionSymbol {name} {handle:?} is not an existing {wanted} object")); + return Err(format!( + "SectionSymbol {name} {handle:?} is not an existing {wanted} object" + )); } } } if let EntityType::ViewBorder(value) = entity { - if !matches!(document.get_entity(value.active_viewport), Some(EntityType::Viewport(_))) { - return Err(format!("ViewBorder viewport {:?} does not exist", value.active_viewport)); + if !matches!( + document.get_entity(value.active_viewport), + Some(EntityType::Viewport(_)) + ) { + return Err(format!( + "ViewBorder viewport {:?} does not exist", + value.active_viewport + )); } if !matches!( document.objects.get(&value.scale_handle), Some(crate::host::codec::objects::ObjectType::Scale(_)) ) { - return Err(format!("ViewBorder scale {:?} does not exist", value.scale_handle)); + return Err(format!( + "ViewBorder scale {:?} does not exist", + value.scale_handle + )); } } if let EntityType::Viewport(value) = entity { @@ -2177,7 +2490,9 @@ pub fn validate_canvas_entity_references( document.objects.get(&handle), Some(crate::host::codec::objects::ObjectType::ImageDefinition(_)) ) { - return Err(format!("RasterImage image definition {handle:?} does not exist")); + return Err(format!( + "RasterImage image definition {handle:?} does not exist" + )); } } } @@ -2193,13 +2508,23 @@ pub fn validate_canvas_entity_references( for (ri, row) in value.rows.iter().enumerate() { for (ci, cell) in row.cells.iter().enumerate() { for content in &cell.contents { - if let Some(handle) = content.text_style_handle.filter(|handle| !handle.is_null()) { - if !document.text_styles.iter().any(|style| style.handle == handle) { + if let Some(handle) = + content.text_style_handle.filter(|handle| !handle.is_null()) + { + if !document + .text_styles + .iter() + .any(|style| style.handle == handle) + { return Err(format!("Table.rows[{ri}].cells[{ci}] text style handle {handle:?} does not exist")); } } if let Some(handle) = content.block_handle.filter(|handle| !handle.is_null()) { - if !document.block_records.iter().any(|record| record.handle == handle) { + if !document + .block_records + .iter() + .any(|record| record.handle == handle) + { return Err(format!("Table.rows[{ri}].cells[{ci}] block handle {handle:?} does not exist")); } } @@ -2209,34 +2534,63 @@ pub fn validate_canvas_entity_references( } if let EntityType::MultiLeader(value) = entity { use crate::host::codec::objects::ObjectType; - let handle_exists = |handle: Option, what: &str, ok: &dyn Fn(crate::host::codec::Handle) -> bool| { - match handle.filter(|handle| !handle.is_null()) { - Some(handle) if !ok(handle) => Err(format!("MultiLeader {what} handle {handle:?} does not exist")), - _ => Ok(()), - } + let handle_exists = + |handle: Option, + what: &str, + ok: &dyn Fn(crate::host::codec::Handle) -> bool| { + match handle.filter(|handle| !handle.is_null()) { + Some(handle) if !ok(handle) => Err(format!( + "MultiLeader {what} handle {handle:?} does not exist" + )), + _ => Ok(()), + } + }; + let is_block = |h| { + document + .block_records + .iter() + .any(|record| record.handle == h) }; - let is_block = |h| document.block_records.iter().any(|record| record.handle == h); let is_text_style = |h| document.text_styles.iter().any(|style| style.handle == h); let is_linetype = |h| document.line_types.iter().any(|ltype| ltype.handle == h); - let is_mleader_style = |h| matches!(document.objects.get(&h), Some(ObjectType::MultiLeaderStyle(_))); + let is_mleader_style = |h| { + matches!( + document.objects.get(&h), + Some(ObjectType::MultiLeaderStyle(_)) + ) + }; handle_exists(value.style_handle, "style", &is_mleader_style)?; handle_exists(value.text_style_handle, "text style", &is_text_style)?; - handle_exists(value.context.text_style_handle, "context text style", &is_text_style)?; + handle_exists( + value.context.text_style_handle, + "context text style", + &is_text_style, + )?; handle_exists(value.line_type_handle, "line type", &is_linetype)?; handle_exists(value.arrowhead_handle, "arrowhead block", &is_block)?; handle_exists(value.block_content_handle, "block content", &is_block)?; - handle_exists(value.context.block_content_handle, "context block content", &is_block)?; + handle_exists( + value.context.block_content_handle, + "context block content", + &is_block, + )?; } if let EntityType::Dimension(value) = entity { let name = value.base().style_name.trim(); - if !document.dim_styles.iter().any(|style| style.name.eq_ignore_ascii_case(name)) { - return Err(format!("Dimension style {:?} does not exist", value.base().style_name)); + if !document + .dim_styles + .iter() + .any(|style| style.name.eq_ignore_ascii_case(name)) + { + return Err(format!( + "Dimension style {:?} does not exist", + value.base().style_name + )); } } if let EntityType::MLine(value) = entity { - let style = resolve_mline_style(document, value).ok_or_else(|| { - format!("MLine style {:?} does not exist", value.style_name) - })?; + let style = resolve_mline_style(document, value) + .ok_or_else(|| format!("MLine style {:?} does not exist", value.style_name))?; if style.elements.is_empty() { return Err(format!("MLine style {:?} has no elements", style.name)); } @@ -2253,11 +2607,11 @@ pub fn validate_canvas_entity_references( } if let EntityType::Leader(value) = entity { use crate::host::codec::entities::LeaderCreationType; - if !document - .dim_styles - .iter() - .any(|style| style.name.eq_ignore_ascii_case(value.dimension_style.trim())) - { + if !document.dim_styles.iter().any(|style| { + style + .name + .eq_ignore_ascii_case(value.dimension_style.trim()) + }) { return Err(format!( "Leader dimension style {:?} does not exist", value.dimension_style @@ -2277,10 +2631,9 @@ pub fn validate_canvas_entity_references( ) })?; let expected = match value.creation_type { - LeaderCreationType::WithText => matches!( - annotation, - EntityType::Text(_) | EntityType::MText(_) - ), + LeaderCreationType::WithText => { + matches!(annotation, EntityType::Text(_) | EntityType::MText(_)) + } LeaderCreationType::WithTolerance => matches!(annotation, EntityType::Tolerance(_)), LeaderCreationType::WithBlock => matches!(annotation, EntityType::Insert(_)), LeaderCreationType::NoAnnotation => unreachable!(), @@ -2565,8 +2918,17 @@ mod tests { serde_json::from_str(get_embedded_entity_coverage_json()).unwrap(); for entry in &catalog.entity_kinds { let mut seen = std::collections::HashSet::new(); - for property in entry.properties.iter().filter(|p| p.model_access != ModelAccess::Unmapped) { - assert!(seen.insert(property.name.clone()), "{}: duplicate exposed name {}", entry.kind, property.name); + for property in entry + .properties + .iter() + .filter(|p| p.model_access != ModelAccess::Unmapped) + { + assert!( + seen.insert(property.name.clone()), + "{}: duplicate exposed name {}", + entry.kind, + property.name + ); } } } @@ -2648,7 +3010,7 @@ mod tests { entry .properties .iter() - .filter(|property| property.validation == "transaction_geometry") + .filter(|property| property.validation == "transaction_geometry") .map(|property| format!("{}.{}", entry.kind, property.name)) }) .collect(); @@ -2676,7 +3038,7 @@ mod tests { "Face3D.second_corner".to_owned(), "Face3D.third_corner".to_owned(), "Face3D.fourth_corner".to_owned(), - "Face3D.invisible_edges".to_owned(), + "Face3D.invisible_edges".to_owned(), "Insert.insert_point".to_owned(), "Insert.x_scale".to_owned(), "Insert.y_scale".to_owned(), @@ -2693,7 +3055,7 @@ mod tests { "Tolerance.text".to_owned(), "Tolerance.dimension_style_name".to_owned(), "Tolerance.text_height".to_owned(), - "Tolerance.dimension_gap".to_owned(), + "Tolerance.dimension_gap".to_owned(), "Shape.insertion_point".to_owned(), "Shape.size".to_owned(), "Shape.shape_name".to_owned(), @@ -2706,24 +3068,24 @@ mod tests { "Shape.style_name".to_owned(), "AttributeDefinition.tag".to_owned(), "AttributeDefinition.prompt".to_owned(), - "AttributeDefinition.default_value".to_owned(), - "AttributeDefinition.insertion_point".to_owned(), - "AttributeDefinition.alignment_point".to_owned(), + "AttributeDefinition.default_value".to_owned(), + "AttributeDefinition.insertion_point".to_owned(), + "AttributeDefinition.alignment_point".to_owned(), "AttributeDefinition.height".to_owned(), "AttributeDefinition.rotation".to_owned(), - "AttributeDefinition.width_factor".to_owned(), - "AttributeDefinition.oblique_angle".to_owned(), - "AttributeDefinition.text_style".to_owned(), - "AttributeDefinition.text_generation_flags".to_owned(), - "AttributeDefinition.horizontal_alignment".to_owned(), - "AttributeDefinition.vertical_alignment".to_owned(), + "AttributeDefinition.width_factor".to_owned(), + "AttributeDefinition.oblique_angle".to_owned(), + "AttributeDefinition.text_style".to_owned(), + "AttributeDefinition.text_generation_flags".to_owned(), + "AttributeDefinition.horizontal_alignment".to_owned(), + "AttributeDefinition.vertical_alignment".to_owned(), "AttributeDefinition.flags".to_owned(), "AttributeDefinition.field_length".to_owned(), "AttributeDefinition.normal".to_owned(), "AttributeDefinition.mtext_flag".to_owned(), "AttributeDefinition.is_multiline".to_owned(), "AttributeDefinition.line_count".to_owned(), - "AttributeDefinition.lock_position".to_owned(), + "AttributeDefinition.lock_position".to_owned(), "AttributeEntity.tag".to_owned(), "AttributeEntity.value".to_owned(), "AttributeEntity.insertion_point".to_owned(), @@ -2982,10 +3344,8 @@ mod tests { fn tolerance_geometry_and_style_references_are_validated() { use crate::host::codec::{self, types::Vector3}; let document = codec::CadDocument::new(); - let mut tolerance = codec::entities::Tolerance::with_text( - Vector3::new(1.0, 2.0, 0.0), - "{\\Fgdt;p}%%v0.1", - ); + let mut tolerance = + codec::entities::Tolerance::with_text(Vector3::new(1.0, 2.0, 0.0), "{\\Fgdt;p}%%v0.1"); let entity = codec::EntityType::Tolerance(tolerance.clone()); validate_new_canvas_entity(&entity).unwrap(); validate_canvas_entity_references(&document, &entity).unwrap(); @@ -3033,22 +3393,25 @@ mod tests { .contains("does not match")); let mut missing = leader.clone(); missing.annotation_handle = codec::Handle::new(0xdead); - assert!(validate_canvas_entity_references( - &document, - &codec::EntityType::Leader(missing) - ) - .unwrap_err() - .contains("does not exist")); + assert!( + validate_canvas_entity_references(&document, &codec::EntityType::Leader(missing)) + .unwrap_err() + .contains("does not exist") + ); let mut none_with_handle = leader.clone(); none_with_handle.creation_type = LeaderCreationType::NoAnnotation; - assert!(validate_new_canvas_entity(&codec::EntityType::Leader(none_with_handle)) - .unwrap_err() - .contains("no annotation")); + assert!( + validate_new_canvas_entity(&codec::EntityType::Leader(none_with_handle)) + .unwrap_err() + .contains("no annotation") + ); let mut one_point = leader.clone(); one_point.vertices.truncate(1); - assert!(validate_new_canvas_entity(&codec::EntityType::Leader(one_point)) - .unwrap_err() - .contains("at least 2")); + assert!( + validate_new_canvas_entity(&codec::EntityType::Leader(one_point)) + .unwrap_err() + .contains("at least 2") + ); let mut bad_style = leader.clone(); bad_style.dimension_style = "Missing".into(); assert!(validate_canvas_entity_references( @@ -3075,7 +3438,9 @@ mod tests { let document = codec::CadDocument::new(); let mut mline = codec::entities::MLine::new(); for x in [0.0, 5.0] { - mline.vertices.push(MLineVertex::new(Vector3::new(x, 0.0, 0.0))); + mline + .vertices + .push(MLineVertex::new(Vector3::new(x, 0.0, 0.0))); } let mut entity = codec::EntityType::MLine(mline.clone()); validate_new_canvas_entity(&entity).unwrap(); @@ -3084,21 +3449,36 @@ mod tests { let mut missing = mline.clone(); missing.style_name = "Missing".into(); - assert!(bind_canvas_entity_references(&document, &mut codec::EntityType::MLine(missing)) - .unwrap_err().contains("does not exist")); + assert!( + bind_canvas_entity_references(&document, &mut codec::EntityType::MLine(missing)) + .unwrap_err() + .contains("does not exist") + ); let mut wrong_segments = mline.clone(); wrong_segments.vertices[0].init_segments(3); - assert!(bind_canvas_entity_references(&document, &mut codec::EntityType::MLine(wrong_segments)) - .unwrap_err().contains("segments but style")); + assert!(bind_canvas_entity_references( + &document, + &mut codec::EntityType::MLine(wrong_segments) + ) + .unwrap_err() + .contains("segments but style")); let mut one = mline.clone(); one.vertices.truncate(1); - assert!(validate_new_canvas_entity(&codec::EntityType::MLine(one)).unwrap_err().contains("at least 2")); + assert!(validate_new_canvas_entity(&codec::EntityType::MLine(one)) + .unwrap_err() + .contains("at least 2")); let mut closed = mline.clone(); closed.flags |= codec::entities::MLineFlags::CLOSED; - assert!(validate_new_canvas_entity(&codec::EntityType::MLine(closed)).unwrap_err().contains("at least 3")); + assert!( + validate_new_canvas_entity(&codec::EntityType::MLine(closed)) + .unwrap_err() + .contains("at least 3") + ); let mut scale = mline.clone(); scale.scale_factor = 0.0; - assert!(validate_new_canvas_entity(&codec::EntityType::MLine(scale)).unwrap_err().contains("nonzero")); + assert!(validate_new_canvas_entity(&codec::EntityType::MLine(scale)) + .unwrap_err() + .contains("nonzero")); } #[cfg(feature = "host")] @@ -3129,11 +3509,9 @@ mod tests { unreachable!() }; bad.size = 0.0; - assert!( - validate_new_canvas_entity(&codec::EntityType::Shape(bad)) - .unwrap_err() - .contains("greater than zero") - ); + assert!(validate_new_canvas_entity(&codec::EntityType::Shape(bad)) + .unwrap_err() + .contains("greater than zero")); } #[test] @@ -3257,11 +3635,9 @@ mod tests { let mut xline = codec::entities::XLine::default(); xline.base_point.x = f64::NAN; - assert!( - validate_new_canvas_entity(&codec::EntityType::XLine(xline)) - .unwrap_err() - .contains("XLine.base_point") - ); + assert!(validate_new_canvas_entity(&codec::EntityType::XLine(xline)) + .unwrap_err() + .contains("XLine.base_point")); let solid = codec::entities::Solid::new( Vector3::ZERO, @@ -3438,35 +3814,42 @@ mod tests { let mut invalid = hatch.clone(); invalid.paths.clear(); - assert!(validate_new_canvas_entity(&codec::EntityType::Hatch(invalid)) - .unwrap_err() - .contains("at least one boundary path")); + assert!( + validate_new_canvas_entity(&codec::EntityType::Hatch(invalid)) + .unwrap_err() + .contains("at least one boundary path") + ); let mut invalid = hatch.clone(); invalid.is_solid = false; invalid.pattern_scale = 0.0; - assert!(validate_new_canvas_entity(&codec::EntityType::Hatch(invalid)) - .unwrap_err() - .contains("greater than zero")); + assert!( + validate_new_canvas_entity(&codec::EntityType::Hatch(invalid)) + .unwrap_err() + .contains("greater than zero") + ); let mut document = codec::CadDocument::new(); - let boundary = document.add_entity(codec::EntityType::Line( - codec::entities::Line::from_points( + let boundary = document + .add_entity(codec::EntityType::Line(codec::entities::Line::from_points( codec::types::Vector3::ZERO, codec::types::Vector3::UNIT_X, - ), - )).unwrap(); + ))) + .unwrap(); let mut associative = hatch.clone(); associative.is_associative = true; associative.paths[0].boundary_handles.push(boundary); validate_canvas_entity_references( &document, &codec::EntityType::Hatch(associative.clone()), - ).unwrap(); + ) + .unwrap(); associative.paths[0].boundary_handles[0] = codec::Handle::new(u64::MAX); assert!(validate_canvas_entity_references( &document, &codec::EntityType::Hatch(associative), - ).unwrap_err().contains("does not exist")); + ) + .unwrap_err() + .contains("does not exist")); } } diff --git a/crates/ocs_plugin_api/src/entity_coverage_types.rs b/crates/ocs_plugin_api/src/entity_coverage_types.rs index 5d101c694..49654d36a 100644 --- a/crates/ocs_plugin_api/src/entity_coverage_types.rs +++ b/crates/ocs_plugin_api/src/entity_coverage_types.rs @@ -1,5 +1,4 @@ /// Stable, language-neutral entity capability schema. - use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/ocs_plugin_api/src/host.rs b/crates/ocs_plugin_api/src/host.rs index 5880a0335..ad9966ff1 100644 --- a/crates/ocs_plugin_api/src/host.rs +++ b/crates/ocs_plugin_api/src/host.rs @@ -65,15 +65,37 @@ pub enum LogLevel { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum SolidPrimitive { /// Axis-aligned box from its center and full extents. - Box { center: [f64; 3], size: [f64; 3] }, + Box { + center: [f64; 3], + size: [f64; 3], + }, /// Right triangular prism from its minimum corner and extents. - Wedge { origin: [f64; 3], size: [f64; 3] }, + Wedge { + origin: [f64; 3], + size: [f64; 3], + }, /// Cylinder standing on the plane through `center`. - Cylinder { center: [f64; 3], radius: f64, height: f64 }, - Sphere { center: [f64; 3], radius: f64 }, - Torus { center: [f64; 3], major: f64, minor: f64 }, + Cylinder { + center: [f64; 3], + radius: f64, + height: f64, + }, + Sphere { + center: [f64; 3], + radius: f64, + }, + Torus { + center: [f64; 3], + major: f64, + minor: f64, + }, /// Regular pyramid with `sides` base edges. - Pyramid { center: [f64; 3], radius: f64, height: f64, sides: u32 }, + Pyramid { + center: [f64; 3], + radius: f64, + height: f64, + sides: u32, + }, } /// How two solids combine. @@ -159,12 +181,20 @@ pub enum TableOperation { /// Create a dimension style, optionally copying an existing one first. /// `properties` is a JSON object of DimStyle fields (see the Python docs); /// handles and xref fields are managed by the host and refused. - DimStyleCreate { name: String, copy_from: Option, properties: String }, + DimStyleCreate { + name: String, + copy_from: Option, + properties: String, + }, /// Change only the DimStyle fields the JSON object `properties` names. DimStyleModify { name: String, properties: String }, /// Rename a text or dimension style; references to it follow. `Standard` /// is never renamed and a case-only change is refused. - StyleRename { kind: TableStyleKind, from: String, to: String }, + StyleRename { + kind: TableStyleKind, + from: String, + to: String, + }, /// Delete a text or dimension style that is neither current nor in use. /// `Standard` is never deleted. StyleDelete { kind: TableStyleKind, name: String }, @@ -200,9 +230,17 @@ pub enum TableOperation { /// Create a simple linetype. `pattern` is signed lengths in drawing units: /// positive is a dash, negative a gap, zero a dot (2-12 elements with at /// least one dash or dot and one gap). Returns the linetype's handle. - LinetypeCreate { name: String, description: String, pattern: Vec }, + LinetypeCreate { + name: String, + description: String, + pattern: Vec, + }, /// Change a simple linetype's description or pattern in place. - LinetypeModify { name: String, description: Option, pattern: Option> }, + LinetypeModify { + name: String, + description: Option, + pattern: Option>, + }, /// Rename a linetype; layers and entities that use it follow. LinetypeRename { from: String, to: String }, /// Delete a linetype that no layer, entity, dimension style or the current @@ -257,7 +295,10 @@ pub struct TextStyleConfig { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum SolidOperation { /// Build a new solid on `layer` (layer `0` when `None`). - Create { primitive: SolidPrimitive, layer: Option }, + Create { + primitive: SolidPrimitive, + layer: Option, + }, /// Apply a rigid transform, given as a column-major 4x4 matrix, to an /// existing solid. Scaling and shear are refused. Transform { handle: Handle, matrix: [f64; 16] }, @@ -265,12 +306,25 @@ pub enum SolidOperation { /// ellipse, closed polyline or spline, ...). The new region goes on /// `layer`, or on the source's layer when `None`; `delete_source` erases /// the profile afterwards, as the REGION command does. - RegionFromProfile { source: Handle, layer: Option, delete_source: bool }, + RegionFromProfile { + source: Handle, + layer: Option, + delete_source: bool, + }, /// Build a plane surface from one closed planar profile entity. - SurfaceFromProfile { source: Handle, layer: Option, delete_source: bool }, + SurfaceFromProfile { + source: Handle, + layer: Option, + delete_source: bool, + }, /// Extrude a planar profile along `direction`: a solid when the profile is /// closed, a surface when it is open. - Extrude { source: Handle, direction: [f64; 3], layer: Option, delete_source: bool }, + Extrude { + source: Handle, + direction: [f64; 3], + layer: Option, + delete_source: bool, + }, /// Combine two solids into a new one. Both operands are consumed unless /// `keep_operands`; the result goes on `layer` or the first operand's /// layer. The kernel refuses some cases (coincident faces, cuts it has no @@ -280,7 +334,12 @@ pub enum SolidOperation { /// `origin`, `width` drawing units wide, keeping the picture's aspect. /// PNG, JPEG and BMP are stored as read; other formats are re-encoded as /// PNG. The frame goes on `layer` (layer `0` when `None`). - EmbedPicture { path: String, origin: [f64; 3], width: f64, layer: Option }, + EmbedPicture { + path: String, + origin: [f64; 3], + width: f64, + layer: Option, + }, Boolean { first: Handle, second: Handle, @@ -302,21 +361,41 @@ pub enum HostSettingValue { #[derive(Debug, Clone, PartialEq)] #[non_exhaustive] pub enum HostNotification { - InputLine { line: String }, + InputLine { + line: String, + }, Cancel, - DocumentChanged { version: u64 }, - SelectionChanged { handles: Vec }, + DocumentChanged { + version: u64, + }, + SelectionChanged { + handles: Vec, + }, Raw(Vec), /// V4 snapshot changed for a specific tab. Discriminant 5. - DocumentChangedV4 { tab_id: u64, version: u64 }, + DocumentChangedV4 { + tab_id: u64, + version: u64, + }, /// V4 tab closed notification. Discriminant 6. - DocumentTabClosed { tab_id: u64 }, + DocumentTabClosed { + tab_id: u64, + }, /// V4 selection changed for a specific tab. Discriminant 7. - SelectionChangedV4 { tab_id: u64, handles: Vec }, + SelectionChangedV4 { + tab_id: u64, + handles: Vec, + }, /// V7 active interactive-command state for a tab. Discriminant 8. - CommandStateChanged { tab_id: u64, command: Option }, + CommandStateChanged { + tab_id: u64, + command: Option, + }, /// V7 drawing epoch, broadcast even without an open shared view. Discriminant 9. - DrawingChanged { tab_id: u64, epoch: u64 }, + DrawingChanged { + tab_id: u64, + epoch: u64, + }, /// Fallback for notification variants added in future minor revisions. /// Carries the raw bincode payload so an older peer can ignore it without /// failing deserialization. @@ -329,57 +408,44 @@ impl Serialize for HostNotification { match self { HostNotification::InputLine { line } => { bytes.push(0); - bincode::serialize_into(&mut bytes, line) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, line).map_err(serde::ser::Error::custom)?; } HostNotification::Cancel => bytes.push(1), HostNotification::DocumentChanged { version } => { bytes.push(2); - bincode::serialize_into(&mut bytes, version) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, version).map_err(serde::ser::Error::custom)?; } HostNotification::SelectionChanged { handles } => { bytes.push(3); - bincode::serialize_into(&mut bytes, handles) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, handles).map_err(serde::ser::Error::custom)?; } HostNotification::Raw(data) => { bytes.push(4); - bincode::serialize_into(&mut bytes, data) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, data).map_err(serde::ser::Error::custom)?; } HostNotification::DocumentChangedV4 { tab_id, version } => { bytes.push(5); - bincode::serialize_into(&mut bytes, tab_id) - .map_err(serde::ser::Error::custom)?; - bincode::serialize_into(&mut bytes, version) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, tab_id).map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, version).map_err(serde::ser::Error::custom)?; } HostNotification::DocumentTabClosed { tab_id } => { bytes.push(6); - bincode::serialize_into(&mut bytes, tab_id) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, tab_id).map_err(serde::ser::Error::custom)?; } HostNotification::SelectionChangedV4 { tab_id, handles } => { bytes.push(7); - bincode::serialize_into(&mut bytes, tab_id) - .map_err(serde::ser::Error::custom)?; - bincode::serialize_into(&mut bytes, handles) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, tab_id).map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, handles).map_err(serde::ser::Error::custom)?; } HostNotification::CommandStateChanged { tab_id, command } => { bytes.push(8); - bincode::serialize_into(&mut bytes, tab_id) - .map_err(serde::ser::Error::custom)?; - bincode::serialize_into(&mut bytes, command) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, tab_id).map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, command).map_err(serde::ser::Error::custom)?; } HostNotification::DrawingChanged { tab_id, epoch } => { bytes.push(9); - bincode::serialize_into(&mut bytes, tab_id) - .map_err(serde::ser::Error::custom)?; - bincode::serialize_into(&mut bytes, epoch) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, tab_id).map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, epoch).map_err(serde::ser::Error::custom)?; } HostNotification::Unknown(raw) => bytes.extend_from_slice(raw), } @@ -434,14 +500,28 @@ impl<'de> Deserialize<'de> for HostNotification { #[derive(Debug, Clone, PartialEq)] #[non_exhaustive] pub enum PluginNotification { - Output { text: String }, - Error { text: String }, - Prompt { text: String }, - Progress { percent: u8 }, - Log { level: LogLevel, text: String }, + Output { + text: String, + }, + Error { + text: String, + }, + Prompt { + text: String, + }, + Progress { + percent: u8, + }, + Log { + level: LogLevel, + text: String, + }, Raw(Vec), /// V4 REPL status update. Discriminant 6. - ReplStatus { status: String, message: String }, + ReplStatus { + status: String, + message: String, + }, /// Fallback for notification variants added in future minor revisions. /// Carries the raw bincode payload so an older peer can ignore it without /// failing deserialization. @@ -454,42 +534,33 @@ impl Serialize for PluginNotification { match self { PluginNotification::Output { text } => { bytes.push(0); - bincode::serialize_into(&mut bytes, text) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, text).map_err(serde::ser::Error::custom)?; } PluginNotification::Error { text } => { bytes.push(1); - bincode::serialize_into(&mut bytes, text) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, text).map_err(serde::ser::Error::custom)?; } PluginNotification::Prompt { text } => { bytes.push(2); - bincode::serialize_into(&mut bytes, text) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, text).map_err(serde::ser::Error::custom)?; } PluginNotification::Progress { percent } => { bytes.push(3); - bincode::serialize_into(&mut bytes, percent) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, percent).map_err(serde::ser::Error::custom)?; } PluginNotification::Log { level, text } => { bytes.push(4); - bincode::serialize_into(&mut bytes, level) - .map_err(serde::ser::Error::custom)?; - bincode::serialize_into(&mut bytes, text) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, level).map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, text).map_err(serde::ser::Error::custom)?; } PluginNotification::Raw(data) => { bytes.push(5); - bincode::serialize_into(&mut bytes, data) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, data).map_err(serde::ser::Error::custom)?; } PluginNotification::ReplStatus { status, message } => { bytes.push(6); - bincode::serialize_into(&mut bytes, status) - .map_err(serde::ser::Error::custom)?; - bincode::serialize_into(&mut bytes, message) - .map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, status).map_err(serde::ser::Error::custom)?; + bincode::serialize_into(&mut bytes, message).map_err(serde::ser::Error::custom)?; } PluginNotification::Unknown(raw) => bytes.extend_from_slice(raw), } @@ -519,8 +590,8 @@ impl<'de> Deserialize<'de> for PluginNotification { .map(|percent| PluginNotification::Progress { percent }) .map_err(serde::de::Error::custom), 4 => { - let (level, text): (LogLevel, String) = bincode::deserialize(rest) - .map_err(serde::de::Error::custom)?; + let (level, text): (LogLevel, String) = + bincode::deserialize(rest).map_err(serde::de::Error::custom)?; Ok(PluginNotification::Log { level, text }) } 5 => bincode::deserialize(rest) @@ -641,7 +712,10 @@ pub enum PreviewWire { impl PreviewWire { /// Create a polyline preview wire with default host cyan color. pub fn new(points: Vec<[f64; 3]>) -> Self { - Self::Polyline { points, color: None } + Self::Polyline { + points, + color: None, + } } /// Optional RGBA color (0.0 to 1.0) specified for this preview wire. @@ -869,9 +943,7 @@ pub trait HostApi { /// Returns the optional `command_id` used to correlate the notification /// with a running command, and the notification payload. Long-running /// plugins should call this periodically to drain the bounded queue. - fn try_recv_notification( - &mut self, - ) -> Option<(Option, HostNotification)> { + fn try_recv_notification(&mut self) -> Option<(Option, HostNotification)> { None } @@ -949,7 +1021,9 @@ pub trait HostApi { } /// Current ordered selection in this session's tab (API v7). - fn selection(&self) -> Vec { Vec::new() } + fn selection(&self) -> Vec { + Vec::new() + } /// Replace this tab's selection with exactly these handles, in order. /// Missing or duplicate handles reject the request (API v7). diff --git a/crates/ocs_plugin_api/src/ipc/client.rs b/crates/ocs_plugin_api/src/ipc/client.rs index 1d10b1130..829a84255 100644 --- a/crates/ocs_plugin_api/src/ipc/client.rs +++ b/crates/ocs_plugin_api/src/ipc/client.rs @@ -16,7 +16,7 @@ use crate::ipc::protocol::{ HostResponse, HostToPlugin, PluginRequest, PluginResponse, PluginToHost, RunnerHandshake, }; use crate::ipc::transport::{recv, send}; -use crate::shm::{DocumentViewInfo, SharedDocumentReader, DocumentViewData}; +use crate::shm::{DocumentViewData, DocumentViewInfo, SharedDocumentReader}; /// Shared registry of active interactive commands, keyed by host-assigned id. pub type InteractiveRegistry = Rc>>>; @@ -60,7 +60,10 @@ impl IpcClient { &self, req: PluginRequest, ) -> Result { - send(&mut self.stream.borrow_mut(), &PluginToHost::Request(Box::new(req)))?; + send( + &mut self.stream.borrow_mut(), + &PluginToHost::Request(Box::new(req)), + )?; loop { match recv::(&mut self.stream.borrow_mut())? { HostToPlugin::Response(resp) => return Ok(*resp), @@ -389,16 +392,18 @@ impl HostApi for PluginHostApi { } } match self.doc_view.borrow().as_ref() { - Some(info) => match SharedDocumentReader::::open(Path::new(&info.path)) { - Ok(reader) => Box::new(reader), - Err(e) => { - eprintln!( - "[plugin] failed to open document view at {}: {e}", - info.path - ); - Box::new(EmptyDocumentReader) + Some(info) => { + match SharedDocumentReader::::open(Path::new(&info.path)) { + Ok(reader) => Box::new(reader), + Err(e) => { + eprintln!( + "[plugin] failed to open document view at {}: {e}", + info.path + ); + Box::new(EmptyDocumentReader) + } } - }, + } None => Box::new(EmptyDocumentReader), } } @@ -419,7 +424,9 @@ impl HostApi for PluginHostApi { } fn system_variable(&self, name: &str) -> Option { - match self.client.request(PluginRequest::GetSystemVariable { name: name.to_owned() }) { + match self.client.request(PluginRequest::GetSystemVariable { + name: name.to_owned(), + }) { Ok(PluginResponse::SystemVariable(value)) => value, _ => None, } @@ -451,11 +458,16 @@ impl HostApi for PluginHostApi { label: &str, entities: Vec, ) -> Result<(), String> { - match self.client.request(PluginRequest::UpdateEntitiesTransaction { - label: label.to_owned(), entities, - }) { + match self + .client + .request(PluginRequest::UpdateEntitiesTransaction { + label: label.to_owned(), + entities, + }) { Ok(PluginResponse::EntityTransactionResult(result)) => { - if result.is_ok() { self.document_cache = OnceCell::new(); } + if result.is_ok() { + self.document_cache = OnceCell::new(); + } result } Ok(PluginResponse::Error(error)) => Err(error), @@ -471,10 +483,18 @@ impl HostApi for PluginHostApi { } } - fn solid_operation(&mut self, operation: crate::host::SolidOperation) -> Result { - match self.client.request(PluginRequest::SolidOperation { operation }) { + fn solid_operation( + &mut self, + operation: crate::host::SolidOperation, + ) -> Result { + match self + .client + .request(PluginRequest::SolidOperation { operation }) + { Ok(PluginResponse::SolidResult(result)) => { - if result.is_ok() { self.document_cache = OnceCell::new(); } + if result.is_ok() { + self.document_cache = OnceCell::new(); + } result } Ok(PluginResponse::Error(error)) => Err(error), @@ -483,7 +503,10 @@ impl HostApi for PluginHostApi { } } - fn run_command(&mut self, request: crate::host::CommandRequest) -> Result { + fn run_command( + &mut self, + request: crate::host::CommandRequest, + ) -> Result { match self.client.request(PluginRequest::RunCommand { request }) { Ok(PluginResponse::CommandResult(result)) => { self.document_cache = OnceCell::new(); @@ -495,10 +518,18 @@ impl HostApi for PluginHostApi { } } - fn table_operation(&mut self, operation: crate::host::TableOperation) -> Result { - match self.client.request(PluginRequest::TableOperation { operation }) { + fn table_operation( + &mut self, + operation: crate::host::TableOperation, + ) -> Result { + match self + .client + .request(PluginRequest::TableOperation { operation }) + { Ok(PluginResponse::TableResult(result)) => { - if result.is_ok() { self.document_cache = OnceCell::new(); } + if result.is_ok() { + self.document_cache = OnceCell::new(); + } result } Ok(PluginResponse::Error(error)) => Err(error), @@ -508,7 +539,9 @@ impl HostApi for PluginHostApi { } fn set_selection(&mut self, handles: &[Handle]) -> Result<(), String> { - match self.client.request(PluginRequest::SetSelection { handles: handles.to_vec() }) { + match self.client.request(PluginRequest::SetSelection { + handles: handles.to_vec(), + }) { Ok(PluginResponse::SelectionResult(result)) => result, Ok(PluginResponse::Error(error)) => Err(error), Ok(other) => Err(format!("unexpected selection response: {other:?}")), @@ -555,7 +588,10 @@ impl HostApi for PluginHostApi { } fn execute_command(&mut self, cmd: &str) -> bool { - match self.client.request(PluginRequest::ExecuteCommand(cmd.to_string())) { + match self + .client + .request(PluginRequest::ExecuteCommand(cmd.to_string())) + { Ok(PluginResponse::Bool(b)) => b, Ok(other) => { eprintln!("[plugin] unexpected ExecuteCommand response: {other:?}"); @@ -662,7 +698,11 @@ mod tests { }, other => panic!("unexpected: {other:?}"), } - send(&mut peer, &HostToPlugin::Response(Box::new(PluginResponse::Ok))).unwrap(); + send( + &mut peer, + &HostToPlugin::Response(Box::new(PluginResponse::Ok)), + ) + .unwrap(); }); api.push_info("hello host"); peer_handle.join().unwrap(); @@ -719,7 +759,10 @@ mod tests { EntityType::Point(Point::new()), ]); peer_handle.join().unwrap(); - assert_eq!(handles, vec![Handle::new(10), Handle::new(11), Handle::new(12)]); + assert_eq!( + handles, + vec![Handle::new(10), Handle::new(11), Handle::new(12)] + ); } #[test] @@ -734,7 +777,11 @@ mod tests { }, other => panic!("unexpected: {other:?}"), } - send(&mut peer, &HostToPlugin::Response(Box::new(PluginResponse::Bool(true)))).unwrap(); + send( + &mut peer, + &HostToPlugin::Response(Box::new(PluginResponse::Bool(true))), + ) + .unwrap(); }); assert!(api.update_entity(EntityType::Point(Point::new()))); peer_handle.join().unwrap(); @@ -754,7 +801,11 @@ mod tests { }, other => panic!("unexpected: {other:?}"), } - send(&mut peer, &HostToPlugin::Response(Box::new(PluginResponse::Bool(true)))).unwrap(); + send( + &mut peer, + &HostToPlugin::Response(Box::new(PluginResponse::Bool(true))), + ) + .unwrap(); }); assert!(api.remove_entity(Handle::new(7))); peer_handle.join().unwrap(); diff --git a/crates/ocs_plugin_api/src/ipc/mod.rs b/crates/ocs_plugin_api/src/ipc/mod.rs index 971b47e9b..befb23aac 100644 --- a/crates/ocs_plugin_api/src/ipc/mod.rs +++ b/crates/ocs_plugin_api/src/ipc/mod.rs @@ -23,13 +23,13 @@ pub mod client; #[cfg(feature = "host")] pub mod protocol; #[cfg(feature = "host")] +pub mod proxy; +#[cfg(feature = "host")] pub mod server; #[cfg(feature = "host")] pub mod transport; #[cfg(feature = "host")] pub mod v4; -#[cfg(feature = "host")] -pub mod proxy; #[cfg(all(test, feature = "host"))] mod tests { diff --git a/crates/ocs_plugin_api/src/ipc/protocol.rs b/crates/ocs_plugin_api/src/ipc/protocol.rs index a7f282be7..3832140af 100644 --- a/crates/ocs_plugin_api/src/ipc/protocol.rs +++ b/crates/ocs_plugin_api/src/ipc/protocol.rs @@ -21,9 +21,9 @@ use crate::host::{CommandSource, CommandStep, HostSettingValue}; use crate::manifest::ApiVersion; use crate::ribbon::owned::{OwnedPluginManifest, OwnedRibbonGroup}; +pub use crate::host::PreviewWire; pub use codec::xdata::{ExtendedDataRecord, XDataValue}; pub use codec::{CadDocument, EntityType, Handle}; -pub use crate::host::PreviewWire; /// Events the host forwards to an active plugin `InteractiveCommand`. #[derive(Debug, Clone, Serialize, Deserialize)] @@ -50,7 +50,10 @@ pub enum InteractiveEvent { #[derive(Debug, Serialize, Deserialize)] pub enum RunnerHandshake { Token(String), - TokenV4 { token: String, protocol_version: u32 }, + TokenV4 { + token: String, + protocol_version: u32, + }, } /// Environment variable through which the host passes the pre-shared @@ -83,7 +86,9 @@ pub enum HostRequest { tab_index: usize, }, /// V7: release an interactive command after completion or cancellation. - DropInteractive { command_id: u64 }, + DropInteractive { + command_id: u64, + }, /// V7: cursor move preview update during an interactive command. CursorMove { command_id: u64, @@ -145,13 +150,19 @@ pub enum PluginRequest { AddEntities(Vec), /// V4: ask the host to create/refresh a tab-keyed shared-memory document /// view and return the file path + current version. - OpenDocumentViewV4 { tab_id: u64 }, + OpenDocumentViewV4 { + tab_id: u64, + }, /// V4: ask the host to close the tab-keyed shared-memory document view. - CloseDocumentViewV4 { tab_id: u64 }, + CloseDocumentViewV4 { + tab_id: u64, + }, /// V4: ask the host for the stable tab identifier of the active tab. GetTabId, /// V5: ask the host for the filesystem path of the document in `tab_id`. - DocumentPath { tab_id: u64 }, + DocumentPath { + tab_id: u64, + }, /// Add a layer to the active document with full initial properties. AddLayer(crate::host::LayerConfig), /// Modify specified properties of an existing layer in the active document. @@ -159,20 +170,36 @@ pub enum PluginRequest { /// Run a command on the active document tab's command line (AutoLISP style). ExecuteCommand(String), /// Read a host-managed setting without nested command dispatch. - GetSystemVariable { name: String }, + GetSystemVariable { + name: String, + }, /// Change a host-managed setting without nested command dispatch. - SetSystemVariable { name: String, value: HostSettingValue }, + SetSystemVariable { + name: String, + value: HostSettingValue, + }, /// V7: validate and replace existing entities in a single undo step. - UpdateEntitiesTransaction { label: String, entities: Vec }, + UpdateEntitiesTransaction { + label: String, + entities: Vec, + }, /// V7: synchronous selection read/write for the dispatch tab. GetSelection, - SetSelection { handles: Vec }, + SetSelection { + handles: Vec, + }, /// V7 (additive): kernel-backed solid create or transform. - SolidOperation { operation: crate::host::SolidOperation }, + SolidOperation { + operation: crate::host::SolidOperation, + }, /// V7 (additive): drawing table record create/modify/rename/delete. - TableOperation { operation: crate::host::TableOperation }, + TableOperation { + operation: crate::host::TableOperation, + }, /// V7 (additive): drive an OCS command. - RunCommand { request: crate::host::CommandRequest }, + RunCommand { + request: crate::host::CommandRequest, + }, } /// Responses the host sends back for `PluginRequest`. diff --git a/crates/ocs_plugin_api/src/ipc/proxy.rs b/crates/ocs_plugin_api/src/ipc/proxy.rs index 6b5172b08..7816903d6 100644 --- a/crates/ocs_plugin_api/src/ipc/proxy.rs +++ b/crates/ocs_plugin_api/src/ipc/proxy.rs @@ -50,7 +50,10 @@ impl From for PluginRequestError { } /// Send a length-framed bincode message over a generic stream. -pub fn send_framed(writer: &mut W, msg: &impl serde::Serialize) -> Result<(), ProxyError> { +pub fn send_framed( + writer: &mut W, + msg: &impl serde::Serialize, +) -> Result<(), ProxyError> { let bytes = bincode::serialize(msg)?; if bytes.len() > MAX_MESSAGE_SIZE { return Err(ProxyError::TooLarge(bytes.len())); @@ -63,7 +66,9 @@ pub fn send_framed(writer: &mut W, msg: &impl serde::Serialize) -> Res } /// Receive a length-framed bincode message from a generic stream. -pub fn recv_framed(reader: &mut R) -> Result { +pub fn recv_framed( + reader: &mut R, +) -> Result { let mut len_buf = [0u8; 8]; reader.read_exact(&mut len_buf)?; let len = u64::from_le_bytes(len_buf) as usize; @@ -114,10 +119,12 @@ fn read_with_poll( let mut off = 0; while off < buf.len() { match reader.read(&mut buf[off..]) { - Ok(0) => return Err(PollError::Io(ProxyError::Io(std::io::Error::new( - std::io::ErrorKind::UnexpectedEof, - "proxy disconnected", - )))), + Ok(0) => { + return Err(PollError::Io(ProxyError::Io(std::io::Error::new( + std::io::ErrorKind::UnexpectedEof, + "proxy disconnected", + )))) + } Ok(n) => off += n, Err(e) if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut => { if let Err(err) = poll() { @@ -386,7 +393,8 @@ mod tests { run_request_proxy(listener, sender, token).ok(); }); - let sender = ProxyPluginRequestSender::connect_with_token("127.0.0.1", port, &token).unwrap(); + let sender = + ProxyPluginRequestSender::connect_with_token("127.0.0.1", port, &token).unwrap(); let resp = sender .request(PluginRequest::PushInfo("hello proxy".to_string())) .unwrap(); diff --git a/crates/ocs_plugin_api/src/ipc/server.rs b/crates/ocs_plugin_api/src/ipc/server.rs index 1941184d2..2856700a2 100644 --- a/crates/ocs_plugin_api/src/ipc/server.rs +++ b/crates/ocs_plugin_api/src/ipc/server.rs @@ -110,8 +110,12 @@ pub fn handle_plugin_request( ), GetSelection => PluginResponse::Selection(host.selection()), SetSelection { handles } => PluginResponse::SelectionResult(host.set_selection(&handles)), - SolidOperation { operation } => PluginResponse::SolidResult(host.solid_operation(operation)), - TableOperation { operation } => PluginResponse::TableResult(host.table_operation(operation)), + SolidOperation { operation } => { + PluginResponse::SolidResult(host.solid_operation(operation)) + } + TableOperation { operation } => { + PluginResponse::TableResult(host.table_operation(operation)) + } RunCommand { request } => PluginResponse::CommandResult(host.run_command(request)), } } diff --git a/crates/ocs_plugin_api/src/ipc/v4/client.rs b/crates/ocs_plugin_api/src/ipc/v4/client.rs index 41834fc05..22cea2f08 100644 --- a/crates/ocs_plugin_api/src/ipc/v4/client.rs +++ b/crates/ocs_plugin_api/src/ipc/v4/client.rs @@ -16,8 +16,8 @@ use interprocess::local_socket::{GenericNamespaced, Stream, ToNsName}; use interprocess::TryClone; use crate::host::{ - DocumentReader, ExecutionResult, HostApi, HostNotification, InteractiveCommand, - PluginNotification, PluginRequestSender, PluginRequestError, ReaderEntity, HostSettingValue, + DocumentReader, ExecutionResult, HostApi, HostNotification, HostSettingValue, + InteractiveCommand, PluginNotification, PluginRequestError, PluginRequestSender, ReaderEntity, }; use crate::ipc::protocol::{ HostRequest, HostResponse, PluginRequest, PluginResponse, RunnerHandshake, @@ -26,7 +26,7 @@ use crate::ipc::transport::{recv, send, TransportError}; use crate::ipc::v4::protocol::{ HostToPluginV4, NotificationEnvelope, PluginToHostV4, V4_PROTOCOL_VERSION, }; -use crate::shm::{DocumentViewInfo, SharedDocumentReader, DocumentViewData}; +use crate::shm::{DocumentViewData, DocumentViewInfo, SharedDocumentReader}; /// Default capacity of the bounded host→plugin notification queue. const DEFAULT_NOTIFY_QUEUE_SIZE: usize = 1024; @@ -89,8 +89,7 @@ impl V4Client { ))) })?; - let (notify_tx, notify_rx) = - mpsc::sync_channel(notify_queue_size()); + let (notify_tx, notify_rx) = mpsc::sync_channel(notify_queue_size()); let (runner_tx, runner_rx) = mpsc::channel(); let shared = Arc::new(Shared { @@ -101,14 +100,7 @@ impl V4Client { let shared_for_reader = Arc::clone(&shared); let notifications = Arc::new(Mutex::new(notify_rx)); - std::thread::spawn(move || { - reader_thread( - reader, - shared_for_reader, - notify_tx, - runner_tx, - ) - }); + std::thread::spawn(move || reader_thread(reader, shared_for_reader, notify_tx, runner_tx)); Ok(Self { shared, @@ -133,7 +125,11 @@ impl V4Client { /// Non-blocking poll for host-to-plugin notifications. pub fn try_recv_notification(&self) -> Option<(Option, HostNotification)> { - self.notifications.lock().unwrap_or_else(|e| e.into_inner()).try_recv().ok() + self.notifications + .lock() + .unwrap_or_else(|e| e.into_inner()) + .try_recv() + .ok() } /// Send a plugin response for a host request. @@ -143,18 +139,11 @@ impl V4Client { resp: HostResponse, ) -> Result<(), crate::ipc::transport::TransportError> { let mut writer = self.shared.writer.lock().unwrap_or_else(|e| e.into_inner()); - send( - &mut writer, - &PluginToHostV4::Response { id, payload: resp }, - ) + send(&mut writer, &PluginToHostV4::Response { id, payload: resp }) } /// Send a plugin-to-host notification. - pub fn notify_plugin( - &self, - command_id: Option, - notification: PluginNotification, - ) { + pub fn notify_plugin(&self, command_id: Option, notification: PluginNotification) { let envelope = NotificationEnvelope { command_id, payload: notification, @@ -168,10 +157,7 @@ impl V4Client { /// Build a callback that sends a delayed `CodeExecutionResult` response for /// the given request id. Used by `start_execute_code` implementations that /// run REPL code on a background thread. - pub fn execute_code_responder( - &self, - id: u64, - ) -> Box { + pub fn execute_code_responder(&self, id: u64) -> Box { let shared = Arc::clone(&self.shared); Box::new(move |result| { let mut writer = shared.writer.lock().unwrap_or_else(|e| e.into_inner()); @@ -235,7 +221,10 @@ fn reader_thread( } } Ok(HostToPluginV4::Notification(envelope)) => { - if notify_tx.try_send((envelope.command_id, envelope.payload)).is_err() { + if notify_tx + .try_send((envelope.command_id, envelope.payload)) + .is_err() + { let now = Instant::now(); let should_log = last_drop_log .map(|t| now.duration_since(t) >= DROP_LOG_INTERVAL) @@ -247,7 +236,10 @@ fn reader_thread( } } Ok(HostToPluginV4::Request { id, payload }) => { - if runner_tx.send(RunnerFrame::Request { id, payload }).is_err() { + if runner_tx + .send(RunnerFrame::Request { id, payload }) + .is_err() + { // Runner loop has exited; stop reading. break; } @@ -510,9 +502,7 @@ impl HostApi for V4PluginHostApi { fn write_record(&mut self, handle: Handle, record: ExtendedDataRecord) -> bool { let app = record.application_name.clone(); - match self - .request(PluginRequest::WriteRecord { handle, record }) - { + match self.request(PluginRequest::WriteRecord { handle, record }) { Ok(PluginResponse::Bool(b)) => { if b { self.record_cache.borrow_mut().remove(&(handle, app)); @@ -631,16 +621,18 @@ impl HostApi for V4PluginHostApi { } } match self.doc_view.borrow().as_ref() { - Some(info) => match SharedDocumentReader::::open(Path::new(&info.path)) { - Ok(reader) => Box::new(reader), - Err(e) => { - eprintln!( - "[plugin] failed to open document view at {}: {e}", - info.path - ); - Box::new(EmptyDocumentReader) + Some(info) => { + match SharedDocumentReader::::open(Path::new(&info.path)) { + Ok(reader) => Box::new(reader), + Err(e) => { + eprintln!( + "[plugin] failed to open document view at {}: {e}", + info.path + ); + Box::new(EmptyDocumentReader) + } } - }, + } None => Box::new(EmptyDocumentReader), } } @@ -657,7 +649,11 @@ impl HostApi for V4PluginHostApi { } fn try_recv_notification(&mut self) -> Option<(Option, HostNotification)> { - self.notifications.lock().unwrap_or_else(|e| e.into_inner()).try_recv().ok() + self.notifications + .lock() + .unwrap_or_else(|e| e.into_inner()) + .try_recv() + .ok() } fn plugin_request_sender(&self) -> Option> { @@ -734,7 +730,9 @@ impl HostApi for V4PluginHostApi { } fn system_variable(&self, name: &str) -> Option { - match self.request(PluginRequest::GetSystemVariable { name: name.to_owned() }) { + match self.request(PluginRequest::GetSystemVariable { + name: name.to_owned(), + }) { Ok(PluginResponse::SystemVariable(value)) => value, _ => None, } @@ -767,10 +765,13 @@ impl HostApi for V4PluginHostApi { entities: Vec, ) -> Result<(), String> { match self.request(PluginRequest::UpdateEntitiesTransaction { - label: label.to_owned(), entities, + label: label.to_owned(), + entities, }) { Ok(PluginResponse::EntityTransactionResult(result)) => { - if result.is_ok() { self.document_cache = OnceCell::new(); } + if result.is_ok() { + self.document_cache = OnceCell::new(); + } result } Ok(PluginResponse::Error(error)) => Err(error), @@ -786,10 +787,15 @@ impl HostApi for V4PluginHostApi { } } - fn solid_operation(&mut self, operation: crate::host::SolidOperation) -> Result { + fn solid_operation( + &mut self, + operation: crate::host::SolidOperation, + ) -> Result { match self.request(PluginRequest::SolidOperation { operation }) { Ok(PluginResponse::SolidResult(result)) => { - if result.is_ok() { self.document_cache = OnceCell::new(); } + if result.is_ok() { + self.document_cache = OnceCell::new(); + } result } Ok(PluginResponse::Error(error)) => Err(error), @@ -798,7 +804,10 @@ impl HostApi for V4PluginHostApi { } } - fn run_command(&mut self, request: crate::host::CommandRequest) -> Result { + fn run_command( + &mut self, + request: crate::host::CommandRequest, + ) -> Result { match self.request(PluginRequest::RunCommand { request }) { Ok(PluginResponse::CommandResult(result)) => { self.document_cache = OnceCell::new(); @@ -810,10 +819,15 @@ impl HostApi for V4PluginHostApi { } } - fn table_operation(&mut self, operation: crate::host::TableOperation) -> Result { + fn table_operation( + &mut self, + operation: crate::host::TableOperation, + ) -> Result { match self.request(PluginRequest::TableOperation { operation }) { Ok(PluginResponse::TableResult(result)) => { - if result.is_ok() { self.document_cache = OnceCell::new(); } + if result.is_ok() { + self.document_cache = OnceCell::new(); + } result } Ok(PluginResponse::Error(error)) => Err(error), @@ -823,7 +837,9 @@ impl HostApi for V4PluginHostApi { } fn set_selection(&mut self, handles: &[Handle]) -> Result<(), String> { - match self.request(PluginRequest::SetSelection { handles: handles.to_vec() }) { + match self.request(PluginRequest::SetSelection { + handles: handles.to_vec(), + }) { Ok(PluginResponse::SelectionResult(result)) => result, Ok(PluginResponse::Error(error)) => Err(error), Ok(other) => Err(format!("unexpected selection response: {other:?}")), @@ -899,9 +915,7 @@ impl V4Client { }); let shared_for_reader = Arc::clone(&shared); let notifications = Arc::new(Mutex::new(notify_rx)); - std::thread::spawn(move || { - reader_thread(reader, shared_for_reader, notify_tx, runner_tx) - }); + std::thread::spawn(move || reader_thread(reader, shared_for_reader, notify_tx, runner_tx)); Self { shared, notifications, @@ -1081,7 +1095,10 @@ mod tests { }); assert!(matches!(first.join().unwrap(), PluginResponse::Bool(true))); - assert!(matches!(second.join().unwrap(), PluginResponse::Bool(false))); + assert!(matches!( + second.join().unwrap(), + PluginResponse::Bool(false) + )); runner.join().unwrap(); } diff --git a/crates/ocs_plugin_api/src/ipc/v4/protocol.rs b/crates/ocs_plugin_api/src/ipc/v4/protocol.rs index 7f5822603..0b4e206d9 100644 --- a/crates/ocs_plugin_api/src/ipc/v4/protocol.rs +++ b/crates/ocs_plugin_api/src/ipc/v4/protocol.rs @@ -53,7 +53,10 @@ pub enum PluginToHostV4 { tab_id: Option, payload: PluginRequest, }, - Response { id: u64, payload: HostResponse }, + Response { + id: u64, + payload: HostResponse, + }, Notification(NotificationEnvelope), } @@ -95,11 +98,17 @@ mod tests { let bytes = bincode::serialize(¬ification).unwrap(); let decoded: HostNotification = bincode::deserialize(&bytes).unwrap(); assert_eq!(decoded, notification); - let stopped = HostNotification::CommandStateChanged { tab_id: 42, command: None }; + let stopped = HostNotification::CommandStateChanged { + tab_id: 42, + command: None, + }; let bytes = bincode::serialize(&stopped).unwrap(); let decoded: HostNotification = bincode::deserialize(&bytes).unwrap(); assert_eq!(decoded, stopped); - let changed = HostNotification::DrawingChanged { tab_id: 42, epoch: 7 }; + let changed = HostNotification::DrawingChanged { + tab_id: 42, + epoch: 7, + }; let bytes = bincode::serialize(&changed).unwrap(); let decoded: HostNotification = bincode::deserialize(&bytes).unwrap(); assert_eq!(decoded, changed); @@ -109,19 +118,33 @@ mod tests { fn command_request_and_outcome_roundtrip() { use crate::host::{CommandOutcome, CommandRequest}; for request in [ - CommandRequest::Run { line: "LINE 0,0 1,1".into() }, - CommandRequest::Start { name: "OFFSET".into() }, - CommandRequest::Point { point: [1.0, 2.0, 3.0] }, + CommandRequest::Run { + line: "LINE 0,0 1,1".into(), + }, + CommandRequest::Start { + name: "OFFSET".into(), + }, + CommandRequest::Point { + point: [1.0, 2.0, 3.0], + }, CommandRequest::Text { text: "2".into() }, CommandRequest::Token { text: "R".into() }, - CommandRequest::Entity { handle: codec::Handle::new(9), point: [0.0; 3] }, + CommandRequest::Entity { + handle: codec::Handle::new(9), + point: [0.0; 3], + }, CommandRequest::Selection, CommandRequest::Enter, CommandRequest::Cancel, ] { - let bytes = bincode::serialize(&PluginRequest::RunCommand { request: request.clone() }).unwrap(); - assert!(matches!(bincode::deserialize::(&bytes).unwrap(), - PluginRequest::RunCommand { request: decoded } if decoded == request)); + let bytes = bincode::serialize(&PluginRequest::RunCommand { + request: request.clone(), + }) + .unwrap(); + assert!( + matches!(bincode::deserialize::(&bytes).unwrap(), + PluginRequest::RunCommand { request: decoded } if decoded == request) + ); } let outcome = CommandOutcome { status: "waiting_input".into(), @@ -137,8 +160,10 @@ mod tests { }; for result in [Ok(outcome), Err("refused".to_owned())] { let bytes = bincode::serialize(&PluginResponse::CommandResult(result.clone())).unwrap(); - assert!(matches!(bincode::deserialize::(&bytes).unwrap(), - PluginResponse::CommandResult(decoded) if decoded == result)); + assert!( + matches!(bincode::deserialize::(&bytes).unwrap(), + PluginResponse::CommandResult(decoded) if decoded == result) + ); } } @@ -155,19 +180,60 @@ mod tests { ..Default::default() }, }, - TableOperation::LayerModify { config: LayerConfig { name: "Walls".into(), off: Some(true), ..Default::default() } }, - TableOperation::LayerRename { from: "A".into(), to: "B".into() }, - TableOperation::LayerDelete { name: "A".into(), erase_objects: true }, + TableOperation::LayerModify { + config: LayerConfig { + name: "Walls".into(), + off: Some(true), + ..Default::default() + }, + }, + TableOperation::LayerRename { + from: "A".into(), + to: "B".into(), + }, + TableOperation::LayerDelete { + name: "A".into(), + erase_objects: true, + }, TableOperation::LayerSetCurrent { name: "A".into() }, TableOperation::TextStyleCreate { - config: crate::host::TextStyleConfig { name: "T".into(), height: Some(2.5), oblique_angle: Some(0.2), backward: Some(true), ..Default::default() }, + config: crate::host::TextStyleConfig { + name: "T".into(), + height: Some(2.5), + oblique_angle: Some(0.2), + backward: Some(true), + ..Default::default() + }, + }, + TableOperation::TextStyleModify { + config: crate::host::TextStyleConfig { + name: "T".into(), + font_file: Some("romans".into()), + ..Default::default() + }, + }, + TableOperation::DimStyleCreate { + name: "D".into(), + copy_from: Some("Standard".into()), + properties: "{\"dimscale\":2}".into(), + }, + TableOperation::DimStyleModify { + name: "D".into(), + properties: "{}".into(), + }, + TableOperation::StyleRename { + kind: crate::host::TableStyleKind::Text, + from: "A".into(), + to: "B".into(), + }, + TableOperation::StyleDelete { + kind: crate::host::TableStyleKind::Dim, + name: "A".into(), + }, + TableOperation::StyleSetCurrent { + kind: crate::host::TableStyleKind::Dim, + name: "A".into(), }, - TableOperation::TextStyleModify { config: crate::host::TextStyleConfig { name: "T".into(), font_file: Some("romans".into()), ..Default::default() } }, - TableOperation::DimStyleCreate { name: "D".into(), copy_from: Some("Standard".into()), properties: "{\"dimscale\":2}".into() }, - TableOperation::DimStyleModify { name: "D".into(), properties: "{}".into() }, - TableOperation::StyleRename { kind: crate::host::TableStyleKind::Text, from: "A".into(), to: "B".into() }, - TableOperation::StyleDelete { kind: crate::host::TableStyleKind::Dim, name: "A".into() }, - TableOperation::StyleSetCurrent { kind: crate::host::TableStyleKind::Dim, name: "A".into() }, TableOperation::BlockCreate { name: "B".into(), entities: vec![codec::Handle::new(4), codec::Handle::new(5)], @@ -175,31 +241,67 @@ mod tests { erase_originals: true, description: Some("d".into()), }, - TableOperation::BlockModify { name: "B".into(), description: None, explodable: Some(false), scale_uniformly: Some(true) }, - TableOperation::BlockRename { from: "B".into(), to: "C".into() }, + TableOperation::BlockModify { + name: "B".into(), + description: None, + explodable: Some(false), + scale_uniformly: Some(true), + }, + TableOperation::BlockRename { + from: "B".into(), + to: "C".into(), + }, TableOperation::BlockDelete { name: "C".into() }, TableOperation::BlockEntityAdd { block: "C".into(), entity: codec::EntityType::Line(codec::entities::Line::new()), }, - TableOperation::LinetypeCreate { name: "L".into(), description: "d".into(), pattern: vec![1.0, -1.0] }, - TableOperation::LinetypeModify { name: "L".into(), description: None, pattern: Some(vec![2.0, -2.0]) }, - TableOperation::LinetypeRename { from: "L".into(), to: "M".into() }, + TableOperation::LinetypeCreate { + name: "L".into(), + description: "d".into(), + pattern: vec![1.0, -1.0], + }, + TableOperation::LinetypeModify { + name: "L".into(), + description: None, + pattern: Some(vec![2.0, -2.0]), + }, + TableOperation::LinetypeRename { + from: "L".into(), + to: "M".into(), + }, TableOperation::LinetypeDelete { name: "M".into() }, TableOperation::LayoutCreate { name: "S".into() }, - TableOperation::LayoutRename { from: "S".into(), to: "T".into() }, + TableOperation::LayoutRename { + from: "S".into(), + to: "T".into(), + }, TableOperation::LayoutDelete { name: "T".into() }, - TableOperation::LayoutSetCurrent { name: "Model".into() }, - TableOperation::LayoutSetPage { name: "S".into(), paper_size: Some([420.0, 297.0]), rotation: Some(90), scale: Some([1.0, 50.0]) }, + TableOperation::LayoutSetCurrent { + name: "Model".into(), + }, + TableOperation::LayoutSetPage { + name: "S".into(), + paper_size: Some([420.0, 297.0]), + rotation: Some(90), + scale: Some([1.0, 50.0]), + }, ] { - let bytes = bincode::serialize(&PluginRequest::TableOperation { operation: operation.clone() }).unwrap(); - assert!(matches!(bincode::deserialize::(&bytes).unwrap(), - PluginRequest::TableOperation { operation: decoded } if decoded == operation)); + let bytes = bincode::serialize(&PluginRequest::TableOperation { + operation: operation.clone(), + }) + .unwrap(); + assert!( + matches!(bincode::deserialize::(&bytes).unwrap(), + PluginRequest::TableOperation { operation: decoded } if decoded == operation) + ); } for result in [Ok(codec::Handle::new(9)), Err("refused".to_owned())] { let bytes = bincode::serialize(&PluginResponse::TableResult(result.clone())).unwrap(); - assert!(matches!(bincode::deserialize::(&bytes).unwrap(), - PluginResponse::TableResult(decoded) if decoded == result)); + assert!( + matches!(bincode::deserialize::(&bytes).unwrap(), + PluginResponse::TableResult(decoded) if decoded == result) + ); } } @@ -208,12 +310,29 @@ mod tests { use crate::host::{SolidOperation, SolidPrimitive}; for operation in [ SolidOperation::Create { - primitive: SolidPrimitive::Pyramid { center: [1.0, 2.0, 3.0], radius: 4.0, height: 5.0, sides: 6 }, + primitive: SolidPrimitive::Pyramid { + center: [1.0, 2.0, 3.0], + radius: 4.0, + height: 5.0, + sides: 6, + }, layer: Some("SOLIDS".into()), }, - SolidOperation::Transform { handle: codec::Handle::new(7), matrix: [1.0; 16] }, - SolidOperation::RegionFromProfile { source: codec::Handle::new(3), layer: None, delete_source: true }, - SolidOperation::EmbedPicture { path: "p.png".into(), origin: [0.0; 3], width: 5.0, layer: None }, + SolidOperation::Transform { + handle: codec::Handle::new(7), + matrix: [1.0; 16], + }, + SolidOperation::RegionFromProfile { + source: codec::Handle::new(3), + layer: None, + delete_source: true, + }, + SolidOperation::EmbedPicture { + path: "p.png".into(), + origin: [0.0; 3], + width: 5.0, + layer: None, + }, SolidOperation::Boolean { first: codec::Handle::new(1), second: codec::Handle::new(2), @@ -221,30 +340,52 @@ mod tests { layer: None, keep_operands: false, }, - SolidOperation::SurfaceFromProfile { source: codec::Handle::new(4), layer: Some("S".into()), delete_source: false }, - SolidOperation::Extrude { source: codec::Handle::new(5), direction: [0.0, 0.0, 2.0], layer: None, delete_source: true }, + SolidOperation::SurfaceFromProfile { + source: codec::Handle::new(4), + layer: Some("S".into()), + delete_source: false, + }, + SolidOperation::Extrude { + source: codec::Handle::new(5), + direction: [0.0, 0.0, 2.0], + layer: None, + delete_source: true, + }, ] { - let bytes = bincode::serialize(&PluginRequest::SolidOperation { operation: operation.clone() }).unwrap(); - assert!(matches!(bincode::deserialize::(&bytes).unwrap(), - PluginRequest::SolidOperation { operation: decoded } if decoded == operation)); + let bytes = bincode::serialize(&PluginRequest::SolidOperation { + operation: operation.clone(), + }) + .unwrap(); + assert!( + matches!(bincode::deserialize::(&bytes).unwrap(), + PluginRequest::SolidOperation { operation: decoded } if decoded == operation) + ); } for result in [Ok(codec::Handle::new(9)), Err("refused".to_owned())] { let bytes = bincode::serialize(&PluginResponse::SolidResult(result.clone())).unwrap(); - assert!(matches!(bincode::deserialize::(&bytes).unwrap(), - PluginResponse::SolidResult(decoded) if decoded == result)); + assert!( + matches!(bincode::deserialize::(&bytes).unwrap(), + PluginResponse::SolidResult(decoded) if decoded == result) + ); } } #[test] fn selection_request_and_response_roundtrip() { - let request = PluginRequest::SetSelection { handles: vec![codec::Handle::new(9)] }; + let request = PluginRequest::SetSelection { + handles: vec![codec::Handle::new(9)], + }; let bytes = bincode::serialize(&request).unwrap(); - assert!(matches!(bincode::deserialize::(&bytes).unwrap(), - PluginRequest::SetSelection { handles } if handles == vec![codec::Handle::new(9)])); + assert!( + matches!(bincode::deserialize::(&bytes).unwrap(), + PluginRequest::SetSelection { handles } if handles == vec![codec::Handle::new(9)]) + ); let response = PluginResponse::Selection(vec![codec::Handle::new(9)]); let bytes = bincode::serialize(&response).unwrap(); - assert!(matches!(bincode::deserialize::(&bytes).unwrap(), - PluginResponse::Selection(handles) if handles == vec![codec::Handle::new(9)])); + assert!( + matches!(bincode::deserialize::(&bytes).unwrap(), + PluginResponse::Selection(handles) if handles == vec![codec::Handle::new(9)]) + ); } #[test] diff --git a/crates/ocs_plugin_api/src/ipc/v4/server.rs b/crates/ocs_plugin_api/src/ipc/v4/server.rs index 08e38c669..d6fdea7d5 100644 --- a/crates/ocs_plugin_api/src/ipc/v4/server.rs +++ b/crates/ocs_plugin_api/src/ipc/v4/server.rs @@ -26,7 +26,10 @@ macro_rules! vlog { /// A frame delivered from the V4 reader thread to the host main thread. pub enum HostIncoming { - Response { id: u64, payload: crate::ipc::protocol::HostResponse }, + Response { + id: u64, + payload: crate::ipc::protocol::HostResponse, + }, Request { id: u64, tab_id: Option, diff --git a/crates/ocs_plugin_api/src/lib.rs b/crates/ocs_plugin_api/src/lib.rs index c36f23d49..e315972eb 100644 --- a/crates/ocs_plugin_api/src/lib.rs +++ b/crates/ocs_plugin_api/src/lib.rs @@ -32,12 +32,12 @@ //! - `runner` — child-process runner entry point. //! - `shm` — shared-memory document views. +pub mod entity_coverage; +pub mod entity_coverage_types; pub mod manifest; pub mod ribbon; pub mod type_registry; pub mod type_registry_types; -pub mod entity_coverage; -pub mod entity_coverage_types; pub mod version_info; /// Runtime host surface — only built with the `host` feature (pulls `opencadcodec`). @@ -66,17 +66,19 @@ pub mod shm; #[cfg(feature = "host")] pub mod runner; +pub use entity_coverage::{ + get_embedded_entity_coverage_json, EntityCoverageCatalog, EntityKindCoverage, EntityScope, + ModelAccess, PropertyCoverage, +}; pub use manifest::{ effective_max_api_version, host_accepts_plugin_version, ApiVersion, PluginManifest, API_VERSION, API_VERSION_MIN_SUPPORTED, MAX_API_VERSION_ENV, }; pub use ribbon::{CadModule, IconKind, ModuleEvent, RibbonGroup, RibbonItem, StyleKey, ToolDef}; pub use type_registry::{ - get_embedded_type_registry_json, EnumVariantInfo, FieldInfo, MethodInfo, ParameterInfo, - TypeId, TypeInfo, TypeKind, TypeRegistry, + get_embedded_type_registry_json, EnumVariantInfo, FieldInfo, MethodInfo, ParameterInfo, TypeId, + TypeInfo, TypeKind, TypeRegistry, }; -pub use entity_coverage::{get_embedded_entity_coverage_json, EntityCoverageCatalog, - EntityKindCoverage, EntityScope, ModelAccess, PropertyCoverage}; pub use version_info::get_embedded_version_info_json; #[cfg(test)] diff --git a/crates/ocs_plugin_api/src/process.rs b/crates/ocs_plugin_api/src/process.rs index 760f1353c..117ed330d 100644 --- a/crates/ocs_plugin_api/src/process.rs +++ b/crates/ocs_plugin_api/src/process.rs @@ -28,7 +28,9 @@ use std::time::{Duration, Instant}; use interprocess::local_socket::traits::Listener; use interprocess::local_socket::{GenericNamespaced, ListenerOptions, Stream, ToNsName}; -use crate::host::{CommandSource, CommandStep, ExecutionResult, HostApi, HostNotification, PluginNotification}; +use crate::host::{ + CommandSource, CommandStep, ExecutionResult, HostApi, HostNotification, PluginNotification, +}; use crate::ipc::protocol::{ CadDocument, EntityType, Handle, HostRequest, HostResponse, HostToPlugin, InteractiveEvent, PluginToHost, RunnerHandshake, PLUGIN_TOKEN_ENV, @@ -100,7 +102,11 @@ impl HostApi for NullHost { ) -> Option<&crate::ipc::protocol::ExtendedDataRecord> { None } - fn write_record(&mut self, _handle: Handle, _record: crate::ipc::protocol::ExtendedDataRecord) -> bool { + fn write_record( + &mut self, + _handle: Handle, + _record: crate::ipc::protocol::ExtendedDataRecord, + ) -> bool { false } fn remove_record(&mut self, _handle: Handle, _app_name: &str) -> bool { @@ -112,10 +118,7 @@ impl HostApi for NullHost { fn push_output(&mut self, _msg: &str) {} fn push_error(&mut self, _msg: &str) {} fn start_interactive(&mut self, _command: Box) {} - fn plugin_state_any( - &self, - _plugin_id: &str, - ) -> Option<&(dyn std::any::Any + Send + Sync)> { + fn plugin_state_any(&self, _plugin_id: &str) -> Option<&(dyn std::any::Any + Send + Sync)> { None } fn plugin_state_any_mut( @@ -419,7 +422,11 @@ impl PluginProcess { // the V4 reader thread. The notification handler receives an empty // plugin id until we read the manifest below; that brief window is // fine because the plugin has not yet been asked to do work. - let stream = stream.lock().unwrap_or_else(|e| e.into_inner()).take().expect("V4 stream"); + let stream = stream + .lock() + .unwrap_or_else(|e| e.into_inner()) + .take() + .expect("V4 stream"); let plugin_id_placeholder = Arc::new(std::sync::Mutex::new(String::new())); let handler_for_v4: Arc, PluginNotification) + Send + Sync> = { let plugin_id = Arc::clone(&plugin_id_placeholder); @@ -434,7 +441,9 @@ impl PluginProcess { HostResponse::Manifest(m) => m, other => return Err(PluginError::UnexpectedResponse(Box::new(other))), }; - *plugin_id_placeholder.lock().unwrap_or_else(|e| e.into_inner()) = manifest.id.clone(); + *plugin_id_placeholder + .lock() + .unwrap_or_else(|e| e.into_inner()) = manifest.id.clone(); ribbon = match v4_conn.call(host, HostRequest::GetRibbon, no_op)? { HostResponse::Ribbon(r) => r, other => return Err(PluginError::UnexpectedResponse(Box::new(other))), @@ -626,8 +635,14 @@ impl PluginProcess { /// Release a completed or abandoned V4 interactive command in the runner. pub fn drop_interactive(&self, command_id: u64) -> Result<(), PluginError> { - let Some(v4) = &self.v4 else { return Ok(()); }; - match v4.call(&mut NullHost, HostRequest::DropInteractive { command_id }, &mut |_| {})? { + let Some(v4) = &self.v4 else { + return Ok(()); + }; + match v4.call( + &mut NullHost, + HostRequest::DropInteractive { command_id }, + &mut |_| {}, + )? { HostResponse::Bool(true) => Ok(()), other => Err(PluginError::UnexpectedResponse(Box::new(other))), } @@ -886,10 +901,7 @@ fn format_exit_status(status: Option) -> String { /// Describe why a runner failed before/during connection, including any stderr /// it produced. -fn spawn_failure_status( - child: &Mutex>, - last_stderr: &Mutex, -) -> String { +fn spawn_failure_status(child: &Mutex>, last_stderr: &Mutex) -> String { let mut parts = vec![format_exit_status(child_status(child))]; if let Ok(line) = last_stderr.lock() { if !line.is_empty() { @@ -1259,7 +1271,10 @@ mod timeout_tests { fn push_undo(&mut self, _label: &str) {} fn set_dirty(&mut self) {} fn push_info(&mut self, msg: &str) { - self.push_info_messages.lock().unwrap().push(msg.to_string()); + self.push_info_messages + .lock() + .unwrap() + .push(msg.to_string()); } fn push_output(&mut self, _msg: &str) {} fn push_error(&mut self, _msg: &str) {} @@ -1433,7 +1448,9 @@ mod timeout_tests { ) .expect("send nested request"); let resp = recv::(&mut peer).expect("read nested response"); - assert!(matches!(resp, HostToPlugin::Response(ref r) if matches!(**r, PluginResponse::Ok))); + assert!( + matches!(resp, HostToPlugin::Response(ref r) if matches!(**r, PluginResponse::Ok)) + ); send(&mut peer, &PluginToHost::Response(HostResponse::Bool(true))) .expect("send final response"); }); @@ -1443,7 +1460,11 @@ mod timeout_tests { assert!(result.expect("dispatch succeeds")); assert!(process.is_alive(), "process should still be alive"); let infos = host.take_push_info(); - assert_eq!(infos, vec!["hello".to_string()], "push_info should be delivered to host"); + assert_eq!( + infos, + vec!["hello".to_string()], + "push_info should be delivered to host" + ); // Clean up the helper child so it does not outlive the test. if let Some(mut child) = process @@ -1482,7 +1503,10 @@ mod timeout_tests { RunnerHandshake::Token("correct-token".to_string()), "correct-token", ); - assert!(result.is_ok(), "expected authentication success, got {result:?}"); + assert!( + result.is_ok(), + "expected authentication success, got {result:?}" + ); assert!(!result.unwrap(), "V3 token should select V3 path"); } @@ -1497,7 +1521,10 @@ mod timeout_tests { }, "correct-token", ); - assert!(result.is_ok(), "expected authentication success, got {result:?}"); + assert!( + result.is_ok(), + "expected authentication success, got {result:?}" + ); assert!(result.unwrap(), "TokenV4 should select V4 path"); } diff --git a/crates/ocs_plugin_api/src/process/manager.rs b/crates/ocs_plugin_api/src/process/manager.rs index 90ac264d7..8d1578c9f 100644 --- a/crates/ocs_plugin_api/src/process/manager.rs +++ b/crates/ocs_plugin_api/src/process/manager.rs @@ -97,11 +97,7 @@ impl PluginManager { /// Fan out a host notification to every alive V4 plugin. Per-process errors /// are logged; this is best-effort. - pub fn broadcast_notification( - &self, - command_id: Option, - notification: HostNotification, - ) { + pub fn broadcast_notification(&self, command_id: Option, notification: HostNotification) { for p in &self.plugins { if !p.process.is_alive() { continue; diff --git a/crates/ocs_plugin_api/src/process/v4.rs b/crates/ocs_plugin_api/src/process/v4.rs index f7e46d02e..868bd2288 100644 --- a/crates/ocs_plugin_api/src/process/v4.rs +++ b/crates/ocs_plugin_api/src/process/v4.rs @@ -145,12 +145,7 @@ impl V4Connection { payload, }) => { if tab_id.is_none_or(|request_tab| request_tab == host.tab_id()) { - self.respond_to_plugin_request( - host, - rid, - payload, - on_start_interactive, - )?; + self.respond_to_plugin_request(host, rid, payload, on_start_interactive)?; } else { self.deferred .lock() @@ -167,9 +162,7 @@ impl V4Connection { } Err(mpsc::RecvTimeoutError::Disconnected) => { self.shared.alive.store(false, Ordering::SeqCst); - return Err(PluginError::Runner( - "V4 reader thread disconnected".into(), - )); + return Err(PluginError::Runner("V4 reader thread disconnected".into())); } } } @@ -217,12 +210,7 @@ impl V4Connection { payload, }) => { if tab_id.is_none_or(|request_tab| request_tab == current_tab_id) { - self.respond_to_plugin_request( - host, - id, - payload, - on_start_interactive, - )?; + self.respond_to_plugin_request(host, id, payload, on_start_interactive)?; } else { self.deferred .lock() @@ -238,9 +226,7 @@ impl V4Connection { Err(mpsc::TryRecvError::Empty) => return Ok(()), Err(mpsc::TryRecvError::Disconnected) => { self.shared.alive.store(false, Ordering::SeqCst); - return Err(PluginError::Runner( - "V4 reader thread disconnected".into(), - )); + return Err(PluginError::Runner("V4 reader thread disconnected".into())); } } } @@ -324,7 +310,11 @@ impl V4Connection { while self.shared.alive.load(Ordering::SeqCst) && Instant::now() < deadline { std::thread::sleep(Duration::from_millis(10)); } - let _ = self.reader_handle.lock().unwrap_or_else(|e| e.into_inner()).take(); + let _ = self + .reader_handle + .lock() + .unwrap_or_else(|e| e.into_inner()) + .take(); } } @@ -442,20 +432,23 @@ mod tests { label: &str, entities: Vec, ) -> Result<(), String> { - self.transactions.lock().unwrap().push((label.to_owned(), entities)); + self.transactions + .lock() + .unwrap() + .push((label.to_owned(), entities)); Ok(()) } fn set_dirty(&mut self) {} fn push_info(&mut self, msg: &str) { - self.push_info_messages.lock().unwrap().push(msg.to_string()); + self.push_info_messages + .lock() + .unwrap() + .push(msg.to_string()); } fn push_output(&mut self, _msg: &str) {} fn push_error(&mut self, _msg: &str) {} fn start_interactive(&mut self, _command: Box) {} - fn plugin_state_any( - &self, - _plugin_id: &str, - ) -> Option<&(dyn std::any::Any + Send + Sync)> { + fn plugin_state_any(&self, _plugin_id: &str) -> Option<&(dyn std::any::Any + Send + Sync)> { None } fn plugin_state_any_mut( @@ -537,7 +530,10 @@ mod tests { // Read the host's V4 request and respond. let req = recv::(&mut runner_stream).unwrap(); match req { - HostToPluginV4::Request { id, payload: HostRequest::Dispatch { cmd } } => { + HostToPluginV4::Request { + id, + payload: HostRequest::Dispatch { cmd }, + } => { assert_eq!(cmd, "HELLO"); send( &mut runner_stream, @@ -554,7 +550,13 @@ mod tests { let mut host = DummyHost::new(); let resp = conn - .call(&mut host, HostRequest::Dispatch { cmd: "HELLO".to_string() }, &mut |_| {}) + .call( + &mut host, + HostRequest::Dispatch { + cmd: "HELLO".to_string(), + }, + &mut |_| {}, + ) .unwrap(); assert!(matches!(resp, HostResponse::Bool(true))); runner.join().unwrap(); @@ -573,7 +575,10 @@ mod tests { let runner = thread::spawn(move || { let req = recv::(&mut runner_stream).unwrap(); match req { - HostToPluginV4::Request { id, payload: HostRequest::Dispatch { .. } } => { + HostToPluginV4::Request { + id, + payload: HostRequest::Dispatch { .. }, + } => { // Send a nested plugin request. send( &mut runner_stream, @@ -587,8 +592,10 @@ mod tests { // Read the response to the nested request. let resp = recv::(&mut runner_stream).unwrap(); match resp { - HostToPluginV4::Response { id: rid, payload: PluginResponse::Ok } - if rid == 99 => {} + HostToPluginV4::Response { + id: rid, + payload: PluginResponse::Ok, + } if rid == 99 => {} other => panic!("unexpected nested response: {other:?}"), } // Now respond to the original dispatch. @@ -607,11 +614,21 @@ mod tests { let mut host = DummyHost::new(); let resp = conn - .call(&mut host, HostRequest::Dispatch { cmd: "NESTED".to_string() }, &mut |_| {}) + .call( + &mut host, + HostRequest::Dispatch { + cmd: "NESTED".to_string(), + }, + &mut |_| {}, + ) .unwrap(); assert!(matches!(resp, HostResponse::Bool(true))); let infos = host.take_push_info(); - assert_eq!(infos, vec!["nested".to_string()], "push_info should be delivered to host"); + assert_eq!( + infos, + vec!["nested".to_string()], + "push_info should be delivered to host" + ); runner.join().unwrap(); restore_test_env(); } @@ -632,29 +649,56 @@ mod tests { let runner = thread::spawn(move || { let req = recv::(&mut runner_stream).unwrap(); - let HostToPluginV4::Request { id, payload: HostRequest::Dispatch { .. } } = req else { + let HostToPluginV4::Request { + id, + payload: HostRequest::Dispatch { .. }, + } = req + else { panic!("unexpected host request: {req:?}"); }; - send(&mut runner_stream, &PluginToHostV4::Request { - id: 99, - tab_id: Some(0), - payload: PluginRequest::UpdateEntitiesTransaction { - label: "Move point".into(), - entities: vec![sent], + send( + &mut runner_stream, + &PluginToHostV4::Request { + id: 99, + tab_id: Some(0), + payload: PluginRequest::UpdateEntitiesTransaction { + label: "Move point".into(), + entities: vec![sent], + }, }, - }).unwrap(); - assert!(matches!(recv::(&mut runner_stream).unwrap(), - HostToPluginV4::Response { id: 99, payload: PluginResponse::EntityTransactionResult(Ok(())) })); - send(&mut runner_stream, &PluginToHostV4::Response { - id, - payload: HostResponse::Bool(true), - }).unwrap(); + ) + .unwrap(); + assert!(matches!( + recv::(&mut runner_stream).unwrap(), + HostToPluginV4::Response { + id: 99, + payload: PluginResponse::EntityTransactionResult(Ok(())) + } + )); + send( + &mut runner_stream, + &PluginToHostV4::Response { + id, + payload: HostResponse::Bool(true), + }, + ) + .unwrap(); }); let mut host = DummyHost::new(); - assert!(matches!(conn.call(&mut host, HostRequest::Dispatch { cmd: "MOVE".into() }, &mut |_| {}).unwrap(), - HostResponse::Bool(true))); - assert_eq!(host.take_transactions(), vec![("Move point".into(), vec![expected])]); + assert!(matches!( + conn.call( + &mut host, + HostRequest::Dispatch { cmd: "MOVE".into() }, + &mut |_| {} + ) + .unwrap(), + HostResponse::Bool(true) + )); + assert_eq!( + host.take_transactions(), + vec![("Move point".into(), vec![expected])] + ); runner.join().unwrap(); restore_test_env(); } @@ -735,7 +779,11 @@ mod tests { std::thread::sleep(Duration::from_millis(200)); let got = received.lock().unwrap().clone(); - assert_eq!(got.len(), 1, "second notification should survive handler panic"); + assert_eq!( + got.len(), + 1, + "second notification should survive handler panic" + ); assert_eq!(got[0].0, Some(1)); runner.join().unwrap(); drop(conn); @@ -783,7 +831,10 @@ mod tests { count <= 3, "rate limiter should drop most notifications, got {count}" ); - assert!(conn.is_alive(), "connection should survive rate-limited flood"); + assert!( + conn.is_alive(), + "connection should survive rate-limited flood" + ); runner.join().unwrap(); drop(conn); std::env::remove_var("OCS_PLUGIN_NOTIFY_RATE_LIMIT"); @@ -816,7 +867,10 @@ mod tests { assert_eq!(command_id, 1); assert_eq!(source, CommandSource::Editor); assert_eq!(code, "1+1"); - assert_eq!(tab_index, 0, "ExecuteCode should be tied to the host's tab index"); + assert_eq!( + tab_index, 0, + "ExecuteCode should be tied to the host's tab index" + ); thread::sleep(Duration::from_millis(100)); send( &mut runner_stream, diff --git a/crates/ocs_plugin_api/src/ribbon/owned.rs b/crates/ocs_plugin_api/src/ribbon/owned.rs index 239d94026..4e055a7ce 100644 --- a/crates/ocs_plugin_api/src/ribbon/owned.rs +++ b/crates/ocs_plugin_api/src/ribbon/owned.rs @@ -110,7 +110,9 @@ fn collect_item_command_ids(item: &OwnedRibbonItem, out: &mut Vec) { } } OwnedRibbonItem::PropertiesGroup { match_prop } => push_tool_command(match_prop, out), - OwnedRibbonItem::StyleComboGroup { rows, manager_cmd, .. } => { + OwnedRibbonItem::StyleComboGroup { + rows, manager_cmd, .. + } => { for t in rows.iter().flatten() { push_tool_command(t, out); } @@ -214,17 +216,22 @@ impl From for OwnedRibbonItem { .collect(), default: default.to_string(), }, - RibbonItem::LabeledDropdown { id, label, icon, items, default } => { - OwnedRibbonItem::LabeledDropdown { - id: id.to_string(), - label: label.to_string(), - icon: icon.into(), - items: items.into_iter().map(|(a, b, i)| { - (a.to_string(), b.to_string(), i.into()) - }).collect(), - default: default.to_string(), - } - } + RibbonItem::LabeledDropdown { + id, + label, + icon, + items, + default, + } => OwnedRibbonItem::LabeledDropdown { + id: id.to_string(), + label: label.to_string(), + icon: icon.into(), + items: items + .into_iter() + .map(|(a, b, i)| (a.to_string(), b.to_string(), i.into())) + .collect(), + default: default.to_string(), + }, RibbonItem::LargeDropdown { id, label, @@ -242,9 +249,10 @@ impl From for OwnedRibbonItem { default: default.to_string(), }, RibbonItem::ToolGrid { columns } => OwnedRibbonItem::ToolGrid { - columns: columns.into_iter().map(|column| { - column.into_iter().map(Into::into).collect() - }).collect(), + columns: columns + .into_iter() + .map(|column| column.into_iter().map(Into::into).collect()) + .collect(), }, RibbonItem::LayerComboGroup { row2, row3 } => OwnedRibbonItem::LayerComboGroup { row2: row2.into_iter().map(Into::into).collect(), @@ -297,19 +305,28 @@ impl OwnedRibbonItem { .collect(), default: &*Box::leak(default.into_boxed_str()), }, - OwnedRibbonItem::LabeledDropdown { id, label, icon, items, default } => { - RibbonItem::LabeledDropdown { - id: &*Box::leak(id.into_boxed_str()), - label: &*Box::leak(label.into_boxed_str()), - icon: icon.to_static(), - items: items.into_iter().map(|(a, b, i)| ( - &*Box::leak(a.into_boxed_str()), - &*Box::leak(b.into_boxed_str()), - i.to_static(), - )).collect(), - default: &*Box::leak(default.into_boxed_str()), - } - } + OwnedRibbonItem::LabeledDropdown { + id, + label, + icon, + items, + default, + } => RibbonItem::LabeledDropdown { + id: &*Box::leak(id.into_boxed_str()), + label: &*Box::leak(label.into_boxed_str()), + icon: icon.to_static(), + items: items + .into_iter() + .map(|(a, b, i)| { + ( + &*Box::leak(a.into_boxed_str()), + &*Box::leak(b.into_boxed_str()), + i.to_static(), + ) + }) + .collect(), + default: &*Box::leak(default.into_boxed_str()), + }, OwnedRibbonItem::LargeDropdown { id, label, @@ -333,9 +350,10 @@ impl OwnedRibbonItem { default: &*Box::leak(default.into_boxed_str()), }, OwnedRibbonItem::ToolGrid { columns } => RibbonItem::ToolGrid { - columns: columns.into_iter().map(|column| { - column.into_iter().map(OwnedToolDef::to_static).collect() - }).collect(), + columns: columns + .into_iter() + .map(|column| column.into_iter().map(OwnedToolDef::to_static).collect()) + .collect(), }, OwnedRibbonItem::LayerComboGroup { row2, row3 } => RibbonItem::LayerComboGroup { row2: row2.into_iter().map(|t| t.to_static()).collect(), @@ -382,17 +400,22 @@ impl From<&RibbonItem> for OwnedRibbonItem { .collect(), default: default.to_string(), }, - RibbonItem::LabeledDropdown { id, label, icon, items, default } => { - OwnedRibbonItem::LabeledDropdown { - id: id.to_string(), - label: label.to_string(), - icon: icon.into(), - items: items.iter().map(|(a, b, i)| { - (a.to_string(), b.to_string(), i.into()) - }).collect(), - default: default.to_string(), - } - } + RibbonItem::LabeledDropdown { + id, + label, + icon, + items, + default, + } => OwnedRibbonItem::LabeledDropdown { + id: id.to_string(), + label: label.to_string(), + icon: icon.into(), + items: items + .iter() + .map(|(a, b, i)| (a.to_string(), b.to_string(), i.into())) + .collect(), + default: default.to_string(), + }, RibbonItem::LargeDropdown { id, label, @@ -410,9 +433,10 @@ impl From<&RibbonItem> for OwnedRibbonItem { default: default.to_string(), }, RibbonItem::ToolGrid { columns } => OwnedRibbonItem::ToolGrid { - columns: columns.iter().map(|column| { - column.iter().map(Into::into).collect() - }).collect(), + columns: columns + .iter() + .map(|column| column.iter().map(Into::into).collect()) + .collect(), }, RibbonItem::LayerComboGroup { row2, row3 } => OwnedRibbonItem::LayerComboGroup { row2: row2.iter().map(Into::into).collect(), @@ -622,7 +646,10 @@ mod tests { }; let ids = group.command_ids(); - assert_eq!(ids, ["SHOW_ALL", "SHOW_SELECTED", "PARALLEL", "PERPENDICULAR"]); + assert_eq!( + ids, + ["SHOW_ALL", "SHOW_SELECTED", "PARALLEL", "PERPENDICULAR"] + ); let round_trip: OwnedRibbonGroup = group.to_static().into(); assert_eq!(round_trip.command_ids(), ids); } diff --git a/crates/ocs_plugin_api/src/runner.rs b/crates/ocs_plugin_api/src/runner.rs index 237ddc982..e7b17c184 100644 --- a/crates/ocs_plugin_api/src/runner.rs +++ b/crates/ocs_plugin_api/src/runner.rs @@ -76,10 +76,8 @@ fn invoke_on_load( interactive: &InteractiveRegistry, ) -> Result<(), Box> { let mut proxy = client.plugin_host_api(0, interactive.clone()); - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - plugin.on_load(&mut proxy) - })) - .map_err(|_| std::io::Error::other("plugin on_load() panicked"))?; + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| plugin.on_load(&mut proxy))) + .map_err(|_| std::io::Error::other("plugin on_load() panicked"))?; Ok(()) } @@ -121,7 +119,9 @@ fn run_v4( match client.recv_runner_frame_timeout(std::time::Duration::from_millis(50)) { Ok(RunnerFrame::Request { id, payload }) => { - if let Some(resp) = handle_host_request_v4(&mut *plugin, interactive, &client, id, payload) { + if let Some(resp) = + handle_host_request_v4(&mut *plugin, interactive, &client, id, payload) + { client.send_response(id, resp)?; } } @@ -283,7 +283,9 @@ fn handle_host_request_v4( HostRequest::GetManifest => { match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| plugin.manifest())) { Ok(m) => Some(HostResponse::Manifest(m.into())), - Err(_) => Some(HostResponse::Error("plugin manifest() panicked".to_string())), + Err(_) => Some(HostResponse::Error( + "plugin manifest() panicked".to_string(), + )), } } HostRequest::GetRibbon => { @@ -348,7 +350,9 @@ fn handle_host_request_v4( } match step { Ok(s) => Some(HostResponse::CommandStep(Box::new(s))), - Err(_) => Some(HostResponse::Error("interactive command panicked".to_string())), + Err(_) => Some(HostResponse::Error( + "interactive command panicked".to_string(), + )), } } HostRequest::DropInteractive { command_id } => { @@ -365,7 +369,9 @@ fn handle_host_request_v4( match result { Some(Ok(s)) => Some(HostResponse::Text(s)), Some(Err(_)) => Some(HostResponse::Error("prompt() panicked".to_string())), - None => Some(HostResponse::Error(format!("unknown interactive command {command_id}"))), + None => Some(HostResponse::Error(format!( + "unknown interactive command {command_id}" + ))), } } HostRequest::NeedsEntityPick { command_id } => { @@ -379,8 +385,12 @@ fn handle_host_request_v4( }; match result { Some(Ok(b)) => Some(HostResponse::Bool(b)), - Some(Err(_)) => Some(HostResponse::Error("needs_object_pick() panicked".to_string())), - None => Some(HostResponse::Error(format!("unknown interactive command {command_id}"))), + Some(Err(_)) => Some(HostResponse::Error( + "needs_object_pick() panicked".to_string(), + )), + None => Some(HostResponse::Error(format!( + "unknown interactive command {command_id}" + ))), } } HostRequest::CursorMove { command_id, pt } => { @@ -407,13 +417,7 @@ fn handle_host_request_v4( let mut proxy = client.plugin_host_api(tab_index, interactive.clone()); let respond = client.execute_code_responder(id); let started = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - plugin.start_execute_code( - &mut proxy, - command_id, - &code, - source, - respond, - ) + plugin.start_execute_code(&mut proxy, command_id, &code, source, respond) })); match started { Ok(true) => None, @@ -435,7 +439,9 @@ fn handle_host_request_v4( } } -unsafe fn load_plugin(path: &Path) -> Result<(u32, Box), Box> { +unsafe fn load_plugin( + path: &Path, +) -> Result<(u32, Box), Box> { let lib = libloading::Library::new(path)?; let version: libloading::Symbol u32> = lib diff --git a/crates/ocs_plugin_api/src/version_info.rs b/crates/ocs_plugin_api/src/version_info.rs index da14db317..27dceb773 100644 --- a/crates/ocs_plugin_api/src/version_info.rs +++ b/crates/ocs_plugin_api/src/version_info.rs @@ -147,14 +147,19 @@ mod tests { value["ocs_plugin_api_version"].as_str(), Some(env!("CARGO_PKG_VERSION")) ); - assert_eq!(value["api_version"].as_i64(), Some(crate::API_VERSION as i64)); + assert_eq!( + value["api_version"].as_i64(), + Some(crate::API_VERSION as i64) + ); assert_eq!( value["api_version_min_supported"].as_i64(), Some(crate::API_VERSION_MIN_SUPPORTED as i64) ); // acadrust_version should have a patch component (e.g. "0.4.0"). - let opencadcodec = value["acadrust_version"].as_str().expect("acadrust_version string"); + let opencadcodec = value["acadrust_version"] + .as_str() + .expect("acadrust_version string"); assert_eq!(opencadcodec.split('.').count(), 3); } @@ -223,10 +228,10 @@ mod tests { assert!(acadrust_source_hash("").is_none()); assert!(acadrust_source_hash("registry+https://crates.io").is_none()); assert!(acadrust_source_hash("git+https://github.com/foo/bar.git#short").is_none()); - assert!( - acadrust_source_hash("git+https://github.com/foo/bar.git#gggggggggggggggggggggggggggggggggggggggg") - .is_none() - ); + assert!(acadrust_source_hash( + "git+https://github.com/foo/bar.git#gggggggggggggggggggggggggggggggggggggggg" + ) + .is_none()); } #[test] diff --git a/crates/ocs_web_worker/Cargo.toml b/crates/ocs_web_worker/Cargo.toml index 6e3acdefe..d643bd257 100644 --- a/crates/ocs_web_worker/Cargo.toml +++ b/crates/ocs_web_worker/Cargo.toml @@ -8,7 +8,7 @@ publish = false crate-type = ["cdylib"] [dependencies] -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "937745d", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8189f83", features = ["serde"] } bincode = "1.3" serde = { version = "1", features = ["derive"] } console_error_panic_hook = "0.1" diff --git a/crates/ocs_web_worker/src/lib.rs b/crates/ocs_web_worker/src/lib.rs index ed8c657a6..a2950d9a1 100644 --- a/crates/ocs_web_worker/src/lib.rs +++ b/crates/ocs_web_worker/src/lib.rs @@ -54,26 +54,24 @@ pub fn parse_document( "dwg" => { if recovery_mode { DwgReader::from_stream_with_options( - Cursor::new(Arc::clone(&bytes)), - DwgReadOptions::failsafe(), - ) - .read_with_stats() + Cursor::new(Arc::clone(&bytes)), + DwgReadOptions::failsafe(), + ) + .read_with_stats() } else { - DwgReader::from_stream(Cursor::new(Arc::clone(&bytes))) - .read_with_stats() + DwgReader::from_stream(Cursor::new(Arc::clone(&bytes))).read_with_stats() } } "dxf" => { if recovery_mode { - DxfReader::from_reader(Cursor::new(Arc::clone(&bytes))) - .and_then(|reader| { - reader - .with_configuration(DxfReaderConfiguration { - failsafe: true, - ..DxfReaderConfiguration::default() - }) - .read_with_stats() - }) + DxfReader::from_reader(Cursor::new(Arc::clone(&bytes))).and_then(|reader| { + reader + .with_configuration(DxfReaderConfiguration { + failsafe: true, + ..DxfReaderConfiguration::default() + }) + .read_with_stats() + }) } else { DxfReader::from_reader(Cursor::new(Arc::clone(&bytes))) .and_then(|reader| reader.read_with_stats()) @@ -96,9 +94,7 @@ pub fn parse_document( }; if !outcome.stats.has_usable_drawing_data() { let error = if recovery_mode { - format!( - "initial read failed: {initial_error}; recovery found no usable drawing data" - ) + format!("initial read failed: {initial_error}; recovery found no usable drawing data") } else { "initial read returned no source drawing records".to_string() }; @@ -134,8 +130,8 @@ pub fn parse_document( ); outcome.stats.recovered_errors = outcome.stats.recovered_errors.saturating_add(1); } - let source_sha256 = report_fingerprint_needed(&outcome.stats) - .then(|| sha256_document_bytes(&bytes)); + let source_sha256 = + report_fingerprint_needed(&outcome.stats).then(|| sha256_document_bytes(&bytes)); report_stage.call1(&JsValue::NULL, &JsValue::from_str("serialize document"))?; let encoded = encode_result(Ok(outcome), source_sha256, false, &bytes)?; report_stage.call1(&JsValue::NULL, &JsValue::from_str("copy output"))?; @@ -213,7 +209,7 @@ fn encode_result( recoverable_parse_error, runtime_fields, )) - .map_err(|error| worker_error(error.to_string(), bytes, true))?; + .map_err(|error| worker_error(error.to_string(), bytes, true))?; Ok(Uint8Array::from(encoded.as_slice())) } diff --git a/docs/automation/README.md b/docs/automation/README.md index 8333fc9eb..c76d22b5d 100644 --- a/docs/automation/README.md +++ b/docs/automation/README.md @@ -227,6 +227,14 @@ unconsumed, and the command closed rather than left armed for a second line. Geo `ocs_capture` defaults to the drawing viewport and a longest edge of 1600 pixels. Set `scope` to `window` for the full interface or change `max_dimension` between 256 and 4096. +With `diff: true`, an unchanged comparison returns metadata only, including +`diff.changed: false`, the hash and resource URI; it does not resend image or +resource content blocks, regardless of `delivery`. The initial baseline (also +after `reset_diff_baseline: true`) still uses the requested image delivery. +Changed captures retain the requested diff mode, including cropped patches. +Use `diff: false` or read the returned resource URI to retrieve the full image +when an unchanged comparison omitted it. + Clients using MCP 2026-07-28 can advertise `io.modelcontextprotocol/tasks`. OCS then returns a standard task handle when an operation is still running and accepts `tasks/get`, `tasks/update`, and `tasks/cancel`. Other clients continue to receive the existing operation status and can read it with `ocs_read`. The source-tree protocol smoke test treats the executable as a black box and uses only the Python standard library. It checks both supported protocol styles, the published schemas, structured errors, and the four-tool surface: diff --git a/docs/automation/mcp_handshake.py b/docs/automation/mcp_handshake.py new file mode 100644 index 000000000..7c91472cd --- /dev/null +++ b/docs/automation/mcp_handshake.py @@ -0,0 +1,90 @@ +"""Bounded stdio regression probe; point this at the packaged launcher on macOS.""" +import json +import os +from pathlib import Path +import queue +import signal +import subprocess +import sys +import tempfile +import threading + + +def probe(command: list[str], timeout: float = 10) -> None: + # A separate process group lets a failed probe clean up a broken launcher + # and any GUI child it incorrectly spawned. Never touch existing editors. + with tempfile.TemporaryFile() as stderr: + process = subprocess.Popen( + [*command, "--mcp"], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr, + text=True, encoding="utf-8", start_new_session=os.name == "posix", + env={**os.environ, "OCS_SKIP_SCHEMA_SYNC": "1"}, + ) + lines = queue.Queue() + + def read_stdout() -> None: + try: + for line in process.stdout: + lines.put(line) + finally: + lines.put(None) + + threading.Thread(target=read_stdout, daemon=True).start() + + def request(request_id: int, method: str, params: dict) -> dict: + process.stdin.write(json.dumps({ + "jsonrpc": "2.0", "id": request_id, + "method": method, "params": params, + }) + "\n") + process.stdin.flush() + # stdin stays open while waiting: a server buffering until EOF + # must fail this test just like the launcher that ignores --mcp. + try: + line = lines.get(timeout=timeout) + except queue.Empty as error: + raise AssertionError(f"Timed out waiting for {method}") from error + assert line is not None, f"stdout closed before {method} responded" + response = json.loads(line) + assert response["jsonrpc"] == "2.0", response + assert response["id"] == request_id, response + assert "error" not in response, response + return response["result"] + + try: + initialized = request(1, "initialize", { + "protocolVersion": "2025-11-25", "capabilities": {}, + "clientInfo": {"name": "packaged-handshake", "version": "1"}, + }) + assert initialized["protocolVersion"] == "2025-11-25", initialized + assert initialized["serverInfo"]["name"] == "OpenCADStudio", initialized + process.stdin.write( + '{"jsonrpc":"2.0","method":"notifications/initialized"}\n' + ) + process.stdin.flush() + tools = request(2, "tools/list", {}) + assert {tool["name"] for tool in tools["tools"]} == { + "ocs_sessions", "ocs_read", "ocs_execute", "ocs_capture", + }, tools + request(3, "ping", {}) + process.stdin.close() + assert process.wait(timeout=timeout) == 0, process.returncode + finally: + if os.name == "posix": + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + elif process.poll() is None: + process.kill() + process.wait(timeout=5) + process.stdin.close() + process.stdout.close() + stderr.seek(0) + diagnostics = stderr.read().decode("utf-8", errors="replace") + if diagnostics: + print(diagnostics, file=sys.stderr, end="") + + +if __name__ == "__main__": + probe([str(Path(sys.argv[1]).resolve())]) + print("MCP initialize, tools/list, ping and EOF shutdown passed") diff --git a/docs/readme/README.ar.md b/docs/readme/README.ar.md index 80464a85a..c8736397a 100644 --- a/docs/readme/README.ar.md +++ b/docs/readme/README.ar.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

شعار Open CAD Studio

@@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage ## اللغات -يمكن لـ Open CAD Studio اتباع لغة النظام أو استخدام إحدى لغات الواجهة التالية البالغ عددها 21 لغة: +يمكن لـ Open CAD Studio اتباع لغة النظام أو استخدام إحدى لغات الواجهة التالية البالغ عددها 22 لغة: -> العربية · البرتغالية البرازيلية · البلغارية · التشيكية · الهولندية · الإنجليزية · الفنلندية · الفرنسية · الألمانية · اليونانية · الهندية · المجرية · الإيطالية · اليابانية · الكورية · البولندية · الروسية · الصينية المبسطة · الإسبانية · الصينية التقليدية · التركية +> العربية · البرتغالية البرازيلية · البلغارية · التشيكية · الهولندية · الإنجليزية · الفنلندية · الفرنسية · الألمانية · اليونانية · الهندية · المجرية · الإيطالية · اليابانية · الكورية · اللاوية · البولندية · الروسية · الصينية المبسطة · الإسبانية · الصينية التقليدية · التركية غيّر اللغة من إعدادات التطبيق. عند اختيار **النظام**، يستخدم إصدار المتصفح أيضاً الإعدادات المحلية المفضلة للمتصفح. diff --git a/docs/readme/README.bg.md b/docs/readme/README.bg.md index ddc65ecb6..e6368e675 100644 --- a/docs/readme/README.bg.md +++ b/docs/readme/README.bg.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Лого на Open CAD Studio

@@ -93,9 +93,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage ## Езици -Open CAD Studio може да следва системния език или да използва един от следните 21 езика на интерфейса: +Open CAD Studio може да следва системния език или да използва един от следните 22 езика на интерфейса: -> Арабски · Бразилски португалски · Български · Чешки · Нидерландски · Английски · Фински · Френски · Немски · Гръцки · Хинди · Унгарски · Италиански · Японски · Корейски · Полски · Руски · Опростен китайски · Испански · Традиционен китайски · Турски +> Арабски · Бразилски португалски · Български · Чешки · Нидерландски · Английски · Фински · Френски · Немски · Гръцки · Хинди · Унгарски · Италиански · Японски · Корейски · Лаоски · Полски · Руски · Опростен китайски · Испански · Традиционен китайски · Турски Променете езика от настройките на приложението. Когато е избрано **Системен език**, браузърната версия също използва предпочитания локал на браузъра. diff --git a/docs/readme/README.cs.md b/docs/readme/README.cs.md index 819c3c212..dbcd095ae 100644 --- a/docs/readme/README.cs.md +++ b/docs/readme/README.cs.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Logo Open CAD Studio

@@ -91,9 +91,9 @@ Aplikace je podepsána ad hoc, ale v současnosti není notářsky ověřena spo ## Jazyky -Open CAD Studio může používat jazyk systému nebo jeden z těchto 21 jazyků rozhraní: +Open CAD Studio může používat jazyk systému nebo jeden z těchto 22 jazyků rozhraní: -> Arabština · Brazilská portugalština · Bulharština · Čeština · Nizozemština · Angličtina · Finština · Francouzština · Němčina · Řečtina · Hindština · Maďarština · Italština · Japonština · Korejština · Polština · Ruština · Zjednodušená čínština · Španělština · Tradiční čínština · Turečtina +> Arabština · Brazilská portugalština · Bulharština · Čeština · Nizozemština · Angličtina · Finština · Francouzština · Němčina · Řečtina · Hindština · Maďarština · Italština · Japonština · Korejština · Laoština · Polština · Ruština · Zjednodušená čínština · Španělština · Tradiční čínština · Turečtina Jazyk změníte v nastavení aplikace. Pokud je vybrána možnost **Systém**, webová verze používá také preferované národní prostředí prohlížeče. diff --git a/docs/readme/README.de.md b/docs/readme/README.de.md index f6c1b1818..c2a51618d 100644 --- a/docs/readme/README.de.md +++ b/docs/readme/README.de.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Open-CAD-Studio-Logo

@@ -91,9 +91,9 @@ Die Anwendung ist ad hoc signiert, wird derzeit aber nicht von Apple notarisiert ## Sprachen -Open CAD Studio kann der Systemsprache folgen oder eine dieser 21 Oberflächensprachen verwenden: +Open CAD Studio kann der Systemsprache folgen oder eine dieser 22 Oberflächensprachen verwenden: -> Arabisch · Brasilianisches Portugiesisch · Bulgarisch · Tschechisch · Niederländisch · Englisch · Finnisch · Französisch · Deutsch · Griechisch · Hindi · Ungarisch · Italienisch · Japanisch · Koreanisch · Polnisch · Russisch · Vereinfachtes Chinesisch · Spanisch · Traditionelles Chinesisch · Türkisch +> Arabisch · Brasilianisches Portugiesisch · Bulgarisch · Tschechisch · Niederländisch · Englisch · Finnisch · Französisch · Deutsch · Griechisch · Hindi · Ungarisch · Italienisch · Japanisch · Koreanisch · Laotisch · Polnisch · Russisch · Vereinfachtes Chinesisch · Spanisch · Traditionelles Chinesisch · Türkisch Die Sprache lässt sich in den Anwendungseinstellungen ändern. Die Browserversion verwendet bei Auswahl von **System** ebenfalls das bevorzugte Gebietsschema des Browsers. diff --git a/docs/readme/README.el.md b/docs/readme/README.el.md index db0381d38..fe52d4123 100644 --- a/docs/readme/README.el.md +++ b/docs/readme/README.el.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Λογότυπο του Open CAD Studio

@@ -93,9 +93,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage ## Γλώσσες -Το Open CAD Studio μπορεί να ακολουθεί τη γλώσσα του συστήματος ή να χρησιμοποιεί μία από τις ακόλουθες 21 γλώσσες περιβάλλοντος: +Το Open CAD Studio μπορεί να ακολουθεί τη γλώσσα του συστήματος ή να χρησιμοποιεί μία από τις ακόλουθες 22 γλώσσες περιβάλλοντος: -> Αραβικά · Πορτογαλικά Βραζιλίας · Βουλγαρικά · Τσεχικά · Ολλανδικά · Αγγλικά · Φινλανδικά · Γαλλικά · Γερμανικά · Ελληνικά · Χίντι · Ουγγρικά · Ιταλικά · Ιαπωνικά · Κορεατικά · Πολωνικά · Ρωσικά · Απλοποιημένα κινεζικά · Ισπανικά · Παραδοσιακά κινεζικά · Τουρκικά +> Αραβικά · Πορτογαλικά Βραζιλίας · Βουλγαρικά · Τσεχικά · Ολλανδικά · Αγγλικά · Φινλανδικά · Γαλλικά · Γερμανικά · Ελληνικά · Χίντι · Ουγγρικά · Ιταλικά · Ιαπωνικά · Κορεατικά · Λαοτινά · Πολωνικά · Ρωσικά · Απλοποιημένα κινεζικά · Ισπανικά · Παραδοσιακά κινεζικά · Τουρκικά Αλλάξτε τη γλώσσα από τις ρυθμίσεις της εφαρμογής. Όταν είναι επιλεγμένη η **Γλώσσα συστήματος**, η διαδικτυακή έκδοση χρησιμοποιεί επίσης την προτιμώμενη γλώσσα του προγράμματος περιήγησης. diff --git a/docs/readme/README.es.md b/docs/readme/README.es.md index f210a2b72..d6b68c5ad 100644 --- a/docs/readme/README.es.md +++ b/docs/readme/README.es.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Logotipo de Open CAD Studio

@@ -91,9 +91,9 @@ La aplicación tiene firma ad hoc, pero actualmente no está notarizada por Appl ## Idiomas -Open CAD Studio puede seguir el idioma del sistema o usar cualquiera de estos 21 idiomas de interfaz: +Open CAD Studio puede seguir el idioma del sistema o usar cualquiera de estos 22 idiomas de interfaz: -> Árabe · Portugués de Brasil · Búlgaro · Checo · Neerlandés · Inglés · Finés · Francés · Alemán · Griego · Hindi · Húngaro · Italiano · Japonés · Coreano · Polaco · Ruso · Chino simplificado · Español · Chino tradicional · Turco +> Árabe · Portugués de Brasil · Búlgaro · Checo · Neerlandés · Inglés · Finés · Francés · Alemán · Griego · Hindi · Húngaro · Italiano · Japonés · Coreano · Lao · Polaco · Ruso · Chino simplificado · Español · Chino tradicional · Turco Cambia el idioma en los ajustes de la aplicación. La versión web también usa la configuración regional preferida del navegador cuando se selecciona **Sistema**. diff --git a/docs/readme/README.fi.md b/docs/readme/README.fi.md index bab3fb45a..5fdefd283 100644 --- a/docs/readme/README.fi.md +++ b/docs/readme/README.fi.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Open CAD Studion logo

@@ -91,9 +91,9 @@ Sovellus on allekirjoitettu ad hoc -allekirjoituksella, mutta Apple ei ole täll ## Kielet -Open CAD Studio voi seurata järjestelmän kieltä tai käyttää jotakin näistä 21 käyttöliittymäkielestä: +Open CAD Studio voi seurata järjestelmän kieltä tai käyttää jotakin näistä 22 käyttöliittymäkielestä: -> Arabia · Brasilianportugali · Bulgaria · Tšekki · Hollanti · Englanti · Suomi · Ranska · Saksa · Kreikka · Hindi · Unkari · Italia · Japani · Korea · Puola · Venäjä · Yksinkertaistettu kiina · Espanja · Perinteinen kiina · Turkki +> Arabia · Brasilianportugali · Bulgaria · Tšekki · Hollanti · Englanti · Suomi · Ranska · Saksa · Kreikka · Hindi · Unkari · Italia · Japani · Korea · Lao · Puola · Venäjä · Yksinkertaistettu kiina · Espanja · Perinteinen kiina · Turkki Vaihda kieli sovelluksen asetuksissa. Selainversio käyttää myös selaimen ensisijaista kielialuetta, kun **Järjestelmä** on valittu. diff --git a/docs/readme/README.fr.md b/docs/readme/README.fr.md index e0085596c..9d2e84925 100644 --- a/docs/readme/README.fr.md +++ b/docs/readme/README.fr.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Logo Open CAD Studio

@@ -91,9 +91,9 @@ L’application est signée de façon ad hoc, mais n’est actuellement pas nota ## Langues -Open CAD Studio peut suivre la langue du système ou utiliser l’une de ces 21 langues d’interface : +Open CAD Studio peut suivre la langue du système ou utiliser l’une de ces 22 langues d’interface : -> Arabe · Portugais du Brésil · Bulgare · Tchèque · Néerlandais · Anglais · Finnois · Français · Allemand · Grec · Hindi · Hongrois · Italien · Japonais · Coréen · Polonais · Russe · Chinois simplifié · Espagnol · Chinois traditionnel · Turc +> Arabe · Portugais du Brésil · Bulgare · Tchèque · Néerlandais · Anglais · Finnois · Français · Allemand · Grec · Hindi · Hongrois · Italien · Japonais · Coréen · Lao · Polonais · Russe · Chinois simplifié · Espagnol · Chinois traditionnel · Turc Changez la langue dans les paramètres de l’application. La version web utilise aussi la langue préférée du navigateur lorsque **Système** est sélectionné. diff --git a/docs/readme/README.hi.md b/docs/readme/README.hi.md index 253cc155e..96d6cadc5 100644 --- a/docs/readme/README.hi.md +++ b/docs/readme/README.hi.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Open CAD Studio लोगो

@@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage ## भाषाएँ -Open CAD Studio सिस्टम भाषा का अनुसरण कर सकता है या इन 21 इंटरफ़ेस भाषाओं में से किसी एक का उपयोग कर सकता है: +Open CAD Studio सिस्टम भाषा का अनुसरण कर सकता है या इन 22 इंटरफ़ेस भाषाओं में से किसी एक का उपयोग कर सकता है: -> अरबी · ब्राज़ीलियाई पुर्तगाली · बुल्गारियाई · चेक · डच · अंग्रेज़ी · फ़िनिश · फ़्रेंच · जर्मन · यूनानी · हिन्दी · हंगेरियन · इतालवी · जापानी · कोरियाई · पोलिश · रूसी · सरलीकृत चीनी · स्पैनिश · पारंपरिक चीनी · तुर्की +> अरबी · ब्राज़ीलियाई पुर्तगाली · बुल्गारियाई · चेक · डच · अंग्रेज़ी · फ़िनिश · फ़्रेंच · जर्मन · यूनानी · हिन्दी · हंगेरियन · इतालवी · जापानी · कोरियाई · लाओ · पोलिश · रूसी · सरलीकृत चीनी · स्पैनिश · पारंपरिक चीनी · तुर्की अनुप्रयोग सेटिंग में भाषा बदलें। **सिस्टम** चुने जाने पर ब्राउज़र संस्करण भी ब्राउज़र की पसंदीदा लोकेल का उपयोग करता है। diff --git a/docs/readme/README.hu.md b/docs/readme/README.hu.md index d2e2d94f0..748014250 100644 --- a/docs/readme/README.hu.md +++ b/docs/readme/README.hu.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Open CAD Studio logó

@@ -91,9 +91,9 @@ Az alkalmazás ad hoc aláírással rendelkezik, de az Apple jelenleg nem hitele ## Nyelvek -Az Open CAD Studio követheti a rendszer nyelvét, vagy használhatja az alábbi 21 felületi nyelv egyikét: +Az Open CAD Studio követheti a rendszer nyelvét, vagy használhatja az alábbi 22 felületi nyelv egyikét: -> Arab · Brazil portugál · Bolgár · Cseh · Holland · Angol · Finn · Francia · Német · Görög · Hindi · Magyar · Olasz · Japán · Koreai · Lengyel · Orosz · Egyszerűsített kínai · Spanyol · Hagyományos kínai · Török +> Arab · Brazil portugál · Bolgár · Cseh · Holland · Angol · Finn · Francia · Német · Görög · Hindi · Magyar · Olasz · Japán · Koreai · Lao · Lengyel · Orosz · Egyszerűsített kínai · Spanyol · Hagyományos kínai · Török A nyelv az alkalmazás beállításaiban módosítható. **Rendszer** választásakor a böngészős változat is a böngésző előnyben részesített területi beállítását használja. diff --git a/docs/readme/README.it.md b/docs/readme/README.it.md index 45721e569..0e86264e1 100644 --- a/docs/readme/README.it.md +++ b/docs/readme/README.it.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Logo di Open CAD Studio

@@ -91,9 +91,9 @@ L’applicazione è firmata ad hoc, ma attualmente non è autenticata da Apple. ## Lingue -Open CAD Studio può seguire la lingua di sistema o usare una di queste 21 lingue dell’interfaccia: +Open CAD Studio può seguire la lingua di sistema o usare una di queste 22 lingue dell’interfaccia: -> Arabo · Portoghese brasiliano · Bulgaro · Ceco · Olandese · Inglese · Finlandese · Francese · Tedesco · Greco · Hindi · Ungherese · Italiano · Giapponese · Coreano · Polacco · Russo · Cinese semplificato · Spagnolo · Cinese tradizionale · Turco +> Arabo · Portoghese brasiliano · Bulgaro · Ceco · Olandese · Inglese · Finlandese · Francese · Tedesco · Greco · Hindi · Ungherese · Italiano · Giapponese · Coreano · Lao · Polacco · Russo · Cinese semplificato · Spagnolo · Cinese tradizionale · Turco Cambia la lingua nelle impostazioni dell’applicazione. La versione browser usa anche la lingua preferita del browser quando è selezionato **Sistema**. diff --git a/docs/readme/README.ja.md b/docs/readme/README.ja.md index bfee5811d..31fd3d66c 100644 --- a/docs/readme/README.ja.md +++ b/docs/readme/README.ja.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Open CAD Studio ロゴ

@@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage ## 言語 -Open CAD Studio はシステム言語に従うか、次の 21 のインターフェース言語を使用できます。 +Open CAD Studio はシステム言語に従うか、次の 22 のインターフェース言語を使用できます。 -> アラビア語 · ブラジルポルトガル語 · ブルガリア語 · チェコ語 · オランダ語 · 英語 · フィンランド語 · フランス語 · ドイツ語 · ギリシャ語 · ヒンディー語 · ハンガリー語 · イタリア語 · 日本語 · 韓国語 · ポーランド語 · ロシア語 · 簡体字中国語 · スペイン語 · 繁体字中国語 · トルコ語 +> アラビア語 · ブラジルポルトガル語 · ブルガリア語 · チェコ語 · オランダ語 · 英語 · フィンランド語 · フランス語 · ドイツ語 · ギリシャ語 · ヒンディー語 · ハンガリー語 · イタリア語 · 日本語 · 韓国語 · ラオ語 · ポーランド語 · ロシア語 · 簡体字中国語 · スペイン語 · 繁体字中国語 · トルコ語 言語はアプリケーション設定から変更できます。**システム** を選ぶと、ブラウザ版もブラウザの優先ロケールを使用します。 diff --git a/docs/readme/README.ko.md b/docs/readme/README.ko.md index e6da6dd7c..70f4ea0c3 100644 --- a/docs/readme/README.ko.md +++ b/docs/readme/README.ko.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Open CAD Studio 로고

@@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage ## 언어 -Open CAD Studio는 시스템 언어를 따르거나 다음 21개 인터페이스 언어 중 하나를 사용할 수 있습니다. +Open CAD Studio는 시스템 언어를 따르거나 다음 22개 인터페이스 언어 중 하나를 사용할 수 있습니다. -> 아랍어 · 브라질 포르투갈어 · 불가리아어 · 체코어 · 네덜란드어 · 영어 · 핀란드어 · 프랑스어 · 독일어 · 그리스어 · 힌디어 · 헝가리어 · 이탈리아어 · 일본어 · 한국어 · 폴란드어 · 러시아어 · 중국어 간체 · 스페인어 · 중국어 번체 · 터키어 +> 아랍어 · 브라질 포르투갈어 · 불가리아어 · 체코어 · 네덜란드어 · 영어 · 핀란드어 · 프랑스어 · 독일어 · 그리스어 · 힌디어 · 헝가리어 · 이탈리아어 · 일본어 · 한국어 · 라오어 · 폴란드어 · 러시아어 · 중국어 간체 · 스페인어 · 중국어 번체 · 터키어 애플리케이션 설정에서 언어를 변경할 수 있습니다. **시스템**을 선택하면 브라우저 버전도 브라우저의 기본 로캘을 사용합니다. diff --git a/docs/readme/README.lo.md b/docs/readme/README.lo.md new file mode 100644 index 000000000..67b4ae587 --- /dev/null +++ b/docs/readme/README.lo.md @@ -0,0 +1,198 @@ +

+ English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية +

+ +

ໂລໂກ້ Open CAD Studio

+ +

Open CAD Studio

+ +

ແອັບພລິເຄຊັນແຕ້ມແບບ 2D ແລະ ສ້າງແບບຈຳລອງ 3D ແບບໂອເພັນຊອດ ສຳລັບເດັສທັອບ ແລະ ເວັບ, ພັດທະນາດ້ວຍ Rust.

+ +

+ ລຸ້ນລ່າສຸດ + ຈຳນວນການດາວໂຫຼດ + ດາວ GitHub + ລິຂະສິດ GPL-3.0 +

+ +

+ ເປີດແອັບເວັບ · + ດາວໂຫຼດແອັບເດັສທັອບ · + ເຂົ້າຮ່ວມການສົນທະນາ +

+ +

ພື້ນທີ່ເຮັດວຽກ Open CAD Studio

+ +## ພາບລວມ + +Open CAD Studio ເປັນແອັບພລິເຄຊັນຂ້າມແພລັດຟອມ ສຳລັບການແຕ້ມແບບທາງເທັກນິກ, ການຈັດໜ້າເລເອົາຕ໌ ແລະ ການສ້າງແບບຈຳລອງວັດຖຸຕັນ. ມັນອ່ານ ແລະ ຂຽນແບບແຕ້ມ DWG ແລະ DXF ໂດຍກົງ, ແລະ ລຸ້ນເດັສທັອບກັບລຸ້ນບຣາວເຊີໃຊ້ແກນການແກ້ໄຂດຽວກັນ. + +ໂຄງການນີ້ກຳລັງຖືກພັດທະນາຢ່າງຕໍ່ເນື່ອງ. ກະລຸນາສຳຮອງແບບແຕ້ມທີ່ສຳຄັນໄວ້ສະເໝີ ແລະ ລາຍງານບັນຫາທີ່ສາມາດເຮັດຊ້ຳໄດ້ຜ່ານ [GitHub Issues](https://github.com/HakanSeven12/OpenCADStudio/issues). + +## ຈຸດເດັ່ນ + +- **ເຮັດວຽກກັບແບບແຕ້ມໂດຍກົງ** — ເປີດ, ແກ້ໄຂ, ກູ້ຄືນ ແລະ ບັນທຶກໄຟລ໌ DWG ແລະ DXF ໂດຍບໍ່ຕ້ອງໃຊ້ບໍລິການແປງໄຟລ໌. +- **ການແຕ້ມ 2D ທີ່ຊັດເຈນ** — ເສັ້ນ, ໂພລີລາຍ, ເສັ້ນໂຄ້ງ, ເສັ້ນສະພລາຍ, ລວດລາຍແຮເງົາ, ສະແນັບວັດຖຸ, ການຕິດຕາມ, ເລເຢີ, ບລັອກ ແລະ ການອ້າງອີງພາຍນອກ. +- **ເຄື່ອງມືເຮັດເອກະສານ** — ຂໍ້ຄວາມ, ຂະໜາດ, ເສັ້ນຊີ້, ຄ່າຄວາມຄາດເຄື່ອນ, ຕາຕະລາງ, ພື້ນທີ່ໂມເດວ, ພື້ນທີ່ເຈ້ຍ, ວິວພອດ ແລະ ຮູບແບບການພລັອດ. +- **ການສ້າງແບບຈຳລອງ 3D ດ້ວຍເຄີເນວ** — ຮູບຊົງຕັນພື້ນຖານ, ດຶງຢືດ (Extrude), ໝູນກວາດ (Revolve), ກວາດຕາມເສັ້ນ (Sweep), ລັອບ (Loft), ການດຳເນີນການ Boolean ແລະ ການແບ່ງພື້ນຜິວວັດຖຸ ACIS. +- **ການສະແດງຜົນດ້ວຍ GPU** — ວິວພອດ 2D ແລະ 3D ທີ່ເລັ່ງດ້ວຍ `wgpu` ພ້ອມກ້ອງແບບອໍໂທກຣາຟິກ ແລະ ແບບເພີສເປັກທີບ. +- **ຂະຫຍາຍການເຮັດວຽກໄດ້** — ປລັກອິນພາຍໃນເຄື່ອງ, ສະຄຣິບຄຳສັ່ງ, ການແປງໄຟລ໌ແບບບໍ່ມີໜ້າຕ່າງ ແລະ API ອັດຕະໂນມັດແບບ JSON ຕໍ່ແຖວ. + +

ແບບຈຳລອງ 3D ໃນ Open CAD Studio

+ +## ການເຮັດວຽກກັບໄຟລ໌ + +| ຮູບແບບ ຫຼື ການເຮັດວຽກ | ການຮອງຮັບ | +| --- | --- | +| DWG | ອ່ານ ແລະ ຂຽນ; ບັນທຶກເປັນລຸ້ນ R14 ຫາ 2018 | +| DXF | ອ່ານ ແລະ ຂຽນ; ບັນທຶກເປັນລຸ້ນ R14 ຫາ 2018 | +| BAK / SV$ | ເປີດໄຟລ໌ສຳຮອງ ແລະ ໄຟລ໌ບັນທຶກອັດຕະໂນມັດ | +| OBJ | ນຳເຂົ້າຮູບຕາໜ່າງຫຼາຍລ່ຽມ | +| LandXML | ນຳເຂົ້າຈຸດສຳຫຼວດ `CgPoint` | +| STL | ສົ່ງອອກຂໍ້ມູນຮູບຕາໜ່າງ 3D | +| STEP AP203 | ສົ່ງອອກຂໍ້ມູນຮູບຕາໜ່າງ 3D | +| PDF | ພລັອດເລເອົາຕ໌ ແລະ ຮູບຊົງທີ່ເລືອກໃນລຸ້ນເດັສທັອບ | +| CSV | ດຶງຂໍ້ມູນຄຸນສົມບັດຂອງວັດຖຸ | +| CTB / STB | ໂຫຼດ ແລະ ແກ້ໄຂຕາຕະລາງຮູບແບບການພລັອດ | + +## ເດັສທັອບ ຫຼື ເວັບ? + +ໃຊ້ [ແອັບເວັບ](https://www.opencadstudio.com) ເພື່ອເລີ່ມໃຊ້ທັນທີໂດຍບໍ່ຕ້ອງຕິດຕັ້ງ. ແບບແຕ້ມຈະຖືກເລືອກຜ່ານບຣາວເຊີ ແລະ ບັນທຶກເປັນໄຟລ໌ດາວໂຫຼດໃນເຄື່ອງ. + +ໃຊ້ແອັບເດັສທັອບສຳລັບການຜູກໄຟລ໌ກັບລະບົບ, ຮູບຕົວຢ່າງໃນຕົວຈັດການໄຟລ໌, ການພິມຂອງລະບົບ, ການສົ່ງອອກ PDF, ປລັກອິນພາຍນອກ, ສະຄຣິບຄຳສັ່ງ ແລະ ການເຮັດວຽກອັດຕະໂນມັດແບບບໍ່ມີໜ້າຕ່າງ. ມີລຸ້ນສຳລັບ Windows, Linux ແລະ macOS ທີ່ໃຊ້ Apple Silicon. + +## ການຕິດຕັ້ງ + +ດາວໂຫຼດແພັກເກດທັງໝົດທີ່ໃໝ່ສຸດຈາກ [ລຸ້ນລ່າສຸດ](https://github.com/HakanSeven12/OpenCADStudio/releases/latest). + +### Windows + +ເລືອກແພັກເກດ x86-64 ທີ່ມີລາຍເຊັນອັນໃດອັນໜຶ່ງ: + +- `OpenCADStudio-*-windows-x86_64-installer.msi` — ຕົວຕິດຕັ້ງທີ່ແນະນຳ ພ້ອມທາງລັດໃນເມນູ Start, ການຜູກໄຟລ໌ DWG/DXF ແລະ ຮູບຕົວຢ່າງແບບແຕ້ມ. +- `OpenCADStudio-*-windows-x86_64-portable.exe` — ແອັບແບບພົກພາທີ່ບໍ່ຕ້ອງຕິດຕັ້ງ. + +### Linux + +ດາວໂຫຼດໄຟລ໌ AppImage x86-64, ເຮັດໃຫ້ມັນສາມາດເອີ້ນໃຊ້ໄດ້ ແລະ ເປີດມັນ: + +```bash +chmod +x OpenCADStudio-*-linux-x86_64.AppImage +./OpenCADStudio-*-linux-x86_64.AppImage +``` + +### macOS + +ແພັກເກດ macOS ທີ່ເຜີຍແຜ່ຮອງຮັບ Apple Silicon: + +1. ດາວໂຫຼດ `OpenCADStudio-*-macos-arm64.dmg`. +2. ເປີດດິສອິມເມດ ແລະ ລາກ `OpenCADStudio.app` ໄປໃສ່ໂຟນເດີ **Applications**. +3. ຖ້າ Gatekeeper ບລັອກການເປີດຄັ້ງທຳອິດ, ໃຫ້ອະນຸມັດແອັບໃນ **System Settings → Privacy & Security**. + +ແອັບມີລາຍເຊັນແບບຊົ່ວຄາວ ແຕ່ປະຈຸບັນຍັງບໍ່ໄດ້ຮັບການຮັບຮອງ (notarize) ຈາກ Apple. + +## ພາສາ + +Open CAD Studio ສາມາດໃຊ້ພາສາຂອງລະບົບ ຫຼື ໃຊ້ພາສາໜ້າຕ່າງໃດໜຶ່ງໃນ 22 ພາສາຕໍ່ໄປນີ້: + +> ອາຣັບ · ປອກຕຸຍການ (ບຣາຊິວ) · ບັນແກເຣຍ · ເຊັກ · ໂຮນລັງ · ອັງກິດ · ຟິນແລນ · ຝຣັ່ງ · ເຢຍລະມັນ · ກຣີກ · ຮິນດີ · ຮັງກາຣີ · ອີຕາລີ · ຍີ່ປຸ່ນ · ເກົາຫຼີ · ລາວ · ໂປແລນ · ຣັດເຊຍ · ຈີນຕົວຫຍໍ້ · ສະເປນ · ຈີນຕົວເຕັມ · ຕວກກີ + +ປ່ຽນພາສາໄດ້ໃນການຕັ້ງຄ່າຂອງແອັບ. ເມື່ອເລືອກ **ລະບົບ**, ລຸ້ນບຣາວເຊີຈະໃຊ້ພາສາທີ່ບຣາວເຊີຕັ້ງໄວ້. + +## ການສ້າງຈາກໂຄດຕົ້ນສະບັບ + +### ເດັສທັອບ + +ສິ່ງທີ່ຕ້ອງມີ: + +- Git +- ຊຸດເຄື່ອງມື Rust ລຸ້ນສະຖຽນລ່າສຸດ +- ໄລບຣາຣີພັດທະນາດ້ານກຣາຟິກ ແລະ ຟອນຂອງແຕ່ລະແພລັດຟອມ + +ໃນ Ubuntu ຫຼື Debian, ໃຫ້ຕິດຕັ້ງສ່ວນປະກອບທີ່ຈຳເປັນ: + +```bash +sudo apt update +sudo apt install libgl1-mesa-dev libx11-dev libxcursor-dev libxi-dev \ + libxrandr-dev libxkbcommon-dev libwayland-dev libfontconfig1-dev \ + libfreetype6-dev +``` + +ຈາກນັ້ນສ້າງໂປຣແກຣມ: + +```bash +git clone https://github.com/HakanSeven12/OpenCADStudio.git +cd OpenCADStudio +cargo build --release --bin OpenCADStudio +``` + +ໄຟລ໌ໂປຣແກຣມຈະຖືກຂຽນໄວ້ທີ່ `target/release/OpenCADStudio` (ໃນ Windows ແມ່ນ `OpenCADStudio.exe`). + +### ເວັບ + +ຕິດຕັ້ງເປົ້າໝາຍ WebAssembly ແລະ ເຄື່ອງມືສ້າງໂປຣແກຣມຄັ້ງດຽວ: + +```bash +rustup target add wasm32-unknown-unknown +cargo install trunk wasm-bindgen-cli +``` + +ເລີ່ມເຊີບເວີພັດທະນາ: + +```bash +trunk serve +``` + +## ການເຮັດວຽກອັດຕະໂນມັດ + +ໂປຣແກຣມເດັສທັອບຮອງຮັບການແປງໄຟລ໌ຄັ້ງດຽວ ແລະ ເຊີບເວີແບບບໍ່ມີໜ້າຕ່າງທີ່ເຮັດວຽກຕໍ່ເນື່ອງ: + +```bash +OpenCADStudio --export input.dwg output.dxf +OpenCADStudio --serve +OpenCADStudio --serve --port 4242 +OpenCADStudio --mcp +``` + +ເຊີບເວີແລກປ່ຽນວັດຖຸ JSON ໜຶ່ງອັນຕໍ່ແຖວຜ່ານ stdin/stdout ຫຼື TCP socket ພາຍໃນເຄື່ອງ. ເບິ່ງ [ຄູ່ມືການເຮັດວຽກອັດຕະໂນມັດ](../automation/README.md). + +## ປລັກອິນ + +ປລັກອິນເດັສທັອບເຮັດວຽກໃນໂປຣເຊດແຍກຕ່າງຫາກ ແລະ ສື່ສານກັບແອັບຫຼັກຜ່ານ API ປລັກອິນທີ່ມີລຸ້ນ. ລຸ້ນບຣາວເຊີບໍ່ໂຫຼດປລັກອິນພາຍໃນເຄື່ອງ. + +- [ສະຖາປັດຕະຍະກຳປລັກອິນ](../plugin-architecture.md) +- [ແມ່ແບບປລັກອິນ](../plugin-template/README.md) +- [ທະບຽນປລັກອິນ](../../plugins/README.md) + +## ເອກະສານໂຄງການ + +- [API ການເຮັດວຽກອັດຕະໂນມັດ](../automation/README.md) +- [ສະຖາປັດຕະຍະກຳປລັກອິນ](../plugin-architecture.md) +- [ຂັ້ນຕອນການແບ່ງພື້ນຜິວ (Tessellation)](../tessellation.md) +- [ນະໂຍບາຍຄວາມປອດໄພ](../../SECURITY.md) + +## ການມີສ່ວນຮ່ວມ + +ຍິນດີຮັບລາຍງານຂໍ້ຜິດພາດ, pull request ທີ່ເນັ້ນຈຸດດຽວ, ການແປພາສາ, ການປັບປຸງເອກະສານ ແລະ ປລັກອິນ. + +- ຄົ້ນຫາໃນ [issues](https://github.com/HakanSeven12/OpenCADStudio/issues) ທີ່ມີຢູ່ກ່ອນເປີດລາຍງານໃໝ່. +- ໃຊ້ [Discussions](https://github.com/HakanSeven12/OpenCADStudio/discussions) ສຳລັບຄຳຖາມ ແລະ ແນວຄິດ. +- ລາຍງານຊ່ອງໂຫວ່ດ້ານຄວາມປອດໄພແບບສ່ວນຕົວ ຕາມ [ນະໂຍບາຍຄວາມປອດໄພ](../../SECURITY.md). + +## ການເຕີບໂຕຂອງໂຄງການ + + + + + + ດາວ ແລະ ການດາວໂຫຼດຂອງ Open CAD Studio + + + +## ສະໜັບສະໜູນໂຄງການ + +ຖ້າ Open CAD Studio ຊ່ວຍວຽກຂອງທ່ານ, ທ່ານສາມາດສະໜັບສະໜູນການພັດທະນາຜ່ານ [GitHub Sponsors](https://github.com/sponsors/HakanSeven12) ຫຼື [Patreon](https://www.patreon.com/HakanSeven12). + +## ລິຂະສິດ + +Open CAD Studio ເຜີຍແຜ່ພາຍໃຕ້ [GNU General Public License v3.0](../../LICENSE). diff --git a/docs/readme/README.nl.md b/docs/readme/README.nl.md index 27b73f256..17aa8cc9b 100644 --- a/docs/readme/README.nl.md +++ b/docs/readme/README.nl.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Logo van Open CAD Studio

@@ -91,9 +91,9 @@ De toepassing is ad hoc ondertekend, maar momenteel niet door Apple genotariseer ## Talen -Open CAD Studio kan de systeemtaal volgen of een van deze 21 interfacetalen gebruiken: +Open CAD Studio kan de systeemtaal volgen of een van deze 22 interfacetalen gebruiken: -> Arabisch · Braziliaans-Portugees · Bulgaars · Tsjechisch · Nederlands · Engels · Fins · Frans · Duits · Grieks · Hindi · Hongaars · Italiaans · Japans · Koreaans · Pools · Russisch · Vereenvoudigd Chinees · Spaans · Traditioneel Chinees · Turks +> Arabisch · Braziliaans-Portugees · Bulgaars · Tsjechisch · Nederlands · Engels · Fins · Frans · Duits · Grieks · Hindi · Hongaars · Italiaans · Japans · Koreaans · Laotiaans · Pools · Russisch · Vereenvoudigd Chinees · Spaans · Traditioneel Chinees · Turks Wijzig de taal in de toepassingsinstellingen. De browserversie gebruikt ook de voorkeurstaal van de browser wanneer **Systeem** is geselecteerd. diff --git a/docs/readme/README.pl.md b/docs/readme/README.pl.md index c41814eb9..3911cd937 100644 --- a/docs/readme/README.pl.md +++ b/docs/readme/README.pl.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Logo Open CAD Studio

@@ -91,9 +91,9 @@ Aplikacja jest podpisana ad hoc, ale obecnie nie jest notaryzowana przez Apple. ## Języki -Open CAD Studio może korzystać z języka systemu lub jednego z 21 języków interfejsu: +Open CAD Studio może korzystać z języka systemu lub jednego z 22 języków interfejsu: -> Arabski · Portugalski brazylijski · Bułgarski · Czeski · Niderlandzki · Angielski · Fiński · Francuski · Niemiecki · Grecki · Hindi · Węgierski · Włoski · Japoński · Koreański · Polski · Rosyjski · Chiński uproszczony · Hiszpański · Chiński tradycyjny · Turecki +> Arabski · Portugalski brazylijski · Bułgarski · Czeski · Niderlandzki · Angielski · Fiński · Francuski · Niemiecki · Grecki · Hindi · Węgierski · Włoski · Japoński · Koreański · Laotański · Polski · Rosyjski · Chiński uproszczony · Hiszpański · Chiński tradycyjny · Turecki Język zmienisz w ustawieniach aplikacji. Gdy wybrano **System**, wersja przeglądarkowa używa także preferowanych ustawień regionalnych przeglądarki. diff --git a/docs/readme/README.pt-BR.md b/docs/readme/README.pt-BR.md index 8950a211f..85e0e89d8 100644 --- a/docs/readme/README.pt-BR.md +++ b/docs/readme/README.pt-BR.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Logotipo do Open CAD Studio

@@ -91,9 +91,9 @@ O aplicativo possui assinatura ad hoc, mas atualmente não é notarizado pela Ap ## Idiomas -Open CAD Studio pode seguir o idioma do sistema ou usar um destes 21 idiomas de interface: +Open CAD Studio pode seguir o idioma do sistema ou usar um destes 22 idiomas de interface: -> Árabe · Português do Brasil · Búlgaro · Tcheco · Holandês · Inglês · Finlandês · Francês · Alemão · Grego · Hindi · Húngaro · Italiano · Japonês · Coreano · Polonês · Russo · Chinês simplificado · Espanhol · Chinês tradicional · Turco +> Árabe · Português do Brasil · Búlgaro · Tcheco · Holandês · Inglês · Finlandês · Francês · Alemão · Grego · Hindi · Húngaro · Italiano · Japonês · Coreano · Laosiano · Polonês · Russo · Chinês simplificado · Espanhol · Chinês tradicional · Turco Altere o idioma nas configurações do aplicativo. A versão web também usa a localidade preferida do navegador quando **Sistema** está selecionado. diff --git a/docs/readme/README.ru.md b/docs/readme/README.ru.md index a60d046c0..00600a2e7 100644 --- a/docs/readme/README.ru.md +++ b/docs/readme/README.ru.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Логотип Open CAD Studio

@@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage ## Языки -Open CAD Studio может следовать языку системы или использовать любой из следующих языков интерфейса (всего 21): +Open CAD Studio может следовать языку системы или использовать любой из следующих языков интерфейса (всего 22): -> Арабский · Бразильский португальский · Болгарский · Чешский · Нидерландский · Английский · Финский · Французский · Немецкий · Греческий · Хинди · Венгерский · Итальянский · Японский · Корейский · Польский · Русский · Упрощённый китайский · Испанский · Традиционный китайский · Турецкий +> Арабский · Бразильский португальский · Болгарский · Чешский · Нидерландский · Английский · Финский · Французский · Немецкий · Греческий · Хинди · Венгерский · Итальянский · Японский · Корейский · Лаосский · Польский · Русский · Упрощённый китайский · Испанский · Традиционный китайский · Турецкий Язык меняется в настройках приложения. При выборе **Система** версия для браузера также использует предпочтительную локаль браузера. diff --git a/docs/readme/README.tr.md b/docs/readme/README.tr.md index 04ee08115..154021f2f 100644 --- a/docs/readme/README.tr.md +++ b/docs/readme/README.tr.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Open CAD Studio logosu

@@ -93,9 +93,9 @@ Uygulama geçici imzaya sahiptir ancak şu anda Apple tarafından noterlenmemiş ## Diller -Open CAD Studio sistem dilini izleyebilir veya şu 21 arayüz dilinden birini kullanabilir: +Open CAD Studio sistem dilini izleyebilir veya şu 22 arayüz dilinden birini kullanabilir: -> Arapça · Brezilya Portekizcesi · Bulgarca · Çekçe · Felemenkçe · İngilizce · Fince · Fransızca · Almanca · Yunanca · Hintçe · Macarca · İtalyanca · Japonca · Korece · Lehçe · Rusça · Basitleştirilmiş Çince · İspanyolca · Geleneksel Çince · Türkçe +> Arapça · Brezilya Portekizcesi · Bulgarca · Çekçe · Felemenkçe · İngilizce · Fince · Fransızca · Almanca · Yunanca · Hintçe · Macarca · İtalyanca · Japonca · Korece · Laoca · Lehçe · Rusça · Basitleştirilmiş Çince · İspanyolca · Geleneksel Çince · Türkçe Dili uygulama ayarlarından değiştirin. **Sistem** seçildiğinde tarayıcı sürümü de tarayıcının tercih ettiği yerel ayarı kullanır. diff --git a/docs/readme/README.zh-CN.md b/docs/readme/README.zh-CN.md index a561344af..c898e90eb 100644 --- a/docs/readme/README.zh-CN.md +++ b/docs/readme/README.zh-CN.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Open CAD Studio 标志

@@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage ## 语言 -Open CAD Studio 可以跟随系统语言,也可以使用以下 21 种界面语言: +Open CAD Studio 可以跟随系统语言,也可以使用以下 22 种界面语言: -> 阿拉伯语 · 巴西葡萄牙语 · 保加利亚语 · 捷克语 · 荷兰语 · 英语 · 芬兰语 · 法语 · 德语 · 希腊语 · 印地语 · 匈牙利语 · 意大利语 · 日语 · 韩语 · 波兰语 · 俄语 · 简体中文 · 西班牙语 · 繁体中文 · 土耳其语 +> 阿拉伯语 · 巴西葡萄牙语 · 保加利亚语 · 捷克语 · 荷兰语 · 英语 · 芬兰语 · 法语 · 德语 · 希腊语 · 印地语 · 匈牙利语 · 意大利语 · 日语 · 韩语 · 老挝语 · 波兰语 · 俄语 · 简体中文 · 西班牙语 · 繁体中文 · 土耳其语 可在应用设置中更改语言。选择**系统**时,浏览器版也会使用浏览器的首选区域设置。 diff --git a/docs/readme/README.zh-TW.md b/docs/readme/README.zh-TW.md index e84a40497..e5f9e0803 100644 --- a/docs/readme/README.zh-TW.md +++ b/docs/readme/README.zh-TW.md @@ -1,5 +1,5 @@

- English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية + English · Български · Português (Brasil) · Čeština · Nederlands · Français · Suomi · Deutsch · Ελληνικά · Magyar · Italiano · 日本語 · 한국어 · ລາວ · Polski · Русский · 简体中文 · Español · 繁體中文 · Türkçe · हिन्दी · العربية

Open CAD Studio 標誌

@@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage ## 語言 -Open CAD Studio 可以跟隨系統語言,也可以使用以下 21 種介面語言: +Open CAD Studio 可以跟隨系統語言,也可以使用以下 22 種介面語言: -> 阿拉伯文 · 巴西葡萄牙文 · 保加利亞文 · 捷克文 · 荷蘭文 · 英文 · 芬蘭文 · 法文 · 德文 · 希臘文 · 北印度文 · 匈牙利文 · 義大利文 · 日文 · 韓文 · 波蘭文 · 俄文 · 簡體中文 · 西班牙文 · 繁體中文 · 土耳其文 +> 阿拉伯文 · 巴西葡萄牙文 · 保加利亞文 · 捷克文 · 荷蘭文 · 英文 · 芬蘭文 · 法文 · 德文 · 希臘文 · 北印度文 · 匈牙利文 · 義大利文 · 日文 · 韓文 · 寮文 · 波蘭文 · 俄文 · 簡體中文 · 西班牙文 · 繁體中文 · 土耳其文 可在應用程式設定中變更語言。選擇**系統**時,瀏覽器版也會使用瀏覽器的偏好地區設定。 diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 5d25a9dc7..6208b835b 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1449,6 +1450,8 @@ common = .user-preferences = تفضيلات المستخدم .zoom = التكبير .reverse-mouse-wheel-zoom-zoomwheel = عكس التكبير بعجلة الفأرة (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = لوحة اللمس: التمرير يحرك العرض، Ctrl+تمرير أو القرص يكبّر + .interface-scale = مقياس الواجهة .zoom-factor = معامل التكبير .how-far-one-wheel-notch-zooms-zoomfactor = مقدار التكبير لكل درجة من العجلة (ZOOMFACTOR). .text-and-dimensions = النص والأبعاد @@ -2518,6 +2521,12 @@ inquiry = .id-specify-point = ID حدد نقطة: .list-no-entities-selected-select-entities-firs = LIST: لم تُحدد كيانات. حدد الكيانات أولًا. .massprop-no-entities-selected-select-entities = MASSPROP: لم تُحدد كيانات. حدد الكيانات أولًا. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ لا يحتوي على جسم مغلق لقياسه. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ الحجم=__ocs_fmt_1__ مركز الثقل=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = عزوم القصور الذاتي X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = جداءات القصور الذاتي XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = أنصاف أقطار الدوران X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = العزوم الرئيسية حول مركز الثقل I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE حدد الكائن المراد قياسه: .measure-specify-segment-length = MEASURE حدد طول المقطع: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM أدخل خيارًا [مسافة/نصف قطر/زاوية/مساحة]: diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 71edc232d..16fff813d 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1436,6 +1437,8 @@ common = .user-preferences = Потребителски предпочитания .zoom = Мащабиране .reverse-mouse-wheel-zoom-zoomwheel = Обръщане на мащабирането с колелцето (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Тъчпад: превъртането мести изгледа, Ctrl+превъртане или щипване мащабира + .interface-scale = Мащаб на интерфейса .zoom-factor = Коефициент на мащабиране .how-far-one-wheel-notch-zooms-zoomfactor = Колко мащабира едно щракване на колелцето (ZOOMFACTOR). .text-and-dimensions = Текст и размери @@ -2505,6 +2508,12 @@ inquiry = .id-specify-point = ID Посочи точка: .list-no-entities-selected-select-entities-firs = LIST: не са избрани обекти. Първо избери обекти. .massprop-no-entities-selected-select-entities = MASSPROP: не са избрани обекти. Първо избери обекти. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ няма затворено тяло за измерване. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ Обем=__ocs_fmt_1__ Център на тежестта=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = Инерционни моменти X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = Центробежни моменти XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = Радиуси на инерция X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = Главни моменти спрямо центъра на тежестта I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE Избери обект за измерване: .measure-specify-segment-length = MEASURE Посочи дължина на сегмент: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Въведи опция [Разстояние/Радиус/Ъгъл/Площ]: diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index 8155edc24..d72ae01f9 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1436,6 +1437,8 @@ common = .user-preferences = Předvolby uživatele .zoom = Zoom .reverse-mouse-wheel-zoom-zoomwheel = Obrátit zoom kolečkem myši (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: posun posouvá pohled, Ctrl+posun nebo sevření zvětšuje + .interface-scale = Měřítko rozhraní .zoom-factor = Faktor zoomu .how-far-one-wheel-notch-zooms-zoomfactor = O kolik přiblíží jeden zub kolečka (ZOOMFACTOR). .text-and-dimensions = Text a kóty @@ -2505,6 +2508,12 @@ inquiry = .id-specify-point = ID Určete bod: .list-no-entities-selected-select-entities-firs = LIST: nebyly vybrány žádné entity. Nejprve vyberte entity. .massprop-no-entities-selected-select-entities = MASSPROP: nebyly vybrány žádné entity. Nejprve vyberte entity. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ nemá žádné uzavřené těleso k měření. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ Objem=__ocs_fmt_1__ Těžiště=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = Momenty setrvačnosti X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = Deviační momenty XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = Poloměry setrvačnosti X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = Hlavní momenty k těžišti I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE Vyberte objekt k měření: .measure-specify-segment-length = MEASURE Zadejte délku segmentu: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Enter možnost [Vzdálenost/Poloměr/Úhel/Area]: diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index 3bccb34cf..8998cd8c7 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1437,6 +1438,8 @@ common = .user-preferences = Benutzereinstellungen .zoom = Zoom .reverse-mouse-wheel-zoom-zoomwheel = Mausradzoom umkehren (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: Scrollen verschiebt, Strg+Scrollen oder Zusammenziehen zoomt + .interface-scale = Oberflächenskalierung .zoom-factor = Zoomfaktor .how-far-one-wheel-notch-zooms-zoomfactor = Wie weit eine Radrastung zoomt (ZOOMFACTOR). .text-and-dimensions = Text und Bemaßung @@ -2506,6 +2509,12 @@ inquiry = .id-specify-point = ID Anzugeben ist: .list-no-entities-selected-select-entities-firs = LIST: keine Objekte ausgewählt. .massprop-no-entities-selected-select-entities = MASSPROP: Keine Objekte ausgewählt. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ hat keinen geschlossenen Körper zum Messen. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ Volumen=__ocs_fmt_1__ Schwerpunkt=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = Trägheitsmomente X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = Deviationsmomente XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = Trägheitsradien X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = Hauptträgheitsmomente um den Schwerpunkt I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE Objekt auswählen, um zu messen: .measure-specify-segment-length = MEASURE Segmentlänge angeben: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Geben Sie eine Option ein [Distance/Radius/Angle/ARea]: diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index cc1479411..9b484c188 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1438,6 +1439,8 @@ common = .user-preferences = Προτιμήσεις χρήστη .zoom = Ζουμ .reverse-mouse-wheel-zoom-zoomwheel = Αντιστροφή ζουμ με τον τροχό (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Επιφάνεια αφής: η κύλιση μετακινεί, Ctrl+κύλιση ή τσίμπημα κάνει ζουμ + .interface-scale = Κλίμακα διεπαφής .zoom-factor = Συντελεστής ζουμ .how-far-one-wheel-notch-zooms-zoomfactor = Πόσο ζουμάρει ένα σκαλί του τροχού (ZOOMFACTOR). .text-and-dimensions = Κείμενο και διαστάσεις @@ -2505,6 +2508,12 @@ inquiry = .id-specify-point = ID Ορίστε σημείο: .list-no-entities-selected-select-entities-firs = LIST: δεν έχουν επιλεγεί οντότητες. Επιλέξτε πρώτα οντότητες. .massprop-no-entities-selected-select-entities = MASSPROP: δεν έχουν επιλεγεί οντότητες. Επιλέξτε πρώτα οντότητες. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ δεν έχει κλειστό σώμα για μέτρηση. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ Όγκος=__ocs_fmt_1__ Κέντρο βάρους=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = Ροπές αδράνειας X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = Γινόμενα αδράνειας XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = Ακτίνες αδράνειας X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = Κύριες ροπές ως προς το κέντρο βάρους I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE Επιλέξτε αντικείμενο προς μέτρηση: .measure-specify-segment-length = MEASURE Ορίστε μήκος τμήματος: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Δώστε επιλογή [Απόσταση/Ακτίνα/Γωνία/Εμβαδόν]: diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 43a004594..773c1ee3d 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1438,6 +1439,8 @@ common = .user-preferences = User Preferences .zoom = Zoom .reverse-mouse-wheel-zoom-zoomwheel = Reverse mouse wheel zoom (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: scroll pans, Ctrl+scroll or pinch zooms + .interface-scale = Interface scale .zoom-factor = Zoom factor .how-far-one-wheel-notch-zooms-zoomfactor = How far one wheel notch zooms (ZOOMFACTOR). .text-and-dimensions = Text and Dimensions @@ -2506,6 +2509,12 @@ inquiry = .id-specify-point = ID Specify point: .list-no-entities-selected-select-entities-firs = LIST: no entities selected. Select entities first. .massprop-no-entities-selected-select-entities = MASSPROP: no entities selected. Select entities first. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ has no closed body to measure. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ Volume=__ocs_fmt_1__ Centroid=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = Moments of inertia X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = Products of inertia XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = Radii of gyration X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = Principal moments about centroid I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE Select object to measure: .measure-specify-segment-length = MEASURE Specify segment length: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Enter an option [Distance/Radius/Angle/ARea]: diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 065b0f532..44a3e286d 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1437,6 +1438,8 @@ common = .user-preferences = Preferencias de usuario .zoom = Zoom .reverse-mouse-wheel-zoom-zoomwheel = Invertir el zoom de la rueda del ratón (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Panel táctil: el desplazamiento encuadra, Ctrl+desplazamiento o pellizco hace zoom + .interface-scale = Escala de la interfaz .zoom-factor = Factor de zoom .how-far-one-wheel-notch-zooms-zoomfactor = Cuánto amplía un paso de la rueda (ZOOMFACTOR). .text-and-dimensions = Texto y acotación @@ -2506,6 +2509,12 @@ inquiry = .id-specify-point = ID Especifique un punto: .list-no-entities-selected-select-entities-firs = LIST: no hay entidades seleccionadas. Seleccione primero las entidades. .massprop-no-entities-selected-select-entities = MASSPROP: no hay entidades seleccionadas. Seleccione primero las entidades. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ no tiene un cuerpo cerrado que medir. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ Volumen=__ocs_fmt_1__ Centroide=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = Momentos de inercia X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = Productos de inercia XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = Radios de giro X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = Momentos principales respecto al centroide I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE Seleccione el objeto que desea medir: .measure-specify-segment-length = MEASURE Especifique la longitud del segmento: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Introduzca una opción [Distancia/Radio/Ángulo/Área]: diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index 152f69c02..5e4c98dc5 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1436,6 +1437,8 @@ common = .user-preferences = Käyttäjäasetukset .zoom = Zoomaus .reverse-mouse-wheel-zoom-zoomwheel = Käännä hiiren rullan zoomaus (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Kosketuslevy: vieritys panoroi, Ctrl+vieritys tai nipistys zoomaa + .interface-scale = Käyttöliittymän skaalaus .zoom-factor = Zoomauskerroin .how-far-one-wheel-notch-zooms-zoomfactor = Kuinka paljon yksi rullan pykälä zoomaa (ZOOMFACTOR). .text-and-dimensions = Teksti ja mitoitus @@ -2505,6 +2508,12 @@ inquiry = .id-specify-point = ID Määritä piste: .list-no-entities-selected-select-entities-firs = LIST: entiteettejä ei ole valittu. Valitse ensin entiteetit. .massprop-no-entities-selected-select-entities = MASSPROP: entiteettejä ei ole valittu. Valitse ensin entiteetit. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ ei sisällä suljettua kappaletta mitattavaksi. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ Tilavuus=__ocs_fmt_1__ Painopiste=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = Hitausmomentit X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = Tulohitausmomentit XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = Hitaussäteet X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = Päähitausmomentit painopisteen suhteen I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE Valitse mitattava kohde: .measure-specify-segment-length = MEASURE Määritä segmentin pituus: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Enter vaihtoehto [etäisyys/säde/kulma/pinta-ala]: diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 0b4092b53..e2fe4e05a 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1437,6 +1438,8 @@ common = .user-preferences = Préférences utilisateur .zoom = Zoom .reverse-mouse-wheel-zoom-zoomwheel = Inverser le zoom à la molette (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Pavé tactile : le défilement déplace, Ctrl+défilement ou pincement zoome + .interface-scale = Échelle de l'interface .zoom-factor = Facteur de zoom .how-far-one-wheel-notch-zooms-zoomfactor = De combien zoome un cran de molette (ZOOMFACTOR). .text-and-dimensions = Texte et cotation @@ -2506,6 +2509,12 @@ inquiry = .id-specify-point = ID Spécifier le point: .list-no-entities-selected-select-entities-firs = LIST : aucune entité sélectionnée. Sélectionnez d'abord les entités. .massprop-no-entities-selected-select-entities = MASSPROP : aucune entité sélectionnée. Sélectionnez d'abord les entités. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ n'a aucun corps fermé à mesurer. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ Volume=__ocs_fmt_1__ Centre de gravité=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = Moments d'inertie X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = Produits d'inertie XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = Rayons de giration X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = Moments principaux au centre de gravité I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE Sélectionnez l'objet à mesurer : .measure-specify-segment-length = MEASURE Spécifier la longueur du segment: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Saisissez une option [Distance/Radius/Angle/ARea] : diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index 6cec5d92b..dd76ed05b 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1425,6 +1426,8 @@ common = .user-preferences = उपयोगकर्ता प्राथमिकताएँ .zoom = ज़ूम .reverse-mouse-wheel-zoom-zoomwheel = माउस व्हील ज़ूम उलटें (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = टचपैड: स्क्रॉल पैन करता है, Ctrl+स्क्रॉल या पिंच ज़ूम करता है + .interface-scale = इंटरफ़ेस स्केल .zoom-factor = ज़ूम गुणक .how-far-one-wheel-notch-zooms-zoomfactor = व्हील का एक पायदान कितना ज़ूम करता है (ZOOMFACTOR)। .text-and-dimensions = पाठ और आयाम @@ -2494,6 +2497,12 @@ inquiry = .id-specify-point = ID निर्दिष्ट बिंदु: .list-no-entities-selected-select-entities-firs = LIST: कोई ऑब्जेक्ट नहीं चुना गया। पहले ऑब्जेक्ट चुनें। .massprop-no-entities-selected-select-entities = MASSPROP: कोई ऑब्जेक्ट नहीं चुना गया। पहले ऑब्जेक्ट चुनें। + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ में मापने के लिए कोई बंद पिंड नहीं है। + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ आयतन=__ocs_fmt_1__ केन्द्रक=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = जड़त्व आघूर्ण X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = जड़त्व गुणनफल XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = घूर्णन त्रिज्याएँ X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = केन्द्रक के सापेक्ष मुख्य आघूर्ण I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE ऑब्जेक्ट को मापने के लिए चुनें: .measure-specify-segment-length = MEASURE सेगमेंट की लंबाई निर्दिष्ट करें: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM विकल्प दर्ज करें [दूरी / त्रिज्या / कोण / क्षेत्रफल]: diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index f695dc778..a73e68a12 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1436,6 +1437,8 @@ common = .user-preferences = Felhasználói beállítások .zoom = Nagyítás .reverse-mouse-wheel-zoom-zoomwheel = Egérgörgős nagyítás megfordítása (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Érintőpad: a görgetés mozgat, a Ctrl+görgetés vagy csippentés nagyít + .interface-scale = Felület mérete .zoom-factor = Nagyítási tényező .how-far-one-wheel-notch-zooms-zoomfactor = Mennyit nagyít egy görgőkattanás (ZOOMFACTOR). .text-and-dimensions = Szöveg és méretezés @@ -2505,6 +2508,12 @@ inquiry = .id-specify-point = ID Pont megadása: .list-no-entities-selected-select-entities-firs = LIST: nincsenek kiválasztva entitások. Először válassza ki az entitásokat. .massprop-no-entities-selected-select-entities = MASSPROP: nincsenek kiválasztva entitások. Először válassza ki az entitásokat. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ nem tartalmaz mérhető zárt testet. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ Térfogat=__ocs_fmt_1__ Súlypont=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = Tehetetlenségi nyomatékok X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = Deviációs nyomatékok XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = Tehetetlenségi sugarak X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = Fő tehetetlenségi nyomatékok a súlypontra I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE Válassza ki a mérendő objektumot: .measure-specify-segment-length = MEASURE Adja meg a szegmens hosszát: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Enter egy opció [Távolság / Sugár / Szög / Terület]: diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index ef970c7fd..f3bbfacec 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1436,6 +1437,8 @@ common = .user-preferences = Preferenze utente .zoom = Zoom .reverse-mouse-wheel-zoom-zoomwheel = Inverti lo zoom con la rotellina (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: lo scorrimento sposta la vista, Ctrl+scorrimento o pizzico esegue lo zoom + .interface-scale = Scala dell'interfaccia .zoom-factor = Fattore di zoom .how-far-one-wheel-notch-zooms-zoomfactor = Di quanto ingrandisce uno scatto della rotellina (ZOOMFACTOR). .text-and-dimensions = Testo e quote @@ -2505,6 +2508,12 @@ inquiry = .id-specify-point = ID Specificare il punto: .list-no-entities-selected-select-entities-firs = LIST: nessuna entità selezionata. Seleziona prima le entità. .massprop-no-entities-selected-select-entities = MASSPROP: nessuna entità selezionata. Seleziona prima le entità. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ non ha un corpo chiuso da misurare. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ Volume=__ocs_fmt_1__ Baricentro=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = Momenti d'inerzia X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = Prodotti d'inerzia XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = Raggi di girazione X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = Momenti principali rispetto al baricentro I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE Selezionare l'oggetto da misurare: .measure-specify-segment-length = MEASURE Specificare la lunghezza del segmento: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Enter un'opzione [Distanza/Raggio/Angolo/ARea]: diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index 3e3769255..d2582cffc 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1427,6 +1428,8 @@ common = .user-preferences = ユーザー設定 .zoom = ズーム .reverse-mouse-wheel-zoom-zoomwheel = マウス ホイールのズーム方向を反転 (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = タッチパッド: スクロールで画面移動、Ctrl+スクロールまたはピンチでズーム + .interface-scale = インターフェースの拡大率 .zoom-factor = ズーム倍率 .how-far-one-wheel-notch-zooms-zoomfactor = ホイール 1 ノッチのズーム量 (ZOOMFACTOR)。 .text-and-dimensions = 文字と寸法 @@ -2496,6 +2499,12 @@ inquiry = .id-specify-point = ID 点を指定: .list-no-entities-selected-select-entities-firs = LIST: 図形が選択されていません。先に選択してください。 .massprop-no-entities-selected-select-entities = MASSPROP: 図形が選択されていません。先に選択してください。 + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ には計測できる閉じたボディがありません。 + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ 体積=__ocs_fmt_1__ 重心=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = 慣性モーメント X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = 慣性乗積 XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = 回転半径 X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = 重心まわりの主慣性モーメント I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE 計測するオブジェクトを選択: .measure-specify-segment-length = MEASURE 区間長を指定: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM オプションを入力 [距離/半径/角度/面積]: diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 81b900df4..7b7dafc9f 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -13,6 +13,7 @@ language = .arabic = العربية .japanese = 日本語 .korean = 한국어 + .lao = ລາວ .czech = Čeština .italian = Italiano .finnish = Suomi @@ -1436,6 +1437,8 @@ common = .user-preferences = 사용자 환경설정 .zoom = 줌 .reverse-mouse-wheel-zoom-zoomwheel = 마우스 휠 줌 방향 반전 (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = 터치패드: 스크롤은 화면 이동, Ctrl+스크롤 또는 핀치는 확대/축소 + .interface-scale = 인터페이스 배율 .zoom-factor = 줌 배율 .how-far-one-wheel-notch-zooms-zoomfactor = 휠 한 칸이 확대하는 정도 (ZOOMFACTOR). .text-and-dimensions = 문자 및 치수 @@ -2505,6 +2508,12 @@ inquiry = .id-specify-point = ID 지점 지정: .list-no-entities-selected-select-entities-firs = LIST: 선택된 엔터티가 없습니다. 먼저 엔터티를 선택하세요. .massprop-no-entities-selected-select-entities = MASSPROP: 선택된 엔터티가 없습니다. 먼저 엔터티를 선택하세요. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ 에 측정할 닫힌 바디가 없습니다. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ 부피=__ocs_fmt_1__ 도심=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = 관성 모멘트 X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = 관성 곱 XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = 회전 반경 X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = 도심에 대한 주 모멘트 I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ .measure-select-object-to-measure = MEASURE 측정할 물체 선택: .measure-specify-segment-length = MEASURE 세그먼트 길이 지정: .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM Enter 옵션 [거리/반경/각도/지역]: diff --git a/locales/lo-LA/opencadstudio.ftl b/locales/lo-LA/opencadstudio.ftl new file mode 100644 index 000000000..ece866d9a --- /dev/null +++ b/locales/lo-LA/opencadstudio.ftl @@ -0,0 +1,6199 @@ +language = + .system = System language + .english = English + .turkish = Türkçe + .dutch = Nederlands + .french = Français + .german = Deutsch + .hindi = हिन्दी + .russian = Русский + .chinese-simplified = Simplified Chinese + .spanish = Español + .portuguese = Português (Brasil) + .arabic = العربية + .japanese = 日本語 + .korean = 한국어 + .lao = ລາວ + .czech = Čeština + .italian = Italiano + .finnish = Suomi + .hungarian = Magyar + .polish = Polski + .chinese-traditional = 繁體中文 + .bulgarian = Български + .greek = Ελληνικά + +action = + .close = ປິດ + .options = ຕົວເລືອກ + .plugins = ປລັກອິນ + +ribbon-tab = + .draw = ແຕ້ມ + .parametric = ພາຣາມີຕຣິກ + .annotate = ຄຳອະທິບາຍ + .insert = ແຊກ + .model = ໂມເດວ + .layout = ເລເອົາຕ໌ + .manage = ຈັດການ + .view = ມຸມມອງ + +options = + .language-section = ພາສາ + .language-label = ພາສາສ່ວນຕິດຕໍ່ຜູ້ໃຊ້: + .open-save-section = ເປີດ ແລະ ບັນທຶກ + .default-save-format-label = ຮູບແບບບັນທຶກເລີ່ມຕົ້ນ: + .default-save-format-help = ໃຊ້ສຳລັບການບັນທຶກຮູບແຕ້ມໃໝ່ຄັ້ງທຳອິດ. ຮູບແຕ້ມທີ່ມີຢູ່ແລ້ວຈະຮັກສາປະເພດໄຟລ໌ ແລະ ເວີຊັນເດີມໄວ້. + .theme-section = ຮູບແບບສີ + .theme-label = ຮູບແບບສີຂອງແອັບພລິເຄຊັນ: + .theme-help = ການປ່ຽນສີພື້ນຖານຈະສະຫຼັບໄປເປັນ ກຳນົດເອງ. ຮູບແບບສີຈະສ້າງລະດັບສີຂອງທຸກອົງປະກອບຈາກຫົກສີນີ້. + .color-background = ພື້ນຫຼັງ + .color-text = ຂໍ້ຄວາມ + .color-primary = ຫຼັກ + .color-success = ສຳເລັດ + .color-warning = ແຈ້ງເຕືອນ + .color-danger = ອັນຕະລາຍ + .browser-storage = ບ່ອນເກັບຂໍ້ມູນບຣາວເຊີ + .dark = ມືດ + .default-application = ແອັບພລິເຄຊັນເລີ່ມຕົ້ນ + .iced-theme = ຮູບແບບສີ Iced: + .theme = ຮູບແບບສີ + .right-click-customization = ການປັບແຕ່ງການຄລິກຂວາ + .right-click-in-drawing-area = ຄລິກຂວາໃນພື້ນທີ່ແຕ້ມ + .hold-duration = ໄລຍະເວລາກົດຄ້າງ + .right-click-mode-help = ເມນູລັດ: ຄລິກຂວາຈະເປີດເມນູສະເໝີ. ຂຶ້ນກັບເວລາ: ຄລິກໄວເທົ່າກັບ Enter, ກົດຄ້າງດົນກວ່າຈະເປີດເມນູ (SHORTCUTMENUDURATION). + .right-click-mode-shortcut-menu = ເມນູລັດ + .right-click-mode-time-sensitive = ຂຶ້ນກັບເວລາ (ຄລິກໄວ = Enter) + .right-click-mode-enter-first = Enter ກ່ອນ, ຄລິກຄັ້ງທີສອງ = ເມນູ + +command-line = + .ready = Open CAD Studio ພ້ອມໃຊ້ງານ. + .hint = ພິມຄຳສັ່ງ ຫຼື ໃຊ້ຣິບບອນ. ເປີດ OBJ ຈາກແຖບ ແຊກ. + .label = ຄຳສັ່ງ: + .literal-spaces = ຍະຫວ່າງຕາມຕົວອັກສອນ: ປຸ່ມຍະຫວ່າງຈະຢູ່ໃນແຖວແທນທີ່ຈະເປັນການສັ່ງເຮັດວຽກ. ຈະເປີດຢູ່ຈົນກວ່າຈະປິດ. + .invalid = ບໍ່ຖືກຕ້ອງ + .unable-to-save-file-is-in-use = ບໍ່ສາມາດບັນທຶກໄດ້: ໄຟລ໌ກຳລັງຖືກໃຊ້ງານຢູ່. + +start = + .new-drawing = ຮູບແຕ້ມໃໝ່ + .open-file = ເປີດໄຟລ໌… + .donate = ບໍລິຈາກ + .send-feedback = ສົ່ງຄຳຕິຊົມ + .sponsors = ຜູ້ສະໜັບສະໜູນ + .tutorials = ບົດຮຽນ + .loading-videos = ກຳລັງໂຫຼດວິດີໂອ… + .videos-online = ວິດີໂອໂຫຼດຈາກອິນເຕີເນັດ. + .open-playlist = ເປີດລາຍການຫຼິ້ນໃນ YouTube + .discussions = ການສົນທະນາ + .pinned = ປັກໝຸດແລ້ວ + .loading-discussions = ກຳລັງໂຫຼດການສົນທະນາ… + .discussions-online = ການສົນທະນາໂຫຼດມາຈາກ GitHub. + .open-discussions = ເປີດການສົນທະນາໃນ GitHub + .supporters = ຜູ້ສະໜັບສະໜູນ + .support-on-patreon = ສະໜັບສະໜູນໃນ Patreon + .recent-files = ໄຟລ໌ຫຼ້າສຸດ + .videos = ວິດີໂອ + .welcome = ຍິນດີຕ້ອນຮັບ + .recent-documents = ເອກະສານຫຼ້າສຸດ + .no-recent-files = ໄຟລ໌ທີ່ທ່ານເປີດຈະສະແດງຢູ່ນີ້. + .browser-storage = ບ່ອນເກັບຂໍ້ມູນບຣາວເຊີ + .keep-recent-files = ຮັກສາໄຟລ໌ຫຼ້າສຸດໄວ້ + +donation = + .title = ຄຳຮ້ອງຂໍການບໍລິຈາກ + .heading = ບໍລິຈາກໃຫ້ Open CAD Studio + .body = Open CAD Studio ຕ້ອງການຄວາມຊ່ວຍເຫຼືອຈາກທ່ານ! ເງິນບໍລິຈາກຈະນຳໄປສະໜັບສະໜູນການພັດທະນາ ແລະ ພື້ນຖານໂຄງລ່າງທີ່ສຳຄັນຕໍ່ການດຳເນີນງານຕໍ່ເນື່ອງຂອງ Open CAD Studio. + .decline = ບໍ່, ຂອບໃຈ + +modal = + .about = ກ່ຽວກັບ + .keyboard-shortcuts = ປຸ່ມລັດແປ້ນພິມ + .command-aliases = ຊື່ຫຍໍ້ຄຳສັ່ງ + .find-replace = ຊອກຫາ ແລະ ແທນບ່ອນ + .plugin-manager = ຕົວຈັດການປລັກອິນ + .update-available = ມີເວີຊັນອັບເດດໃໝ່ + .layer-manager = ຕົວຈັດການເລເຢີ + .layer-state-manager = ຕົວຈັດການສະຖານະເລເຢີ + .edit-layer-state = ແກ້ໄຂສະຖານະເລເຢີ + .plot = ພລັອດ + .layout-manager = ຕົວຈັດການເລເອົາຕ໌ + .scale-manager = ຕົວຈັດການມາດຕາສ່ວນ + .annotation-object-scale = ມາດຕາສ່ວນວັດຖຸຄຳອະທິບາຍ + .plot-style-editor = ຕົວແກ້ໄຂຮູບແບບການພລັອດ + .text-style-manager = ຕົວຈັດການຮູບແບບຂໍ້ຄວາມ + .multiline-style-manager = ຕົວຈັດການຮູບແບບເສັ້ນຫຼາຍຊັ້ນ + .table-style-manager = ຕົວຈັດການຮູບແບບຕາຕະລາງ + .multileader-style-manager = ຕົວຈັດການຮູບແບບເສັ້ນຊີ້ຫຼາຍຫົວ + .dimension-style-manager = ຕົວຈັດການຮູບແບບເສັ້ນບອກຂະໜາດ + .default-application = ແອັບພລິເຄຊັນເລີ່ມຕົ້ນ + .save-warning = ແຈ້ງເຕືອນການບັນທຶກ + .unable-save = ບໍ່ສາມາດບັນທຶກຮູບແຕ້ມໄດ້ + .drawing-changed = ຮູບແຕ້ມຖືກປ່ຽນແປງໃນດິສກ໌ + .delete-layer = ລຶບເລເຢີ + .unsaved-changes = ການປ່ຽນແປງທີ່ຍັງບໍ່ໄດ້ບັນທຶກ + .unsaved-save-prompt = ທ່ານຕ້ອງການບັນທຶກການປ່ຽນແປງບໍ່? + .point-style = ຮູບແບບຈຸດ + .attribute-editor = ຕົວແກ້ໄຂຄຸນລັກສະນະ + .save-drawing-as = ບັນທຶກຮູບແຕ້ມເປັນ + .recovery-report = ລາຍງານການກູ້ຄືນຮູບແຕ້ມ + .recovery-prompt = ການກູ້ຄືນຮູບແຕ້ມ + .file-name-could-not-be-saved = ບໍ່ສາມາດບັນທຶກ "__ocs_arg_file_name__" ໄດ້. + .file-name-was-changed-by-another-application = "__ocs_arg_file_name__" ຖືກປ່ຽນແປງໂດຍແອັບພລິເຄຊັນອື່ນ. + .count-selected-layers = ເລືອກ __ocs_arg_count__ ເລເຢີ + .subject-still-has-count-obj-deleting-will-also = + __ocs_arg_subject__ ຍັງ __ocs_arg_has__ __ocs_arg_count__ __ocs_arg_obj__. + ການລຶບຈະນຳເອົາ __ocs_arg_those__ ອອກຈາກຮູບແຕ້ມນຳເຊັ່ນກັນ. ສືບຕໍ່ບໍ່? + .delete-objects = ລຶບວັດຖຸ + .details-error = ລາຍລະອຽດ: __ocs_arg_error__ + .file-name = ຊື່ໄຟລ໌: + .format = ຮູບແບບ: + .layer-name = ເລເຢີ "__ocs_arg_name__" + .make-open-cad-studio-your-default-cad-app = ຕັ້ງ Open CAD Studio ເປັນແອັບ CAD ເລີ່ມຕົ້ນຂອງທ່ານບໍ່? + .not-now = ບໍ່ແມ່ນຕອນນີ້ + .open-dwg-and-dxf-drawings-in-open-cad-studio-b = ເປີດຮູບແຕ້ມ .dwg ແລະ .dxf ໃນ Open CAD Studio ເປັນຄ່າເລີ່ມຕົ້ນ. ທ່ານສາມາດປ່ຽນແປງສິ່ງນີ້ໄດ້ໃນພາຍຫຼັງໃນການຕັ້ງຄ່າລະບົບຂອງທ່ານ. + .path-path = ເສັ້ນທາງ: __ocs_arg_path__ + .reload-from-disk = ໂຫຼດຄືນໃໝ່ຈາກດິສກ໌ + .save-as = ບັນທຶກເປັນ... + .saving-now-could-destroy-those-external-change = ການບັນທຶກຕອນນີ້ອາດທັບການປ່ຽນແປງຈາກພາຍນອກເຫຼົ່ານັ້ນ. ໂຫຼດສຳເນົາຈາກດິສກ໌ຄືນໃໝ່, ບັນທຶກວຽກຂອງທ່ານໄວ້ບ່ອນອື່ນ, ຫຼື ຂຽນທັບໂດຍກົງ. + .the-file-is-open-or-being-used-by-another-appl = ໄຟລ໌ກຳລັງເປີດຢູ່ ຫຼື ຖືກໃຊ້ງານໂດຍແອັບພລິເຄຊັນອື່ນ. ກະລຸນາປິດມັນຢູ່ບ່ອນນັ້ນແລ້ວລອງໃໝ່, ຫຼື ບັນທຶກຮູບແຕ້ມນີ້ດ້ວຍຊື່ອື່ນ. + .the-selected-layer-state-is-no-longer-availabl = ສະຖານະເລເຢີທີ່ເລືອກບໍ່ມີໃຫ້ໃຊ້ງານອີກຕໍ່ໄປ. + .this-drawing-contains-count-aec-civil-objects = + ຮູບແຕ້ມນີ້ມີ __ocs_arg_count__ ວັດຖຸ AEC/Civil ທີ່ "__ocs_arg_target__" ບໍ່ສາມາດຈັດເກັບໄດ້, ດັ່ງນັ້ນພວກມັນຈະບໍ່ຖືກບັນທຶກ. + ເພື່ອຮັກສາພວກມັນໄວ້, ໃຫ້ບັນທຶກໃນເວີຊັນຕົ້ນສະບັບ (__ocs_arg_src_version__). + .yes-set-as-default = ແມ່ນແລ້ວ, ຕັ້ງເປັນຄ່າເລີ່ມຕົ້ນ + .has = ມີ + .hold = ຖື + .objects = ວັດຖຸ + .that-object = ວັດຖຸນັ້ນ + .those-objects = ວັດຖຸເຫຼົ່ານັ້ນ + +clipboard = + .no-supported-content = ຄລິບບອດບໍ່ມີຂໍ້ຄວາມ ຫຼື ວັດຖຸຮູບແຕ້ມທີ່ຮອງຮັບ. + .unavailable = ຄລິບບອດຂອງລະບົບບໍ່ພ້ອມໃຊ້ງານ. + .occupied = ຄລິບບອດຂອງລະບົບກຳລັງຖືກໃຊ້ງານຢູ່. ກະລຸນາລອງໃໝ່. + .conversion-failed = ບໍ່ສາມາດອ່ານຂໍ້ຄວາມໃນຄລິບບອດໄດ້. + .copybase-specify-base-point = COPYBASE ກຳນົດຈຸດອ້າງອີງ: + .copy-to-clipboard = ຄັດລອກໄປໃສ່ຄລິບບອດ + .cut = ຕັດ + .cut-to-clipboard = ຕັດໄປໃສ່ຄລິບບອດ + .pasteclip-pick-insertion-point = PASTECLIP ເລືອກຈຸດແຊກ: + .paste-as-block = ວາງເປັນບລັອກ + .paste-from-clipboard = ວາງຈາກຄລິບບອດ + .paste-to-original-coordinates = ວາງໃສ່ພິກັດເດີມ + +viewport = + .preparing-layout = ກຳລັງກຽມເລເອົາຕ໌… + .split-vertical = ແບ່ງວິວພອດຕາມລວງຕັ້ງ + .split-horizontal = ແບ່ງວິວພອດຕາມລວງນອນ + .move = ຍ້າຍວິວພອດ + .close = ປິດວິວພອດ + +gpu = + .title = ແຈ້ງເຕືອນກຣາຟິກ + .software-headline = ບໍ່ພົບໄດຣເວີ GPU ທີ່ໃຊ້ງານໄດ້, ດັ່ງນັ້ນຮູບແຕ້ມຈຶ່ງຖືກປະມວນຜົນດ້ວຍຊອບແວເທິງ CPU ({ $adapter }). + .software-consequences = ຮູບແຕ້ມຂະໜາດໃຫຍ່ຈະເລື່ອນຊ້າ, ແລະ ການປະມວນຜົນອາດຈະບໍ່ສົມບູນ ຫຼື ບໍ່ສະຖຽນ. + .no-renderer-headline = ບໍ່ມີຕົວປະມວນຜົນ GPU ໃນລະບົບນີ້, ດັ່ງນັ້ນຈຶ່ງບໍ່ສາມາດສະແດງພື້ນທີ່ແຕ້ມໄດ້. ສ່ວນອື່ນໆຂອງລະບົບຍັງເຮັດວຽກໄດ້ປົກກະຕິ. + .hint-linux = ກວດສອບໄດຣເວີກຣາຟິກ — ສາເຫດທົ່ວໄປແມ່ນການອັບເດດໄດຣເວີທີ່ຍັງບໍ່ໄດ້ຣີສະຕາດເຄື່ອງ. + .hint-windows = ອັບເດດ ຫຼື ຕິດຕັ້ງໄດຣເວີກຣາຟິກໃໝ່. ເຊດຊັນ Remote Desktop ແລະ ເຄື່ອງສະເໝືອນ (Virtual Machine) ອາດຈະບໍ່ຮອງຮັບການເລັ່ງຄວາມໄວ GPU. + .hint-macos = Mac ເຄື່ອງນີ້ບໍ່ໄດ້ເປີດ GPU ໃຫ້ແອັບພລິເຄຊັນໃຊ້ງານ; ເຄື່ອງສະເໝືອນມັກຈະບໍ່ຮອງຮັບ. + .hint-web = ເປີດໃຊ້ງານ hardware acceleration ໃນການຕັ້ງຄ່າບຣາວເຊີ ຫຼື ອັບເດດໄດຣເວີກຣາຟິກ, ແລ້ວໂຫຼດໜ້າເວັບຄືນໃໝ່. + .pill-software = ⚠ ປະມວນຜົນດ້ວຍຊອບແວ + .pill-no-renderer = ⚠ ບໍ່ມີຕົວປະມວນຜົນ GPU + .pill-tip = ຄລິກເພື່ອເບິ່ງລາຍລະອຽດ + .silence = ບໍ່ຕ້ອງສະແດງອີກສຳລັບອຸປະກອນນີ້ + .command-line-no-renderer = ບໍ່ມີຕົວປະມວນຜົນ GPU — ບໍ່ສາມາດສະແດງພື້ນທີ່ແຕ້ມໄດ້. ກວດສອບໄດຣເວີກຣາຟິກ. + +recovery = + .opened-with-repairs = ຮູບແຕ້ມຖືກເປີດພ້ອມລາຍງານການກູ້ຄືນ + .open-failed = ບໍ່ສາມາດເປີດຮູບແຕ້ມໄດ້ + .repaired-description = ພົບຂໍ້ຜິດພາດຂະນະເປີດຮູບແຕ້ມ. ກະລຸນາກວດສອບລາຍງານ; ຖ້າຂໍ້ມູນຮູບແຕ້ມໄດ້ຮັບການສ້ອມແປງແລ້ວ, ໃຫ້ບັນທຶກເປັນໄຟລ໌ໃໝ່. + .failed-description = ການກູ້ຄືນບໍ່ສາມາດສ້າງຂໍ້ມູນຮູບແຕ້ມທີ່ໃຊ້ງານໄດ້. ກະລຸນາກວດສອບລາຍງານ ແລະ ລອງໃຊ້ສຳເນົາສຳຮອງ ຫຼື ໄຟລ໌ບັນທຶກອັດຕະໂນມັດ. + .entities-checked = ອົງປະກອບທີ່ກວດສອບແລ້ວ + .issues-found = ບັນຫາທີ່ພົບ + .entities-removed = ອົງປະກອບທີ່ຖືກລຶບອອກ + .references-checked = ຈຸດອ້າງອີງທີ່ກວດສອບແລ້ວ + .referenced-entities-removed = ອົງປະກອບອ້າງອີງທີ່ຖືກລຶບອອກ + .references-unavailable = ຈຸດອ້າງອີງບໍ່ພ້ອມໃຊ້ງານ + .log-path = ບັນທຶກເຫດການການກູ້ຄືນ + .log-write-failed = ບໍ່ສາມາດຂຽນບັນທຶກເຫດການການກູ້ຄືນໄດ້ + .log-download-ready = ລາຍງານການກູ້ຄືນພ້ອມດາວໂຫຼດແລ້ວ. + .save-copy = ບັນທຶກສຳເນົາທີ່ສ້ອມແປງແລ້ວ + .show-log = ສະແດງບັນທຶກເຫດການການກູ້ຄືນ + .save-new-file-required = ຮູບແຕ້ມທີ່ສ້ອມແປງແລ້ວຕ້ອງບັນທຶກເປັນໄຟລ໌ໃໝ່. + .prompt-heading = ການເປີດແບບປົກກະຕິຫຼົ້ມເຫຼວ + .prompt-description = ຮູບແຕ້ມຍັງບໍ່ໄດ້ຖືກປ່ຽນແປງ. ໂໝດກູ້ຄືນສາມາດພະຍາຍາມກູ້ຂໍ້ມູນທີ່ໃຊ້ງານໄດ້, ແຕ່ລາຍການທີ່ເສຍຫາຍອາດຖືກຂ້າມໄປ. ທ່ານຕ້ອງການສືບຕໍ່ບໍ່? + .attempt = ລອງກູ້ຄືນ + .decline = ຍົກເລີກ + +area = + .option-objects = ວັດຖຸ + .option-back = ກັບຄືນ + .option-add = ເພີ່ມເນື້ອທີ່ + .option-subtract = ລົບເນື້ອທີ່ + .prompt-first = AREA ກຳນົດຈຸດມຸມທຳອິດ ຫຼື [Objects/Add area/Subtract area] : + .prompt-add = AREA ADD ກຳນົດຈຸດມຸມທຳອິດ ຫຼື [Objects/Subtract area] : + .prompt-subtract = AREA SUBTRACT ກຳນົດຈຸດມຸມທຳອິດ ຫຼື [Objects/Add area] : + .prompt-next = AREA ກຳນົດຈຸດຕໍ່ໄປ (ເລືອກແລ້ວ { $count } ຈຸດ, ກົດ Enter ເພື່ອຄິດໄລ່): + .prompt-objects = AREA ເລືອກວັດຖຸ (ເລືອກແລ້ວ { $count } ອັນ, ກົດ Enter ເພື່ອຄິດໄລ່): + .objects-not-measurable = AREA: ບໍ່ມີວັດຖຸໃດທີ່ເລືອກມີເນື້ອທີ່ທີ່ສາມາດວັດແທກໄດ້. + .result = ເນື້ອທີ່ = { $area }, ລວງຮອບ = { $perimeter } + .result-area-only = ເນື້ອທີ່ = { $area } + .running-result = + ເນື້ອທີ່ = { $area }, ລວງຮອບ = { $perimeter } + ເນື້ອທີ່ທັງໝົດ = { $total_area }, ລວງຮອບທັງໝົດ = { $total_perimeter } + .running-result-area-only = + ເນື້ອທີ່ = { $area } + ເນື້ອທີ່ທັງໝົດ = { $total_area } + +command-move = + .base = + MOVE ກຳນົດຈຸດອ້າງອີງ [{ $count -> + [one] { $count } ວັດຖຸ + *[other] { $count } ວັດຖຸ + }]: + .target = MOVE ກຳນົດປາຍທາງ [ຈຸດອ້າງອີງ { $x },{ $y }]: + .displacement = MOVE ກຳນົດໄລຍະການຍ້າຍ: + +command-copy = + .array-count = COPY ປ້ອນຈຳນວນລາຍການທີ່ຈະຈັດຮຽງ: + .base = + COPY ກຳນົດຈຸດອ້າງອີງ [{ $count -> + [one] { $count } ວັດຖຸ + *[other] { $count } ວັດຖຸ + }]: + .array-target = COPY ກຳນົດຈຸດທີສອງສຳລັບການຈັດຮຽງ { $count } ລາຍການ [ຈຸດອ້າງອີງ { $x },{ $y }]: + .target = + COPY ກຳນົດປາຍທາງ [{ $count -> + [one] ຄັດລອກແລ້ວ { $count } ອັນ + *[other] ຄັດລອກແລ້ວ { $count } ອັນ + } | Array | Enter=ສຳເລັດ | ຈຸດອ້າງອີງ { $x },{ $y }]: + .displacement = COPY ກຳນົດໄລຍະການຍ້າຍ: + .mode = COPY ປ້ອນຕົວເລືອກໂໝດການຄັດລອກ [Single/Multiple] : + +common = + .none-use-shortcuts-set-key-command = (ບໍ່ມີ — ໃຊ້: SHORTCUTS SET ) + .alignment-indented-label-title-case = ການຈັດແນວ: + .background-fill-enabled = ເປີດໃຊ້ການຕື່ມສີພື້ນຫຼັງແລ້ວ + .borders-type-weight-color-spacing-hidden = ເສັ້ນຂອບ (ປະເພດ / ຄວາມໜາ / ສີ / ໄລຍະຫ່າງ / ເຊື່ອງ) + .data-type = ປະເພດຂໍ້ມູນ: + .fill-color-indented-label-sentence-case = ສີຕື່ມ: + .format-string = ຮູບແບບຂໍ້ຄວາມ: + .off-color-ltype = ປິດ ສີ ປະເພດເສັ້ນ + .text-color-indented-label-sentence-case = ສີຂໍ້ຄວາມ: + .text-height-indented-label-sentence-case = ຄວາມສູງຂໍ້ຄວາມ: + .unit-type = ປະເພດຫົວໜ່ວຍ: + .name-already-in-use = ຊື່ "__ocs_fmt_0__" ຖືກໃຊ້ງານແລ້ວ + .rrggbb-or-blank = #RRGGBB ຫຼື ຫວ່າງເປົ່າ + .default-parenthesized-lowercase = (ເລີ່ມຕົ້ນ) + .none-parenthesized-lowercase = (ບໍ່ມີ) + .value-1st-arrowhead-dimblk1 = ຫົວລູກສອນທີ 1 (DIMBLK1) + .value-2d-wireframe = ໂຄງລວດ 2D + .value-2nd-arrowhead-dimblk2 = ຫົວລູກສອນທີ 2 (DIMBLK2) + .value-3d-wireframe = ໂຄງລວດ 3D + .value-3dalign-aligned-the-solid-to-the-destination-p = 3DALIGN: ຈັດແນວຮູບຊົງຕັນໄປໃສ່ຈຸດປາຍທາງແລ້ວ. + .value-3dalign-each-point-triple-must-be-non-coincide = 3DALIGN: ແຕ່ລະຊຸດສາມຈຸດຕ້ອງບໍ່ທັບກັນ ແລະ ບໍ່ຢູ່ໃນເສັ້ນຊື່ດຽວກັນ. + .value-3dalign-select-exactly-one-solid-created-this = 3DALIGN: ເລືອກຮູບຊົງຕັນທີ່ສ້າງໃນເຊດຊັນນີ້ພຽງ 1 ອັນ. + .value-3dmirror-added-a-mirror-across-the-plane = 3DMIRROR: ເພີ່ມການສະທ້ອນຂ້າມລະນາບ __ocs_fmt_0__ ແລ້ວ. + .value-3dmirror-could-not-determine-the-solid-s-exten = 3DMIRROR: ບໍ່ສາມາດລະບຸຂອບເຂດຂອງຮູບຊົງຕັນໄດ້. + .value-3dmirror-select-exactly-one-solid-created-this = 3DMIRROR: ເລືອກຮູບຊົງຕັນທີ່ສ້າງໃນເຊດຊັນນີ້ພຽງ 1 ອັນ. + .value-3drotate-could-not-determine-the-solid-s-exten = 3DROTATE: ບໍ່ສາມາດລະບຸຂອບເຂດຂອງຮູບຊົງຕັນໄດ້. + .value-3drotate-rotated-angle-deg-about-the-axis = 3DROTATE: ໝຸນ __ocs_fmt_0__° ອ້ອມແກນ __ocs_fmt_1__ ແລ້ວ. + .value-3drotate-select-exactly-one-solid-created-this = 3DROTATE: ເລືອກຮູບຊົງຕັນທີ່ສ້າງໃນເຊດຊັນນີ້ພຽງ 1 ອັນ. + .align-applied = ALIGN: ນຳໃຊ້ແລ້ວ. + .above = ດ້ານເທິງ + .accept = ຍອມຮັບ + .add = ເພີ່ມ + .add-leader = ເພີ່ມເສັ້ນຊີ້ + .align-space = ຈັດແນວໄລຍະຫ່າງ: + .aligned = ຈັດແນວ + .alignment-title-case = ການຈັດແນວ + .all = ທັງໝົດ + .alternate = ສຳຮອງ + .alternate-units = ຫົວໜ່ວຍສຳຮອງ + .angle = ມຸມ + .angular-decimals-dimadec = ທົດສະນິຍົມມຸມ (DIMADEC) + .angular-unit-dimaunit = ຫົວໜ່ວຍມຸມ (DIMAUNIT) + .angular-zero-supp-dimazin = ຕັດເລກສູນຂອງມຸມ (DIMAZIN) + .annotation = ຄຳອະທິບາຍ + .annotative = ປັບຂະໜາດຕາມມາດຕາສ່ວນໄດ້ + .apparent-intersection = ຈຸດຕັດສະເໝືອນ + .apply = ນຳໃຊ້ + .apply-cell = ນຳໃຊ້ກັບຫ້ອງ + .apply-margins = ນຳໃຊ້ໄລຍະຂອບ + .arc = ເສັ້ນໂຄ້ງ + .arch = ໂຄ້ງ + .architectural = ສະຖາປັດຕະຍະກຳ + .area = ເນື້ອທີ່ + .arrow-size = ຂະໜາດລູກສອນ + .arrowhead = ຫົວລູກສອນ + .arrowhead-size = ຂະໜາດຫົວລູກສອນ + .as-displayed = ຕາມທີ່ສະແດງ + .attachment-direction = ທິດທາງການຕິດ: + .attribute = ຄຸນລັກສະນະ + .attributes = ຄຸນລັກສະນະ + .auto = ອັດຕະໂນມັດ + .bedit-block-tab-name-is-not-open = BEDIT: ແທັບບລັອກ "__ocs_fmt_0__" ຍັງບໍ່ໄດ້ເປີດ. + .break-entity-type-not-supported = BREAK: ບໍ່ຮອງຮັບປະເພດອົງປະກອບນີ້. + .break-fragment-s = BREAK: __ocs_fmt_0__ ສ່ວນຍ່ອຍ. + .back = ກັບຄືນ + .background-color = ສີພື້ນຫຼັງ + .backward = ຖອຍຫຼັງ + .baseline = ເສັ້ນພື້ນຖານ + .below = ດ້ານລຸ່ມ + .block = ບລັອກ + .block-name-created = ສ້າງບລັອກ "__ocs_fmt_0__" ແລ້ວ. + .block-orig-new-name = ບລັອກ "__ocs_fmt_0__" → "__ocs_fmt_1__" + .block-content = ເນື້ອໃນບລັອກ + .block-color = ສີບລັອກ + .block-connection = ການເຊື່ອມຕໍ່ບລັອກ + .block-rotation = ການໝຸນບລັອກ + .blocks = ບລັອກ + .blue = ສີຟ້າ + .bulge = ຄວາມໂກ່ງ + .boolean-failed-the-solids-may-not-overlap = ບູນີນຫຼົ້ມເຫຼວ — ຮູບຊົງຕັນອາດຈະບໍ່ໄດ້ທັບຊ້ອນກັນ. + .boolean-select-exactly-two-solids-created-this = Boolean: ເລືອກຮູບຊົງຕັນທີ່ສ້າງໃນເຊດຊັນນີ້ພຽງ 2 ອັນ. + .bottom = ລຸ່ມ + .bottom-center-title-case = ລຸ່ມກາງ + .bottom-left-title-case = ລຸ່ມຊ້າຍ + .bottom-right-title-case = ລຸ່ມຂວາ + .bottom-attachment = ການຕິດດ້ານລຸ່ມ: + .bottom-center-sentence-case = ລຸ່ມກາງ + .bottom-left-sentence-case = ລຸ່ມຊ້າຍ + .bottom-right-sentence-case = ລຸ່ມຂວາ + .bottomcenter = ລຸ່ມກາງ + .bottomleft = ລຸ່ມຊ້າຍ + .bottomright = ລຸ່ມຂວາ + .box = ກ່ອງ + .break = ຕັດຂາດ + .brightness = ຄວາມສະຫວ່າງ + .by-layer = ຕາມເລເຢີ + .byblock = ByBlock + .bycolor = ByColor + .bylayer = ByLayer + .convtosurface-converted-n-solid-s-to-surface-s = CONVTOSURFACE: ປ່ຽນ __ocs_fmt_0__ ຮູບຊົງຕັນເປັນພື້ນຜິວແລ້ວ. + .convtosurface-select-a-solid-created-this-sess = CONVTOSURFACE: ເລືອກຮູບຊົງຕັນທີ່ສ້າງໃນເຊດຊັນນີ້. + .coords-mode-label = COORDS = __ocs_fmt_0__ (__ocs_fmt_1__) + .copy-specify-destination-count-copies-so-far-a = COPY ກຳນົດປາຍທາງ [ຄັດລອກແລ້ວ __ocs_arg_count__ ອັນ | Array | Enter=ສຳເລັດ | ຈຸດອ້າງອີງ __ocs_arg_bx__,__ocs_arg_by__]: + .cancel = ຍົກເລີກ + .cancel-active-command = ຍົກເລີກຄຳສັ່ງທີ່ເຮັດວຽກຢູ່ + .cannot-delete-a-style-that-is-current-or-in-us = ບໍ່ສາມາດລຶບຮູບແບບທີ່ກຳລັງໃຊ້ງານຢູ່ໄດ້. + .cannot-delete-the-model-layout = ບໍ່ສາມາດລຶບເລເອົາຕ໌ Model ໄດ້. + .cannot-delete-the-standard-style = ບໍ່ສາມາດລຶບຮູບແບບ Standard ໄດ້. + .cannot-rename-the-model-layout = ບໍ່ສາມາດປ່ຽນຊື່ເລເອົາຕ໌ Model ໄດ້. + .cannot-rename-the-standard-style = ບໍ່ສາມາດປ່ຽນຊື່ຮູບແບບ Standard ໄດ້. + .catchment-tagged-successfully = ຕິດແທັກພື້ນທີ່ຮັບນ້ຳສຳເລັດແລ້ວ. + .center = ກາງ + .center-mark-dimcen = ໝາຍຈຸດໃຈກາງ (DIMCEN) + .centered = ຢູ່ເຄິ່ງກາງ + .centimeters = ຊັງຕີແມັດ + .chamfer = ຕັດມຸມ + .changelog = ບັນທຶກການປ່ຽນແປງ + .changing-a-base-colour-switches-to-custom-iced = ການປ່ຽນສີພື້ນຖານຈະສະຫຼັບໄປເປັນ ກຳນົດເອງ. Iced ຈະສ້າງລະດັບສີຂອງທຸກອົງປະກອບຈາກຫົກສີນີ້. + .circle = ວົງມົນ + .class-version = ເວີຊັນຄລາສ + .clear = ລ້າງ + .clear-all = ລ້າງທັງໝົດ + .clip = ຕັດຂອບ + .clipboard = ຄລິບບອດ + .clipboard-is-empty = ຄລິບບອດຫວ່າງເປົ່າ. + .close = ປິດ + .closed-filled = ປິດແບບຕື່ມເຕັມ + .color = ສີ + .command-cancelled-because-the-active-drawing-s = ຄຳສັ່ງຖືກຍົກເລີກຍ້ອນພື້ນທີ່ແຕ້ມທີ່ໃຊ້ງານຢູ່ມີການປ່ຽນແປງ. + .command-history-navigation = ການເລື່ອນເບິ່ງປະຫວັດຄຳສັ່ງ + .command-label-title-case = ຄຳສັ່ງ: + .conceptual = ແນວຄວາມຄິດ + .cone = ຈວຍ + .content = ເນື້ອໃນ + .content-type = ປະເພດເນື້ອໃນ: + .continue = ສືບຕໍ່ + .contrast = ຄວາມຄົມຊັດ + .coordinates = ພິກັດ + .copied-path = ຄັດລອກເສັ້ນທາງແລ້ວ: __ocs_fmt_0__ + .copy = ຄັດລອກ + .could-not-open-file-location-error = ບໍ່ສາມາດເປີດທີ່ຕັ້ງໄຟລ໌ໄດ້: __ocs_fmt_0__ + .could-not-rename-block-orig = ບໍ່ສາມາດປ່ຽນຊື່ບລັອກ "__ocs_fmt_0__" + .could-not-set-default-app-err = ບໍ່ສາມາດຕັ້ງເປັນແອັບເລີ່ມຕົ້ນໄດ້: __ocs_fmt_0__ + .crossing = ຕັດຜ່ານ + .current-dim-style-set-to = ຮູບແບບເສັ້ນບອກຂະໜາດປັດຈຸບັນຖືກຕັ້ງເປັນ '__ocs_fmt_0__'. + .current-multiline-style = ຮູບແບບເສັ້ນຫຼາຍຊັ້ນປັດຈຸບັນ: __ocs_fmt_0__ + .current-table-style-name = ຮູບແບບຕາຕະລາງປັດຈຸບັນ: __ocs_fmt_0__ + .current-text-style = ຮູບແບບຂໍ້ຄວາມປັດຈຸບັນ: __ocs_fmt_0__ + .custom = ກຳນົດເອງ + .cyan = ສີຟ້າອ່ອນ + .cylinder = ຮູບທໍ່ກົມ + .dataextraction-rows-rows-fname = DATAEXTRACTION __ocs_fmt_0__ ແຖວ → "__ocs_fmt_1__" + .dataextraction-write-failed-e = DATAEXTRACTION: ຂຽນຂໍ້ມູນຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .ddedit-entity-type-not-supported = DDEDIT: ບໍ່ຮອງຮັບປະເພດອົງປະກອບນີ້. + .ddedit-text-updated = DDEDIT: ອັບເດດຂໍ້ຄວາມແລ້ວ. + .dimbreak-not-yet-implemented-nothing-changed = DIMBREAK: ຍັງບໍ່ທັນຮອງຮັບ — ບໍ່ມີຫຍັງປ່ຽນແປງ. + .dimjogline-not-yet-implemented-nothing-changed = DIMJOGLINE: ຍັງບໍ່ທັນຮອງຮັບ — ບໍ່ມີຫຍັງປ່ຽນແປງ. + .dimspace-spacing-adjusted = DIMSPACE ປັບໄລຍະຫ່າງແລ້ວ. + .divide-entity-type-not-supported-or-n-2 = DIVIDE: ບໍ່ຮອງຮັບປະເພດອົງປະກອບ ຫຼື N < 2. + .divide-count-point-s-placed = DIVIDE: ວາງແລ້ວ __ocs_fmt_0__ ຈຸດ. + .draworder-select-entities-first = DRAWORDER: ເລືອກອົງປະກອບກ່ອນ. + .danger = ອັນຕະລາຍ + .data = ຂໍ້ມູນ + .data-row = ແຖວຂໍ້ມູນ: + .decimal = ທົດສະນິຍົມ + .decimal-sep-ascii-dimdsep = ຕົວຂັ້ນທົດສະນິຍົມ ASCII (DIMDSEP) + .decimal-separator = ຕົວຂັ້ນທົດສະນິຍົມ + .decimals-dimaltd = ທົດສະນິຍົມ (DIMALTD) + .decimals-dimdec = ທົດສະນິຍົມ (DIMDEC) + .default-title-case = ຄ່າເລີ່ມຕົ້ນ + .default-text = ຂໍ້ຄວາມເລີ່ມຕົ້ນ: + .deg-min-sec = ອົງສາ/ລິບດາ/ຟິລິບດາ + .delete = ລຶບ + .delete-selected-entities = ລຶບອົງປະກອບທີ່ເລືອກ + .description-title-case = ຄຳອະທິບາຍ + .description-label-title-case = ຄຳອະທິບາຍ: + .diameter = ເສັ້ນຜ່າໃຈກາງ + .dim-style = ຮູບແບບເສັ້ນບອກຂະໜາດ + .dimension = ຂະໜາດ + .dimensions = ຂະໜາດ + .discard = ຍົກເລີກການປ່ຽນແປງ + .discussions = ການສົນທະນາ + .discussions-load-from-github = ການສົນທະນາໂຫຼດມາຈາກ GitHub. + .display = ການສະແດງຜົນ + .distance = ໄລຍະຫ່າງ + .donate = ບໍລິຈາກ + .done = ສຳເລັດ + .double = ຄູ່ + .draft = ຮ່າງ + .draw = ແຕ້ມ + .drawing = ຮູບແຕ້ມ + .drawing-units = ຫົວໜ່ວຍຮູບແຕ້ມ + .drop-0-ft-keep-0-in = ຕັດ 0 ຟຸດ, ຮັກສາ 0 ນິ້ວ + .dynamic = ໄດນາມິກ + .extrude-could-not-build-profile-select-a-close = EXTRUDE: ບໍ່ສາມາດສ້າງໜ້າຕັດໄດ້. ເລືອກອົງປະກອບ 2D ແບບປິດ (ວົງມົນ, LwPolyline, ອື່ນໆ). + .extrude-entity-not-found = EXTRUDE: ບໍ່ພົບອົງປະກອບ. + .extrude-solid-created = EXTRUDE: ສ້າງຮູບຊົງຕັນແລ້ວ. + .edge = ຂອບ + .editing-selected = ກຳລັງແກ້ໄຂ: __ocs_arg_selected__ + .elements = ອົງປະກອບ: + .ellipse = ຮູບວົງຮີ + .enable-alternate-units-dimalt = ເປີດໃຊ້ຫົວໜ່ວຍສຳຮອງ (DIMALT) + .end-angle = ມຸມສິ້ນສຸດ + .endpoint = ຈຸດປາຍ + .engineering = ວິສະວະກຳ + .enter = Enter + .expected-coordinates-x-y-or-a-number-got-text = ຕ້ອງການພິກັດ (x,y) ຫຼື ຕົວເລກ, ແຕ່ໄດ້ຮັບ: "__ocs_fmt_0__" + .extension = ສ່ວນຕໍ່ຂະຫຍາຍ + .extension-dimdle = ສ່ວນຕໍ່ຂະຫຍາຍ (DIMDLE) + .extension-dimexe = ສ່ວນຕໍ່ຂະຫຍາຍ (DIMEXE) + .extents = ຂອບເຂດທັງໝົດ + .find-replace-replaced-1-occurrence-of-search = FIND/REPLACE: ແທນບ່ອນ 1 ຈຸດຂອງ "__ocs_fmt_0__". + .find-replace-replaced-replaced-occurrence-s-of = FIND/REPLACE: ແທນບ່ອນ __ocs_fmt_0__ ຈຸດຂອງ "__ocs_fmt_1__". + .flatshot-created-n-projected-edge-s-at-z-0 = FLATSHOT: ສ້າງ __ocs_fmt_0__ ຂອບສາຍແສງທີ່ Z=0. + .flatshot-select-a-solid-created-this-session = FLATSHOT: ເລືອກຮູບຊົງຕັນທີ່ສ້າງໃນເຊດຊັນນີ້. + .fade = ຈາງ + .failed-to-create-layout-e = ສ້າງເລເອົາຕ໌ຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .feet = ຟຸດ + .fence = ເສັ້ນເລືອກ + .file-locations-are-unavailable-in-the-web-appl = ທີ່ຕັ້ງໄຟລ໌ບໍ່ສາມາດໃຊ້ງານໄດ້ໃນແອັບພລິເຄຊັນເວັບ. + .fill-color-sentence-case = ສີຕື່ມ + .fill-color-aci-dimtfillclr = ສີຕື່ມ ACI (DIMTFILLCLR) + .fillet = ລົບມຸມມົນ + .finalize-command-repeat-last = ຈົບຄຳສັ່ງ / ເຮັດຄຳສັ່ງຫຼ້າສຸດຄືນ + .finalizing = ກຳລັງສຳເລັດ… + .find-and-replace = ຊອກຫາ ແລະ ແທນບ່ອນ + .first-seg-angle = ມຸມທ່ອນທຳອິດ: + .fit = ພໍດີ + .fit-dimatfit = ພໍດີ (DIMATFIT) + .fit-legacy-dimfit = ພໍດີ (DIMFIT ແບບເກົ່າ) + .fixed-height = ຄວາມສູງຄົງທີ່: + .fixed-length-dimfxl = ຄວາມຍາວຄົງທີ່ (DIMFXL) + .fixed-length-ext-lines-dimfxlon = ເສັ້ນຊ່ວຍບອກຂະໜາດຄວາມຍາວຄົງທີ່ (DIMFXLON) + .flat-shaded = ແຮເງົາແບບຮາບ + .flat-shaded-edges = ແຮເງົາແບບຮາບ + ຂອບ + .flow-direction = ທິດທາງການໄຫຼ: + .font-load-failed-script-e = ໂຫຼດຟອນຫຼົ້ມເຫຼວ (__ocs_fmt_0__): __ocs_fmt_1__ + .force-text-inside-dimtix = ບັງຄັບຂໍ້ຄວາມໄວ້ທາງໃນ (DIMTIX) + .format = ຮູບແບບ + .format-dimlunit = ຮູບແບບ (DIMLUNIT) + .fraction-format-dimfrac = ຮູບແບບເສດສ່ວນ (DIMFRAC) + .fractional = ເສດສ່ວນ + .front = ດ້ານໜ້າ + .full = ເຕັມ + .full-file-paths-are-unavailable-in-the-web-app = ເສັ້ນທາງໄຟລ໌ແບບເຕັມບໍ່ມີໃຫ້ໃຊ້ງານໃນແອັບພລິເຄຊັນເວັບ. + .general = ທົ່ວໄປ + .generate-tolerances-dimtol = ສ້າງຄ່າຄວາມຄາດເຄື່ອນ (DIMTOL) + .geometry = ເລຂາຄະນິດ + .gouraud-shaded = ແຮເງົາແບບ Gouraud + .gouraud-shaded-edges = ແຮເງົາແບບ Gouraud + ຂອບ + .green = ສີຂຽວ + .group = ກຸ່ມ + .group-created = ສ້າງກຸ່ມ "__ocs_fmt_0__" ແລ້ວ. + .groups = ກຸ່ມ + .h-margin = ໄລຍະຂອບລວງນອນ: + .hatchedit-hatch-entity-not-found = HATCHEDIT: ບໍ່ພົບອົງປະກອບລວດລາຍແຮເງົາ. + .hatchedit-hatch-updated = HATCHEDIT: ອັບເດດລວດລາຍແຮເງົາແລ້ວ. + .hatchedit-not-a-hatch-entity = HATCHEDIT: ບໍ່ແມ່ນອົງປະກອບລວດລາຍແຮເງົາ. + .handle = ແຮນເດິລ + .hatch = ລວດລາຍແຮເງົາ + .header-row = ແຖວຫົວຂໍ້: + .header-row-suppressed = ລະງັບແຖວຫົວຂໍ້ + .height = ຄວາມສູງ + .height-dimtxt = ຄວາມສູງ (DIMTXT) + .helix = ກ້ຽວ + .hidden = ເຊື່ອງ + .hidden-line = ເສັ້ນເຊື່ອງ + .high = ສູງ + .horizontal = ລວງນອນ + .horizontal-inside-dimtih = ລວງນອນດ້ານໃນ (DIMTIH) + .horizontal-just-dimjust = ຈັດແນວລວງນອນ (DIMJUST) + .horizontal-outside-dimtoh = ລວງນອນດ້ານນອກ (DIMTOH) + .hyperlink = ໄຮເປີລິ້ງ + .image-short-pw-ph-px = IMAGE "__ocs_fmt_0__": __ocs_fmt_1__×__ocs_fmt_2__ ພິກເຊວ + .image-e = IMAGE: __ocs_fmt_0__ + .importobj-imported-file-stem-as-mesh = IMPORTOBJ: ນຳເຂົ້າ "__ocs_fmt_0__" ເປັນເມຊແລ້ວ. + .importobj-target-drawing-was-closed = IMPORTOBJ: ຮູບແຕ້ມເປົ້າໝາຍຖືກປິດແລ້ວ. + .importobj-error = IMPORTOBJ: __ocs_fmt_0__ + .interfere-created-an-interference-solid-from-t = INTERFERE: ສ້າງຮູບຊົງຕັນສ່ວນຕັດກັນຈາກສ່ວນທີ່ທັບຊ້ອນແລ້ວ. + .interfere-select-exactly-two-solids-created-th = INTERFERE: ເລືອກຮູບຊົງຕັນທີ່ສ້າງໃນເຊດຊັນນີ້ພຽງ 2 ອັນ. + .interfere-the-selected-solids-do-not-overlap = INTERFERE: ຮູບຊົງຕັນທີ່ເລືອກບໍ່ໄດ້ທັບຊ້ອນກັນ. + .import = ນຳເຂົ້າ + .inches = ນິ້ວ + .index = ດັດຊະນີ + .insert = ແຊກ + .insert-block = ແຊກບລັອກ + .insertion = ການແຊກ + .installed = ຕິດຕັ້ງແລ້ວ + .intersection = ຈຸດຕັດ + .invert-selection-object-s-selected = ສະຫຼັບການເລືອກ: ເລືອກແລ້ວ __ocs_fmt_0__ ວັດຖຸ. + .invisible = ເບິ່ງບໍ່ເຫັນ + .jis = JIS + .join-count-in-object-s-joined-into-count-out = JOIN: ຕໍ່ __ocs_fmt_0__ ວັດຖຸ ເຂົ້າເປັນ __ocs_fmt_1__ ແລ້ວ. + .jog-angle-dimjogang = ມຸມຫັກ ° (DIMJOGANG) + .jogged-radius = ລັດສະໝີແບບຫັກ + .join = ຕໍ່ຕິດ + .keep-0-ft-drop-0-in = ຮັກສາ 0 ຟຸດ, ຕັດ 0 ນິ້ວ + .keep-recent-files = ຮັກສາໄຟລ໌ຫຼ້າສຸດໄວ້ + .kilometers = ກິໂລແມັດ + .layerstate-deleted-name = LAYERSTATE: ລຶບ "__ocs_fmt_0__" ແລ້ວ. + .layerstate-restored-name-restored-layer-s = LAYERSTATE: ກູ້ຄືນ "__ocs_fmt_0__" ແລ້ວ (__ocs_fmt_1__ ເລເຢີ). + .layerstate-saved-name-in-the-drawing = LAYERSTATE: ບັນທຶກ "__ocs_fmt_0__" ໃນຮູບແຕ້ມແລ້ວ. + .layerstate-updated-name = LAYERSTATE: ອັບເດດ "__ocs_fmt_0__" ແລ້ວ. + .layout-e = LAYOUT: __ocs_fmt_0__ + .lengthen-applied = LENGTHEN: ນຳໃຊ້ແລ້ວ. + .lengthen-entity-type-not-supported = LENGTHEN: ບໍ່ຮອງຮັບປະເພດອົງປະກອບນີ້. + .loft-could-not-loft-profiles-ensure-sections-h = LOFT: ບໍ່ສາມາດລັອບຮູບໜ້າຕັດໄດ້. ກະລຸນາກວດສອບໃຫ້ແນ່ໃຈວ່າໜ້າຕັດມີຈຳນວນຂອບເທົ່າກັນ ແລະ ເຂົ້າກັນໄດ້. + .loft-solid-created-from-profiles = LOFT: ສ້າງຮູບຊົງຕັນຈາກ __ocs_fmt_0__ ໜ້າຕັດແລ້ວ. + .landing-distance-sentence-case = ໄລຍະພັກເສັ້ນຊີ້ + .landing-distance-label-sentence-case = ໄລຍະພັກເສັ້ນຊີ້: + .landing-gap-sentence-case = ຊ່ອງຫວ່າງເສັ້ນພັກ + .landing-gap-label-sentence-case = ຊ່ອງຫວ່າງເສັ້ນພັກ: + .landscape = ລວງນອນ + .language-title-case = ພາສາ + .language-label-title-case = ພາສາ: + .layer = ເລເຢີ + .layer-matched-to-layer = ຈັບຄູ່ເລເຢີໃສ່ "__ocs_fmt_0__" ແລ້ວ. + .layer-state-name-already-exists = ຊື່ສະຖານະເລເຢີ "__ocs_fmt_0__" ມີຢູ່ແລ້ວ. + .layer-state-name-was-not-found = ບໍ່ພົບຊື່ສະຖານະເລເຢີ "__ocs_fmt_0__". + .layer-state-name-cannot-be-empty = ຊື່ສະຖານະເລເຢີຕ້ອງບໍ່ຫວ່າງເປົ່າ. + .layers = ເລເຢີ + .layout = ເລເອົາຕ໌ + .layout-name-silindi = ລຶບເລເອົາຕ໌ "__ocs_fmt_0__" ແລ້ວ + .layout-new-name-created-use-mview-to-add-a-vie = ສ້າງເລເອົາຕ໌ "__ocs_fmt_0__" ແລ້ວ — ໃຊ້ MVIEW ເພື່ອເພີ່ມວິວພອດ + .layout-orig-new-name = ເລເອົາຕ໌ "__ocs_fmt_0__" → "__ocs_fmt_1__" + .layout-name-created = ສ້າງເລເອົາຕ໌ '__ocs_fmt_0__' ແລ້ວ. + .layout-name-deleted = ລຶບເລເອົາຕ໌ '__ocs_fmt_0__' ແລ້ວ. + .layout-manager = ຕົວຈັດການເລເອົາຕ໌ + .layout-name-cannot-be-empty = ຊື່ເລເອົາຕ໌ຕ້ອງບໍ່ຫວ່າງເປົ່າ. + .layout-renamed-old-name-new-name = ປ່ຽນຊື່ເລເອົາຕ໌: '__ocs_fmt_0__' → '__ocs_fmt_1__' + .leader = ເສັ້ນຊີ້ + .leader-format = ຮູບແບບເສັ້ນຊີ້ + .leader-structure = ໂຄງສ້າງເສັ້ນຊີ້ + .leaders = ເສັ້ນຊີ້ + .leading-trailing = ທາງໜ້າ ແລະ ທາງຫຼັງ + .left = ຊ້າຍ + .left-attachment = ການຕິດດ້ານຊ້າຍ: + .left-to-right-dimtxtdirection = ຊ້າຍຫາຂວາ (DIMTXTDIRECTION) + .length = ຄວາມຍາວ + .light = ແຈ້ງ + .limits-generation-dimlim = ການສ້າງຂີດຈຳກັດ (DIMLIM) + .line = ເສັ້ນ + .linear = ເສັ້ນຊື່ + .lines = ເສັ້ນ + .linetype-title-case = ປະເພດເສັ້ນ + .linetype-scale = ມາດຕາສ່ວນປະເພດເສັ້ນ + .lineweight-title-case = ຄວາມໜາເສັ້ນ + .list = ລາຍການ + .loaded = ໂຫຼດແລ້ວ + .loading-discussions = ກຳລັງໂຫຼດການສົນທະນາ… + .loading-videos = ກຳລັງໂຫຼດວິດີໂອ… + .load = ໂຫຼດ… + .low = ຕ່ຳ + .measure-entity-type-not-supported-or-distance = MEASURE: ບໍ່ຮອງຮັບປະເພດອົງປະກອບ ຫຼື ໄລຍະຫ່າງໃຫຍ່ເກີນໄປ. + .measure-count-point-s-placed = MEASURE: ວາງແລ້ວ __ocs_fmt_0__ ຈຸດ. + .mleaderalign-leaders-aligned = MLEADERALIGN ຈັດແນວເສັ້ນຊີ້ແລ້ວ. + .mleadercollect-leaders-collected = MLEADERCOLLECT ຮວບຮວມເສັ້ນຊີ້ແລ້ວ. + .model-uppercase = MODEL + .ms-is-only-available-in-paper-space-layouts = MS ສາມາດໃຊ້ໄດ້ສະເພາະໃນເລເອົາຕ໌ພື້ນທີ່ເຈ້ຍ (Paper Space) ເທົ່ານັ້ນ. + .mspace = MSPACE + .mtext = ຂໍ້ຄວາມຫຼາຍແຖວ (Mtext) + .mview-the-clipping-boundary-has-no-usable-area = MVIEW: ຂອບເຂດການຕັດບໍ່ມີພື້ນທີ່ທີ່ໃຊ້ງານໄດ້. + .magenta = ສີບົວເຂັ້ມ + .manage = ຈັດການ + .meters = ແມັດ + .middle = ເຄິ່ງກາງ + .middle-center-title-case = ເຄິ່ງກາງ-ໃຈກາງ + .middle-left-title-case = ເຄິ່ງກາງ-ຊ້າຍ + .middle-right-title-case = ເຄິ່ງກາງ-ຂວາ + .middle-center-sentence-case = ເຄິ່ງກາງ-ໃຈກາງ + .middle-left-sentence-case = ເຄິ່ງກາງ-ຊ້າຍ + .middle-right-sentence-case = ເຄິ່ງກາງ-ຂວາ + .middleleft = ເຄິ່ງກາງ-ຊ້າຍ + .middleright = ເຄິ່ງກາງ-ຂວາ + .mid-between-2-points-m2p = ເຄິ່ງກາງລະຫວ່າງ 2 ຈຸດ (M2P) + .midpoint = ຈຸດເຄິ່ງກາງ + .mtp-needs-an-active-point-prompt = MTP ຕ້ອງການການລະບຸຈຸດທີ່ກຳລັງໃຊ້ງານຢູ່. + .miles = ໄມລ໌ + .millimeters = ມິນລິແມັດ + .model-title-case = ໂມເດວ + .modify = ດັດແປງ + .move = ຍ້າຍ + .multiple = ຫຼາຍອັນ + .multiplier-dimaltf = ຕົວຄູນ (DIMALTF) + .name-title-case = ຊື່ + .name-label-title-case = ຊື່: + .nearest = ໃກ້ຄຽງທີ່ສຸດ + .new = ໃໝ່ + .no = ບໍ່ + .no-drawing-open-use-new-or-open-first = ບໍ່ມີຮູບແຕ້ມເປີດຢູ່ — ໃຊ້ ໃໝ່ ຫຼື ເປີດ ກ່ອນ. + .no-groups-found-for-selected-objects = ບໍ່ພົບກຸ່ມສຳລັບວັດຖຸທີ່ເລືອກ. + .no-plugin-handled-command = ບໍ່ມີປລັກອິນຮອງຮັບ: __ocs_fmt_0__ + .no-viewport-found-in-this-layout = ບໍ່ພົບວິວພອດໃນເລເອົາຕ໌ນີ້. + .node = ໂນດ + .none-title-case = ບໍ່ມີ + .normal = ປົກກະຕິ + .nothing-found-at-that-point = ບໍ່ພົບຫຍັງຢູ່ຈຸດນັ້ນ. + .nothing-to-redo = ບໍ່ມີສິ່ງທີ່ຈະເຮັດຄືນ. + .nothing-to-undo = ບໍ່ມີສິ່ງທີ່ຈະຍ້ອນກັບ. + .ok = ຕົກລົງ + .object-title-case = ວັດຖຸ + .object-is-on-locked-layer-layer-unlock-the-layer-to-edit-it = ວັດຖຸຢູ່ໃນເລເຢີທີ່ລັອກໄວ້ "__ocs_fmt_0__" — ປົດລັອກເລເຢີເພື່ອແກ້ໄຂ. + .object-is-on-locked-layer-layer-unlock-the-layer-to-select-or-edit-it = ວັດຖຸຢູ່ໃນເລເຢີທີ່ລັອກໄວ້ "__ocs_fmt_0__" — ປົດລັອກເລເຢີເພື່ອເລືອກ ຫຼື ແກ້ໄຂ. + .oblique = ມຸມອຽງ (°): + .oblique-angle = ມຸມອຽງ + .off = ປິດ + .offset = ໄລຍະຂະໜານ + .on = ເປີດ + .open = ເປີດ + .open-dwg-dxf-not-yet-implemented = ເປີດ DWG/DXF: ຍັງບໍ່ທັນຮອງຮັບເທື່ອ. + .open-cancelled = ຍົກເລີກການເປີດ: "__ocs_fmt_0__" + .open-failed-e = ເປີດຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .open-or-create-a-drawing-to-add-a-layout = ເປີດ ຫຼື ສ້າງຮູບແຕ້ມເພື່ອເພີ່ມເລເອົາຕ໌. + .open-or-create-a-drawing-to-manage-layer-state = ເປີດ ຫຼື ສ້າງຮູບແຕ້ມເພື່ອຈັດການສະຖານະເລເຢີ. + .open-or-create-a-drawing-to-manage-layouts = ເປີດ ຫຼື ສ້າງຮູບແຕ້ມເພື່ອຈັດການເລເອົາຕ໌. + .open-or-create-a-drawing-to-switch-layouts = ເປີດ ຫຼື ສ້າງຮູບແຕ້ມເພື່ອສະຫຼັບເລເອົາຕ໌. + .opened-file-location = ເປີດທີ່ຕັ້ງໄຟລ໌ແລ້ວ: __ocs_fmt_0__ + .opening-name-size-label = ກຳລັງເປີດ "__ocs_fmt_0__" (__ocs_fmt_1__)… + .options-title-case = ຕົວເລືອກ + .origin = ຈຸດກຳເນີດ + .ortho = ອໍໂທ + .output = ຜົນອອກ + .outside = ດ້ານນອກ + .outside-limits = ເກີນຂອບເຂດ. + .overall-scale = ມາດຕາສ່ວນລວມ + .overwrite = ຂຽນທັບ + .pedit-applied = PEDIT: ນຳໃຊ້ແລ້ວ. + .pedit-cannot-convert-this-entity = PEDIT: ບໍ່ສາມາດແປງອົງປະກອບນີ້ໄດ້. + .pedit-operation-not-applicable-to-this-entity = PEDIT: ການດຳເນີນງານບໍ່ສາມາດໃຊ້ກັບອົງປະກອບນີ້ໄດ້. + .perf = ປະສິດທິພາບ + .polysolid-built-a-wall-solid-from-used-segment = POLYSOLID: ສ້າງຮູບຊົງຕັນແບບກຳແພງຈາກ __ocs_fmt_0__ ທ່ອນແລ້ວ (ຄວາມກວ້າງ __ocs_fmt_1__, ຄວາມສູງ __ocs_fmt_2__). + .polysolid-select-a-polyline-first = POLYSOLID: ເລືອກໂພລີລາຍກ່ອນ. + .polysolid-the-polyline-has-no-usable-segments = POLYSOLID: ໂພລີລາຍບໍ່ມີທ່ອນເສັ້ນທີ່ໃຊ້ງານໄດ້. + .pspace = PSPACE + .pyramid-could-not-build-all-faces = PYRAMID: ບໍ່ສາມາດສ້າງທຸກໜ້າໄດ້. + .pyramid-created-a-n-sided-pyramid-radius-radiu = PYRAMID: ສ້າງພີຣາມິດ __ocs_fmt_0__ ດ້ານແລ້ວ (ລັດສະໝີ __ocs_fmt_1__, ຄວາມສູງ __ocs_fmt_2__). + .pyramid-radius-and-height-must-be-positive = PYRAMID: ລັດສະໝີ ແລະ ຄວາມສູງຕ້ອງເປັນຄ່າບວກ. + .pyramid-tessellation-failed = PYRAMID: ການແບ່ງຕາໜ່າງຜິວຫຼົ້ມເຫຼວ. + .pyramid-tessellation-produced-no-geometry = PYRAMID: ການແບ່ງຕາໜ່າງຜິວບໍ່ໄດ້ສ້າງເລຂາຄະນິດໃດໆ. + .page-setup = ການຕັ້ງຄ່າໜ້າເຈ້ຍ + .paper = ເຈ້ຍ + .parallel = ຂະໜານ + .parsing-entities = ກຳລັງວິເຄາະອົງປະກອບ… + .paste = ວາງ + .perpendicular = ຕັ້ງສາກ + .photometric = ໂຟໂຕເມຕຣິກ + .pick-a-point = ເລືອກຈຸດ + .pick-an-object = ເລືອກວັດຖຸ + .pinned = ປັກໝຸດແລ້ວ + .place-text-manually-dimupt = ວາງຂໍ້ຄວາມດ້ວຍຕົນເອງ (DIMUPT) + .plot = ພລັອດ + .plot-style = ຮູບແບບການພລັອດ + .plot-style-loaded-color-entries = ຮູບແບບການພລັອດ '__ocs_fmt_0__' ຖືກໂຫຼດແລ້ວ (__ocs_fmt_1__ ລາຍການສີ). + .plot-style-table-cleared = ລ້າງຕາຕະລາງຮູບແບບການພລັອດແລ້ວ. + .plot-style-table-saved-to = ບັນທຶກຕາຕະລາງຮູບແບບການພລັອດໄປທີ່ "__ocs_fmt_0__" ແລ້ວ. + .plot-window-x0-2-y0-2-to-x1-2-y1-2 = ປ່ອງຢ້ຽມພລັອດ: __ocs_fmt_0__,__ocs_fmt_1__ ຫາ __ocs_fmt_2__,__ocs_fmt_3__ + .plot-window-x1-2-y1-2-to-x2-2-y2-2 = ປ່ອງຢ້ຽມພລັອດ: __ocs_fmt_0__,__ocs_fmt_1__ ຫາ __ocs_fmt_2__,__ocs_fmt_3__ + .plugin-id-failed-to-load-e = ໂຫຼດປລັກອິນ '__ocs_fmt_0__' ຫຼົ້ມເຫຼວ: __ocs_fmt_1__ + .plugin-manager = ຕົວຈັດການປລັກອິນ + .point = ຈຸດ + .point-cloud = ກຸ່ມຈຸດ (Point Cloud) + .polyline = ໂພລີລາຍ + .portrait = ລວງຕັ້ງ + .position = ຕຳແໜ່ງ + .position-x = ຕຳແໜ່ງ X + .position-y = ຕຳແໜ່ງ Y + .position-z = ຕຳແໜ່ງ Z + .precision = ຄວາມລະອຽດ + .preset = ຄ່າຕັ້ງລ່ວງໜ້າ + .previous = ກ່ອນໜ້າ + .primary = ຫຼັກ + .primary-units = ຫົວໜ່ວຍຫຼັກ + .print = ພິມ + .print-failed-e = ພິມຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .prompt = ຂໍ້ຄວາມເຕືອນ + .properties = ຄຸນສົມບັດ + .properties-matched-to-object-s = ຈັບຄູ່ຄຸນສົມບັດໃສ່ __ocs_fmt_0__ ວັດຖຸແລ້ວ. + .qselect-object-s-selected = QSELECT: ເລືອກແລ້ວ __ocs_fmt_0__ ວັດຖຸ. + .quadrant = ຈຸດຕັດສີ່ສ່ວນວົງມົນ + .quality = ຄຸນນະພາບ + .revolve-could-not-revolve-profile = REVOLVE: ບໍ່ສາມາດໝຸນຮູບໜ້າຕັດຮອບແກນໄດ້. + .revolve-created-objects-failed-sources = REVOLVE: ສ້າງແລ້ວ __ocs_arg_created__ ວັດຖຸ; __ocs_arg_failed__ ຕົ້ນສະບັບບໍ່ສາມາດໝຸນຂຶ້ນຮູບໄດ້. + .revolve-entity-not-found = REVOLVE: ບໍ່ພົບອົງປະກອບ. + .revolve-no-selected-object-could-be-revolved = REVOLVE: ບໍ່ມີວັດຖຸທີ່ເລືອກສາມາດໝຸນຂຶ້ນຮູບຕາມຕົວເລືອກທີ່ລະບຸໄດ້. + .revolve-solid-created-0 = REVOLVE: ສ້າງຮູບຊົງຕັນແລ້ວ (__ocs_fmt_0__°). + .radians = ເຣດຽນ + .radius = ລັດສະໝີ + .ray = ເສັ້ນແສງເຄິ່ງບໍ່ມີສິ້ນສຸດ + .read-only-session-read-only-saving-is-disabled = ເຊດຊັນອ່ານຢ່າງດຽວ (--read-only): ປິດການບັນທຶກ. + .realistic = ສົມຈິງ + .recent-documents = ເອກະສານຫຼ້າສຸດ + .recent-files = ໄຟລ໌ຫຼ້າສຸດ + .recent-file-no-longer-exists = ໄຟລ໌ຫຼ້າສຸດບໍ່ມີຢູ່ນີ້ອີກຕໍ່ໄປ: __ocs_fmt_0__ + .red = ສີແດງ + .redo-last-label = ເຮັດຄືນ: __ocs_fmt_0__ + .reference = ອ້າງອີງ + .reference-point = ຈຸດອ້າງອີງ + .remove-leader = ລຶບເສັ້ນຊີ້ອອກ + .rename = ປ່ຽນຊື່ + .renamed-old-new = ປ່ຽນຊື່ '__ocs_fmt_0__' → '__ocs_fmt_1__' ແລ້ວ. + .replace = ແທນບ່ອນ + .replacement-text = ຂໍ້ຄວາມແທນບ່ອນ + .report = ລາຍງານ + .requires-0-or-1-or-multiple-or-single = ຕ້ອງການ 0 ຫຼື 1 ຫຼື MULTIPLE ຫຼື SINGLE. + .requires-single-or-multiple = ຕ້ອງການ Single ຫຼື Multiple. + .restore = ຄືນຄ່າ + .retry = ລອງໃໝ່ + .reverse = ກັບທິດທາງ + .right = ຂວາ + .right-attachment = ການຕິດດ້ານຂວາ: + .rotate = ໝຸນ + .rotation = ການໝຸນ + .round-off-dimaltrnd = ປັດເສດ (DIMALTRND) + .round-off-dimrnd = ປັດເສດ (DIMRND) + .section-could-not-build-the-cutting-plane = SECTION: ບໍ່ສາມາດສ້າງລະນາບຕັດໄດ້. + .section-could-not-determine-the-solid-s-extent = SECTION: ບໍ່ສາມາດລະບຸຂອບເຂດຂອງຮູບຊົງຕັນໄດ້. + .section-created-section-line-s-at-value = SECTION: ສ້າງ __ocs_fmt_0__ ເສັ້ນໜ້າຕັດ ທີ່ __ocs_fmt_1__=__ocs_fmt_2__ ແລ້ວ. + .section-select-exactly-one-solid-created-this = SECTION: ເລືອກຮູບຊົງຕັນທີ່ສ້າງໃນເຊດຊັນນີ້ພຽງ 1 ອັນ. + .section-solid-modelling-is-unavailable-in-this = SECTION: ການສ້າງແບບຈຳລອງຮູບຊົງຕັນບໍ່ມີໃຫ້ໃຊ້ງານໃນລຸ້ນນີ້. + .section-the-plane-does-not-cross-the-solid = SECTION: ລະນາບບໍ່ໄດ້ຕັດຜ່ານຮູບຊົງຕັນ. + .slice-failed-the-plane-may-not-cross-the-solid = SLICE ຫຼົ້ມເຫຼວ — ລະນາບອາດຈະບໍ່ໄດ້ຕັດຜ່ານຮູບຊົງຕັນ. + .slice-could-not-determine-the-solid-s-extent = SLICE: ບໍ່ສາມາດລະບຸຂອບເຂດຂອງຮູບຊົງຕັນໄດ້. + .slice-cut-at-ax-value-kept-the-half = SLICE: ຕັດທີ່ __ocs_fmt_0__=__ocs_fmt_1__, ຮັກສາເຄິ່ງ __ocs_fmt_2__ ໄວ້. + .slice-select-exactly-one-solid-created-this-se = SLICE: ເລືອກຮູບຊົງຕັນທີ່ສ້າງໃນເຊດຊັນນີ້ພຽງ 1 ອັນ. + .slice-the-plane-does-not-cross-the-solid-on-th = SLICE: ລະນາບບໍ່ໄດ້ຕັດຜ່ານຮູບຊົງຕັນໃນດ້ານທີ່ຮັກສາໄວ້. + .both = ທັງສອງດ້ານ + .slice-select-at-least-one-target = SLICE: ເລືອກຮູບຊົງຕັນ ຫຼື ພື້ນຜິວຢ່າງໜ້ອຍ 1 ອັນ. + .slice-invalid-plane = SLICE: ລະນາບຕັດບໍ່ຖືກຕ້ອງ. + .slice-side-point-on-plane = SLICE: ເລືອກຈຸດທີ່ຢູ່ຫ່າງຈາກລະນາບຕັດ. + .slice-plane-misses-target = SLICE: ລະນາບຕັດບໍ່ໄດ້ຕັດຜ່ານທຸກວັດຖຸທີ່ເລືອກ; ບໍ່ມີວັດຖຸໃດຖືກປ່ຽນແປງ. + .slice-unsupported-intersection = SLICE: ເລຂາຄະນິດທີ່ເລືອກມີສ່ວນຕັດກັນຂອງພື້ນຜິວທີ່ບໍ່ຮອງຮັບ; ບໍ່ມີວັດຖຸໃດຖືກປ່ຽນແປງ. + .slice-coincident-plane = SLICE: ລະນາບຕັດທັບຊ້ອນກັບເລຂາຄະນິດຕົ້ນສະບັບ; ບໍ່ມີວັດຖຸໃດຖືກປ່ຽນແປງ. + .slice-topology-refused = SLICE: ໂຄງສ້າງຮູບຮ່າງທີ່ເລືອກບໍ່ສາມາດແຍກໄດ້ຢ່າງປອດໄພ; ບໍ່ມີວັດຖຸໃດຖືກປ່ຽນແປງ. + .slice-result-encode-failed = SLICE: ບໍ່ສາມາດເຂົ້າລະຫັດຜົນຮັບໄດ້; ບໍ່ມີວັດຖຸໃດຖືກປ່ຽນແປງ. + .slice-result-display-failed = SLICE: ບໍ່ສາມາດສະແດງຜົນຮັບໄດ້ຄົບຖ້ວນ; ບໍ່ມີວັດຖຸໃດຖືກປ່ຽນແປງ. + .slice-result-commit-failed = SLICE: ບໍ່ສາມາດບັນທຶກຜົນຮັບໄດ້; ວັດຖຸຕົ້ນສະບັບທັງໝົດຍັງຖືກຮັກສາໄວ້. + .slice-retained-update-failed = SLICE: ບໍ່ສາມາດອັບເດດຜົນຮັບທີ່ຮັກສາໄວ້ໄດ້. + .slice-created-results = SLICE: ສ້າງ __ocs_arg_count__ ວັດຖຸຜົນຮັບແລ້ວ. + .splinefit-fit-a-spline-through-n-points = SPLINEFIT: ປັບເສັ້ນສະພລາຍຜ່ານ __ocs_fmt_0__ ຈຸດ. + .splinefit-need-at-least-3-points = SPLINEFIT: ຕ້ອງການຢ່າງໜ້ອຍ 3 ຈຸດ. + .splinefit-select-a-polyline-to-fit-a-spline-th = SPLINEFIT: ເລືອກໂພລີລາຍເພື່ອປັບເສັ້ນສະພລາຍຜ່ານ. + .stepout-exported-to = STEPOUT: ສົ່ງອອກໄປທີ່ "__ocs_fmt_0__" ແລ້ວ + .stepout-no-3d-mesh-data-in-this-drawing = STEPOUT: ບໍ່ມີຂໍ້ມູນເມຊ 3D ໃນຮູບແຕ້ມນີ້. + .stepout-error = STEPOUT: __ocs_fmt_0__ + .stlout-exported-to = STLOUT: ສົ່ງອອກໄປທີ່ "__ocs_fmt_0__" ແລ້ວ + .stlout-no-3d-mesh-data-in-this-drawing = STLOUT: ບໍ່ມີຂໍ້ມູນເມຊ 3D ໃນຮູບແຕ້ມນີ້. + .stlout-error = STLOUT: __ocs_fmt_0__ + .stretch-nothing-crosses-the-window = STRETCH: ບໍ່ມີສິ່ງໃດຕັດຜ່ານປ່ອງຢ້ຽມເລືອກ. + .stretch-count-entity-ies-stretched = STRETCH: ຍືດແລ້ວ __ocs_fmt_0__ ອົງປະກອບ. + .sweep-could-not-sweep-profile-along-path-use-a = SWEEP: ບໍ່ສາມາດກວາດໜ້າຕັດໄປຕາມເສັ້ນທາງໄດ້. ໃຊ້ໜ້າຕັດ 2D ແບບປິດ ແລະ ເສັ້ນທາງທີ່ເປັນ Line ຫຼື Polyline. + .sweep-solid-created = SWEEP: ສ້າງຮູບຊົງຕັນແລ້ວ. + .save = ບັນທຶກ + .save-as = ບັນທຶກເປັນ + .save-drawing-as = ບັນທຶກຮູບແຕ້ມເປັນ + .save-already-running-for-this-drawing = ການບັນທຶກກຳລັງດຳເນີນການຢູ່ແລ້ວສຳລັບຮູບແຕ້ມນີ້. + .save-anyway = ບັນທຶກຕໍ່ໄປ + .save-error-e = ຂໍ້ຜິດພາດໃນການບັນທຶກ: __ocs_fmt_0__ + .save-in-source-version = ບັນທຶກໃນເວີຊັນຕົ້ນສະບັບ + .save-the-drawing-before-copying-its-file-path = ບັນທຶກຮູບແຕ້ມກ່ອນທີ່ຈະຄັດລອກເສັ້ນທາງໄຟລ໌. + .save-the-drawing-before-opening-its-file-locat = ບັນທຶກຮູບແຕ້ມກ່ອນທີ່ຈະເປີດທີ່ຕັ້ງໄຟລ໌. + .saved-download-but-recent-copy-could-not-be-st = ບັນທຶກການດາວໂຫຼດແລ້ວ, ແຕ່ບໍ່ສາມາດບັນທຶກສຳເນົາຫຼ້າສຸດໄດ້: __ocs_fmt_0__ + .scale = ມາດຕາສ່ວນ + .scene-cleared = ລ້າງສາກແລ້ວ. + .scene-cleared-standard-linetypes-loaded = ລ້າງສາກແລ້ວ. ໂຫຼດປະເພດເສັ້ນມາດຕະຖານແລ້ວ. + .scientific = ວິທະຍາສາດ + .search-layers = ຄົ້ນຫາເລເຢີ… + .second-seg-angle = ມຸມທ່ອນທີສອງ: + .select-all = ເລືອກທັງໝົດ + .select-similar-added = ເລືອກສິ່ງທີ່ຄ້າຍກັນ: ເພີ່ມແລ້ວ __ocs_fmt_0__ ອັນ. + .select-a-tangent-object = ເລືອກວັດຖຸສຳຜັດ. + .select-one-object-first-then-run-objectscale = ເລືອກວັດຖຸ 1 ອັນກ່ອນ, ແລ້ວຈຶ່ງສັ່ງ OBJECTSCALE. + .send-feedback = ສົ່ງຄຳຕິຊົມ + .sent-to-printer-printer = ສົ່ງໄປເຄື່ອງພິມແລ້ວ: __ocs_fmt_0__ + .set-current = ຕັ້ງເປັນປັດຈຸບັນ + .shade-plot = ການພລັອດແບບແຮເງົາ + .shadow-flags = ແຟລກເງົາ + .single = ດ່ຽວ + .size = ຂະໜາດ + .snap-override-label-next-pick-only = ປ່ຽນຄ່າສະແນັບຊົ່ວຄາວ: __ocs_fmt_0__ (ສຳລັບການເລືອກຄັ້ງຕໍ່ໄປເທົ່ານັ້ນ). + .solid-created = ສ້າງຮູບຊົງຕັນແລ້ວ. + .source-object-not-found = ບໍ່ພົບວັດຖຸຕົ້ນສະບັບ. + .spacing = ໄລຍະຫ່າງ + .spacing-dimdli = ໄລຍະຫ່າງ (DIMDLI) + .sphere = ຮູບໜ່ວຍມົນ + .spline = ເສັ້ນສະພລາຍ + .sponsors = ຜູ້ສະໜັບສະໜູນ + .standard = ມາດຕະຖານ + .start = ເລີ່ມຕົ້ນ + .start-angle = ມຸມເລີ່ມຕົ້ນ + .status = ສະຖານະ + .stretch = ຍືດ + .style-name-created = ສ້າງຮູບແບບ '__ocs_fmt_0__' ແລ້ວ. + .style-name-deleted = ລຶບຮູບແບບ '__ocs_fmt_0__' ແລ້ວ. + .style-new-already-exists = ຮູບແບບ '__ocs_fmt_0__' ມີຢູ່ແລ້ວ. + .success = ສຳເລັດ + .suffix-dimapost = ຄຳຕໍ່ທ້າຍ (DIMAPOST) + .suffix-dimpost = ຄຳຕໍ່ທ້າຍ (DIMPOST) + .support-on-patreon = ສະໜັບສະໜູນໃນ Patreon + .supporters = ຜູ້ສະໜັບສະໜູນ + .suppress-0-ft-0-in = ຕັດ 0 ຟຸດ & 0 ນິ້ວ + .suppress-leading = ຕັດເລກສູນທາງໜ້າ + .suppress-leading-trailing = ຕັດເລກສູນທາງໜ້າ & ທາງຫຼັງ + .suppress-leading-zeros = ຕັດເລກສູນທາງໜ້າ + .suppress-none = ບໍ່ຕັດ + .suppress-outside-arrows-dimsoxd = ຕັດລູກສອນດ້ານນອກ (DIMSOXD) + .suppress-trailing = ຕັດເລກສູນທາງຫຼັງ + .suppress-trailing-zeros = ຕັດເລກສູນທາງຫຼັງ + .switched-to-layout-name = ສະຫຼັບໄປຫາເລເອົາຕ໌ '__ocs_fmt_0__' ແລ້ວ. + .textedit-selected-entity-is-not-text = TEXTEDIT: ອົງປະກອບທີ່ເລືອກບໍ່ແມ່ນຂໍ້ຄວາມ. + .texteditmode-set-to-display-val = TEXTEDITMODE ຖືກຕັ້ງເປັນ __ocs_fmt_0__ + .table = ຕາຕະລາງ + .tag = ແທັກ + .tangent = ສຳຜັດ + .text = ຂໍ້ຄວາມ + .text-alignment = ການຈັດແນວຂໍ້ຄວາມ: + .text-always-left = ຂໍ້ຄວາມຢູ່ເບື້ອງຊ້າຍສະເໝີ + .text-angle = ມຸມຂໍ້ຄວາມ: + .text-color-sentence-case = ສີຂໍ້ຄວາມ + .text-color-aci = ສີຂໍ້ຄວາມ (ACI): + .text-color-aci-dimclrt = ສີຂໍ້ຄວາມ ACI (DIMCLRT) + .text-fill-mode-dimtfill = ໂໝດຕື່ມພື້ນຂໍ້ຄວາມ (DIMTFILL) + .text-frame = ຂອບຂໍ້ຄວາມ + .text-gap-dimgap = ໄລຍະຫ່າງຂໍ້ຄວາມ (DIMGAP) + .text-height-sentence-case = ຄວາມສູງຂໍ້ຄວາມ + .text-height-label-sentence-case = ຄວາມສູງຂໍ້ຄວາມ: + .text-movement = ການຍ້າຍຂໍ້ຄວາມ + .text-movement-dimtmove = ການຍ້າຍຂໍ້ຄວາມ (DIMTMOVE) + .text-out-first = ຂໍ້ຄວາມອອກກ່ອນ + .text-style = ຮູບແບບຂໍ້ຄວາມ + .text-to-find = ຂໍ້ຄວາມທີ່ຈະຊອກຫາ + .the-selected-object-does-not-support-annotatio = ວັດຖຸທີ່ເລືອກບໍ່ຮອງຮັບມາດຕາສ່ວນຄຳອະທິບາຍ. + .tick-size-dimtsz = ຂະໜາດຂີດ (DIMTSZ) + .title = ຫົວຂໍ້ + .title-row = ແຖວຫົວຂໍ້: + .title-row-suppressed = ລະງັບແຖວຫົວຂໍ້ + .toggle-dynamic-input = ເປີດ/ປິດ ການປ້ອນຂໍ້ມູນແບບໄດນາມິກ + .toggle-object-snap = ເປີດ/ປິດ ການເກາະຈຸດວັດຖຸ + .toggle-object-snap-tracking = ເປີດ/ປິດ ການໄລ່ຕາມຈຸດເກາະວັດຖຸ + .toggle-ortho = ເປີດ/ປິດ ອໍໂທ + .toggle-polar-tracking = ເປີດ/ປິດ ການໄລ່ຕາມແນວໂພລາຣ໌ + .tol-decimals-dimalttd = ທົດສະນິຍົມຄ່າຄວາມຄາດເຄື່ອນ (DIMALTTD) + .tol-zero-supp-dimalttz = ຕັດເລກສູນຄ່າຄວາມຄາດເຄື່ອນ (DIMALTTZ) + .tol-decimals-dimtdec = ທົດສະນິຍົມຄ່າຄວາມຄາດເຄື່ອນ (DIMTDEC) + .tol-vert-just-dimtolj = ຈັດແນວລວງຕັ້ງຄ່າຄວາມຄາດເຄື່ອນ (DIMTOLJ) + .tol-zero-supp-dimtzin = ຕັດເລກສູນຄ່າຄວາມຄາດເຄື່ອນ (DIMTZIN) + .tolerance = ຄ່າຄວາມຄາດເຄື່ອນ + .tolerance-precision = ຄວາມລະອຽດຄ່າຄວາມຄາດເຄື່ອນ + .tolerances = ຄ່າຄວາມຄາດເຄື່ອນ + .tool-palettes = + ແຖບ + ເຄື່ອງມື + .top = ເທິງ + .top-center-title-case = ເທິງກາງ + .top-left-title-case = ເທິງຊ້າຍ + .top-right-title-case = ເທິງຂວາ + .top-attachment = ການຕິດດ້ານເທິງ: + .top-center-sentence-case = ເທິງກາງ + .top-left-sentence-case = ເທິງຊ້າຍ + .top-right-sentence-case = ເທິງຂວາ + .top-to-bottom = ເທິງລົງລຸ່ມ + .topcenter = ເທິງກາງ + .topleft = ເທິງຊ້າຍ + .topright = ເທິງຂວາ + .torus = ວົງແຫວນກົມ (Torus) + .transparency = ຄວາມໂປ່ງໃສ + .truetype-system = TrueType (ລະບົບ) + .ttr = Ttr (ສຳຜັດ-ສຳຜັດ-ລັດສະໝີ) + .tutorials = ບົດຮຽນ + .type = ປະເພດ + .ucs-world = UCS: ໂລກ (World) + .ucs = UCS: __ocs_fmt_0__ + .unable-to-change-ui-language-error = ບໍ່ສາມາດປ່ຽນພາສາສ່ວນຕິດຕໍ່ຜູ້ໃຊ້ໄດ້: __ocs_fmt_0__ + .undo = ຍ້ອນກັບ + .undo-last-label = ຍ້ອນກັບ: __ocs_fmt_0__ + .uniform-scale = ມາດຕາສ່ວນສະໝ່ຳສະເໝີ + .unit = ຫົວໜ່ວຍ + .unit-format-dimaltu = ຮູບແບບຫົວໜ່ວຍ (DIMALTU) + .unit-format-dimunit = ຮູບແບບຫົວໜ່ວຍ (DIMUNIT) + .unitless = ບໍ່ມີຫົວໜ່ວຍ + .unknown = ບໍ່ຮູ້ຈັກ + .unsaved-changes = ການປ່ຽນແປງທີ່ຍັງບໍ່ໄດ້ບັນທຶກ + .unsupported-file-type = ປະເພດໄຟລ໌ທີ່ບໍ່ຮອງຮັບ: __ocs_fmt_0__ + .up = ຂຶ້ນ + .update-available = ມີເວີຊັນອັບເດດໃໝ່ + .upside-down = ກັບຫົວ + .v-margin = ໄລຍະຂອບລວງຕັ້ງ: + .vplayer-frozen-count-layer-s-frozen-in-viewpor = VPLAYER: ແຊ່ແຂງ __ocs_fmt_0__ ເລເຢີ ໃນວິວພອດແລ້ວ. + .vplayer-thawed-count-layer-s-thawed-in-viewpor = VPLAYER: ຍົກເລີກການແຊ່ແຂງ __ocs_fmt_0__ ເລເຢີ ໃນວິວພອດແລ້ວ. + .value = ຄ່າ + .version = ເວີຊັນ + .vertical = ລວງຕັ້ງ + .vertical-pos-dimtad = ຕຳແໜ່ງລວງຕັ້ງ (DIMTAD) + .vertical-pos-dimtvp = ຕຳແໜ່ງລວງຕັ້ງ (DIMTVP) + .videos = ວິດີໂອ + .videos-load-from-the-internet = ວິດີໂອໂຫຼດຈາກອິນເຕີເນັດ. + .view = ມຸມມອງ + .view-home = ມຸມມອງ: ເລີ່ມຕົ້ນ + .view-value = ມຸມມອງ: __ocs_fmt_0__ + .viewport = ວິວພອດ + .viewport-visual-style-label = ຮູບແບບການສະແດງພາບວິວພອດ: __ocs_fmt_0__ + .visual-style = ຮູບແບບການສະແດງພາບ + .visual-style-label = ຮູບແບບການສະແດງພາບ: __ocs_fmt_0__ + .wblock-saved-block-name = WBLOCK ບັນທຶກ "__ocs_fmt_0__" → "__ocs_fmt_1__" ແລ້ວ + .wblock-save-failed-error = WBLOCK ບັນທຶກຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .web = ເວັບ + .wedge = ຮູບຫຼີ່ມ + .weight = ນ້ຳໜັກ + .welcome = ຍິນດີຕ້ອນຮັບ + .white = ສີຂາວ + .width = ຄວາມກວ້າງ + .width-factor-title-case = ຕົວຄູນຄວາມກວ້າງ + .width-factor-label-title-case = ຕົວຄູນຄວາມກວ້າງ: + .width-factor-sentence-case = ຕົວຄູນຄວາມກວ້າງ + .window = ປ່ອງຢ້ຽມ + .wipeout = ປິດບັງພື້ນທີ່ + .wireframe = ໂຄງລວດ + .with-text = ພ້ອມຂໍ້ຄວາມ + .world = ໂລກ (World) + .xattach-e = XATTACH: __ocs_fmt_0__ + .xline = ເສັ້ນຮ່າງບໍ່ມີສິ້ນສຸດ (XLine) + .xref-not-found = XREF ບໍ່ພົບ: "__ocs_fmt_0__" (__ocs_fmt_1__) + .xref-unloaded-skipped = XREF ຖອດໂຫຼດແລ້ວ (ຂ້າມ): "__ocs_fmt_0__" + .yards = ຫລາ + .yellow = ສີເຫຼືອງ + .yes = ແມ່ນ + .zero-suppress-dimaltz = ຕັດເລກສູນ (DIMALTZ) + .zero-suppress-dimzin = ຕັດເລກສູນ (DIMZIN) + .zoom-extents = ຊູມຂອບເຂດທັງໝົດ + .zoom-in = ຊູມເຂົ້າ + .zoom-out = ຊູມອອກ + .zoom-window = ຊູມຕາມປ່ອງຢ້ຽມ + .command-lowercase = ຄຳສັ່ງ + .e-g-arial = ຕົວຢ່າງ: Arial + .linetype-lowercase = ປະເພດເສັ້ນ + .lineweight-lowercase = ຄວາມໜາເສັ້ນ + .object-lowercase = ວັດຖຸ + .options-lowercase = ຕົວເລືອກ + .units = ຫົວໜ່ວຍ + .count-object-s-added-to-selection = ເພີ່ມ __ocs_fmt_0__ ວັດຖຸ ເຂົ້າໃນການເລືອກແລ້ວ. + .count-object-s-pasted = ວາງແລ້ວ __ocs_fmt_0__ ວັດຖຸ. + .label-count-noun-created = __ocs_fmt_0__: ສ້າງ __ocs_fmt_1__ __ocs_fmt_2__ ແລ້ວ. + .acquired = ໄດ້ຮັບ __ocs_fmt_0__ ແລ້ວ. + .alias-es-applied = ນຳໃຊ້ __ocs_fmt_0__ ຊື່ຫຍໍ້ແລ້ວ. + .group-s-dissolved = ຍົກເລີກກຸ່ມ __ocs_fmt_0__ ກຸ່ມແລ້ວ. + .layer-s-turned = __ocs_fmt_0__ ເລເຢີ ຖືກປ່ຽນເປັນ __ocs_fmt_1__ + .layer-s = __ocs_fmt_0__ ເລເຢີ __ocs_fmt_1__ + .mod-shortcuts = ── ປຸ່ມລັດ __ocs_arg_MOD__ ────────────────────────────────────── + .custom-overrides-shortcuts-set = ── ການປັບປ່ຽນຄ່າເອງ (SHORTCUTS SET) ────────────────── + .function-keys = ── ປຸ່ມຟັງຊັນ ────────────────────────────────────── + .other-keys = ── ປຸ່ມອື່ນໆ ────────────────────────────────────────── + .prefix = ຄຳນຳໜ້າ + .suffix = ຄຳຕໍ່ທ້າຍ + + .crossing-specify-first-corner = Crossing: ກຳນົດມຸມທຳອິດ. + .window-specify-first-corner = Window: ກຳນົດມຸມທຳອິດ. + .add-mode-picks-join-the-selection = ໂໝດເພີ່ມ: ການເລືອກຈະຖືກເພີ່ມເຂົ້າຊຸດທີ່ເລືອກ. + .remove-mode-picks-leave-the-selection = ໂໝດລົບ: ການເລືອກຈະຖືກນຳອອກຈາກຊຸດທີ່ເລືອກ. + .no-editable-objects-selected = ບໍ່ມີວັດຖຸທີ່ແກ້ໄຂໄດ້ຖືກເລືອກ. + .object-s-found = ພົບ __ocs_fmt_0__ ວັດຖຸ. + .the-cell-is-content-locked = ຫ້ອງນີ້ຖືກລັອກເນື້ອໃນໄວ້. + .no-editable-table-cell-picked = ບໍ່ໄດ້ເລືອກຫ້ອງຕາຕະລາງທີ່ແກ້ໄຂໄດ້. + .extrude-path-entity-not-found = EXTRUDE: ບໍ່ພົບອົງປະກອບເສັ້ນທາງ. + .extrude-created-object-s-source-s-could-not-be-extruded = EXTRUDE: ສ້າງແລ້ວ __ocs_arg_created__ ວັດຖຸ; __ocs_arg_failed__ ຕົ້ນສະບັບບໍ່ສາມາດດຶງຢືດຂຶ້ນຮູບໄດ້. + .extrude-no-selected-object-could-be-extruded-with-the-requested-options = EXTRUDE: ບໍ່ມີວັດຖຸທີ່ເລືອກສາມາດດຶງຢືດຂຶ້ນຮູບຕາມຕົວເລືອກທີ່ລະບຸໄດ້. + .sweep-created-object-s-source-s-could-not-be-swept = SWEEP: ສ້າງແລ້ວ __ocs_fmt_0__ ວັດຖຸ; __ocs_fmt_1__ ຕົ້ນສະບັບບໍ່ສາມາດກວາດຂຶ້ນຮູບໄດ້. + .sweep-could-not-sweep-the-profile-along-the-path = SWEEP: ບໍ່ສາມາດກວາດໜ້າຕັດໄປຕາມເສັ້ນທາງໄດ້. + .loft-could-not-create-a-complete-display-the-source-sections-were-preserved = LOFT ບໍ່ສາມາດສ້າງການສະແດງຜົນທີ່ສົມບູນໄດ້. ໜ້າຕັດຕົ້ນສະບັບຍັງຖືກຮັກສາໄວ້. + .loft-surface-created = LOFT: ສ້າງພື້ນຜິວແລ້ວ. + .loft-solid-created = LOFT: ສ້າງຮູບຊົງຕັນແລ້ວ. + .attsync-no-editable-block-references = ATTSYNC: ບໍ່ມີການອ້າງອີງບລັອກທີ່ແກ້ໄຂໄດ້. + .xref-reloaded-with-repairs = XREF ໂຫຼດໃໝ່ພ້ອມການສ້ອມແປງ: "__ocs_fmt_0__" + .xref-reload-failed = XREF ໂຫຼດໃໝ່ຫຼົ້ມເຫຼວ: "__ocs_fmt_0__" (__ocs_fmt_1__) + .zoom-previous = ຊູມມຸມມອງກ່ອນໜ້າ + .zoom-no-previous-view = ZOOM: ບໍ່ມີມຸມມອງກ່ອນໜ້າ. + .zoom-object = ຊູມວັດຖຸ + .zoom-selected-objects-have-no-visible-bounds = ZOOM: ວັດຖຸທີ່ເລືອກບໍ່ມີຂອບເຂດທີ່ເບິ່ງເຫັນໄດ້. + .zoom-dynamic-drag-horizontally-to-pan-and-vertically-to-zoom-press-esc-to-exit = ZOOM Dynamic: ລາກຕາມລວງນອນເພື່ອເລື່ອນພາບ ແລະ ຕາມລວງຕັ້ງເພື່ອຊູມ. ກົດ Esc ເພື່ອອອກ. + .redraw-viewport-refreshed = REDRAW: ໂຫຼດວິວພອດຄືນໃໝ່ແລ້ວ. + .redrawall-viewports-refreshed = REDRAWALL: ໂຫຼດທຸກວິວພອດຄືນໃໝ່ແລ້ວ. + .regen-regenerated-model = REGEN: ສ້າງໂມເດວໃໝ່ແລ້ວ. + .isoplane-expected-left-top-or-right = ISOPLANE: ຕ້ອງການ Left, Top, ຫຼື Right. + .isodraft-expected-on-or-off = ISODRAFT: ຕ້ອງການ On ຫຼື Off. + .xdata-selected-entities-are-on-locked-layers = XDATA: ອົງປະກອບທີ່ເລືອກຢູ່ໃນເລເຢີທີ່ລັອກໄວ້. + .adjust-no-raster-images-selected-or-unknown-property-use-brightness-contrast-fade = ADJUST: ບໍ່ມີຮູບພາບຣາສເຕີຖືກເລືອກ, ຫຼື ບໍ່ຮູ້ຈັກຄຸນສົມບັດ (ໃຊ້ BRIGHTNESS|CONTRAST|FADE). + .annoautoscale-enter-an-integer-from-4-through-4 = ANNOAUTOSCALE: ປ້ອນຈຳນວນຖ້ວນແຕ່ -4 ຫາ 4. + .usage-datalink-path-to-csv = ວິທີໃຊ້: DATALINK <ເສັ້ນທາງໄຟລ໌-.csv> + .datalinkupdate-no-linked-tables-found = DATALINKUPDATE: ບໍ່ພົບຕາຕະລາງທີ່ເຊື່ອມໂຍງ. + .datalinkupdate-wrote-linked-source-s = DATALINKUPDATE: ຂຽນແລ້ວ __ocs_fmt_0__ ແຫຼ່ງເຊື່ອມໂຍງ. + .datalinkupdate-linked-sources-could-not-be-read = DATALINKUPDATE: ບໍ່ສາມາດອ່ານແຫຼ່ງເຊື່ອມໂຍງໄດ້. + .datalinkupdate-updated-linked-table-s = DATALINKUPDATE: ອັບເດດແລ້ວ __ocs_fmt_0__ ຕາຕະລາງທີ່ເຊື່ອມໂຍງ. + .usage-landxmlimport-path-to-xml-imports-cgpoint-survey-points = ວິທີໃຊ້: LANDXMLIMPORT <ເສັ້ນທາງໄຟລ໌-.xml> (ນຳເຂົ້າຈຸດສຳຫຼວດ CgPoint) + .centerreset-center-object-s-updated = CENTERRESET: ອັບເດດແລ້ວ __ocs_fmt_0__ ວັດຖຸຈຸດໃຈກາງ. + .centerreassociate-center-object-s-associated = CENTERREASSOCIATE: ເຊື່ອມໂຍງແລ້ວ __ocs_fmt_0__ ວັດຖຸຈຸດໃຈກາງ. + .centerdisassociate-center-object-s-detached = CENTERDISASSOCIATE: ຕັດການເຊື່ອມໂຍງແລ້ວ __ocs_fmt_0__ ວັດຖຸຈຸດໃຈກາງ. + .need-saveas-no-file-path-yet = ; __ocs_fmt_0__ ຕ້ອງໃຊ້ SAVEAS (ຍັງບໍ່ມີເສັ້ນທາງໄຟລ໌) + .model-space-background-set-to-match-theme = ພື້ນຫຼັງພື້ນທີ່ໂມເດວຖືກຕັ້ງໃຫ້ກົງກັບຮູບແບບສີ. + .model-space-background-set-to-classic-cad-dark = ພື້ນຫຼັງພື້ນທີ່ໂມເດວຖືກຕັ້ງເປັນ Classic CAD Dark. + .paper-desk-surround-background-reset-to-default = ພື້ນຫຼັງຂອບໂຕະເຈ້ຍຖືກຕັ້ງຄືນເປັນຄ່າເລີ່ມຕົ້ນ. + .desk-surround-background-rgb = ພື້ນຫຼັງຂອບໂຕະ: rgb(__ocs_fmt_0__, __ocs_fmt_1__, __ocs_fmt_2__) + .usage-background-desk-r-g-b-default = ວິທີໃຊ້: BACKGROUND DESK | DEFAULT + .usage-background-r-g-b-0-255-theme-classic-default-desk-black-darkgray-gray-lightgray = ວິທີໃຊ້: BACKGROUND (0–255) | THEME|CLASSIC|DEFAULT|DESK|BLACK|DARKGRAY|GRAY|LIGHTGRAY|WHITE + .flatten-entity-ies-moved-to-z-0-unchanged-or-unsupported = FLATTEN: ຍ້າຍແລ້ວ __ocs_fmt_0__ ອົງປະກອບ ໄປທີ່ Z=0; __ocs_fmt_1__ ບໍ່ປ່ຽນແປງ ຫຼື ບໍ່ຮອງຮັບ. + .ucs-aligned-to-the-current-view = ຈັດແນວ UCS ໃຫ້ກົງກັບມຸມມອງປັດຈຸບັນແລ້ວ. + .ucs-defined-from-three-points = ກຳນົດ UCS ຈາກສາມຈຸດແລ້ວ. + .ucs-points-must-define-two-non-collinear-axes = ຈຸດ UCS ຕ້ອງກຳນົດສອງແກນທີ່ບໍ່ຢູ່ໃນເສັ້ນຊື່ດຽວກັນ. + .ucs-requires-origin-x-axis-point-and-xy-plane-point = UCS ຕ້ອງການຈຸດກຳເນີດ, ຈຸດແກນ X ແລະ ຈຸດລະນາບ XY. + .dwgunits-current-unit = DWGUNITS ຫົວໜ່ວຍປັດຈຸບັນ: __ocs_fmt_0__ + .laytrans = LAYTRANS: __ocs_fmt_0__. + .laytrans-no-layer-names-in-common-use-laytrans-with-no-file-to-map-them = LAYTRANS: ບໍ່ມີຊື່ເລເຢີທີ່ກົງກັນ — ໃຊ້ LAYTRANS ໂດຍບໍ່ມີໄຟລ໌ເພື່ອຈັບຄູ່ພວກມັນ. + .dwgunits-not-a-unit-this-drawing-can-use = DWGUNITS: ບໍ່ແມ່ນຫົວໜ່ວຍທີ່ຮູບແຕ້ມນີ້ສາມາດໃຊ້ໄດ້. + .dwgunits-already-in = DWGUNITS: ເປັນ __ocs_fmt_0__ ຢູ່ແລ້ວ. + .dwgunits-now-nothing-was-converted-has-no-size-to-convert-from = DWGUNITS: ຕອນນີ້ເປັນ __ocs_fmt_0__. ບໍ່ມີສິ່ງໃດຖືກປ່ຽນ — __ocs_fmt_1__ ບໍ່ມີຂະໜາດທີ່ຈະປ່ຽນ. + .dwgunits-to-object-s-scaled-by-viewport-s-reframed = DWGUNITS: __ocs_fmt_0__ ຫາ __ocs_fmt_1__ — __ocs_fmt_2__ ວັດຖຸ ຖືກປັບຂະໜາດໂດຍ __ocs_fmt_3__, __ocs_fmt_4__ ວິວພອດ ຖືກຈັດຂອບໃໝ່. + .laytrans-saved-mapping-s = LAYTRANS: ບັນທຶກແລ້ວ __ocs_fmt_0__ ການຈັບຄູ່. + .laytrans-could-not-save = LAYTRANS: ບໍ່ສາມາດບັນທຶກໄດ້ — __ocs_fmt_0__. + .laytrans-could-not-read = LAYTRANS: ບໍ່ສາມາດອ່ານໄດ້ — __ocs_fmt_0__. + .laytrans-loaded-mapping-s = LAYTRANS: ໂຫຼດແລ້ວ __ocs_fmt_0__ ການຈັບຄູ່. + .laytrans-log-written-to = LAYTRANS: ຂຽນບັນທຶກເຫດການໄປທີ່ __ocs_fmt_0__ ແລ້ວ. + .laytrans-could-not-write-log = LAYTRANS: ບໍ່ສາມາດຂຽນບັນທຶກເຫດການໄດ້ — __ocs_fmt_0__. + .laytrans-skipped = LAYTRANS: ຂ້າມ "__ocs_fmt_0__" — __ocs_fmt_1__. + .laytrans-translated-layer-s-object-s = LAYTRANS: ແປງແລ້ວ __ocs_fmt_0__ ເລເຢີ, __ocs_fmt_1__ ວັດຖຸ. + .expected-an-integer-from-0-to = __ocs_fmt_0__: ຕ້ອງການຈຳນວນຖ້ວນແຕ່ 0 ຫາ __ocs_fmt_1__. + .dimcontinuemode = DIMCONTINUEMODE = __ocs_fmt_0__ + .current-color-scheme = ໂຄງສີປັດຈຸບັນ: <__ocs_fmt_0__> + .vports-unlock-existing-viewport-layers-first = VPORTS: ປົດລັອກເລເຢີວິວພອດທີ່ມີຢູ່ກ່ອນ. + .draworder-moved-entities-to-back = DRAWORDER: ຍ້າຍ __ocs_fmt_0__ ອົງປະກອບ ໄປທາງຫຼັງສຸດແລ້ວ. + .draworder-select-objects-then-press-enter = DRAWORDER ເລືອກວັດຖຸ, ແລ້ວກົດ Enter: + .draworder-above-under-front-back-back = DRAWORDER [Above / Under / Front / Back] : + .the-solid-could-not-be-encoded-as-acis = ບໍ່ສາມາດເຂົ້າລະຫັດຮູບຊົງຕັນເປັນ ACIS ໄດ້. + .the-surface-could-not-be-encoded-as-acis = ບໍ່ສາມາດເຂົ້າລະຫັດພື້ນຜິວເປັນ ACIS ໄດ້. + .the-result-could-not-be-encoded-as-acis = ບໍ່ສາມາດເຂົ້າລະຫັດຜົນຮັບເປັນ ACIS ໄດ້. + .the-result-could-not-be-displayed-completely-the-original-solid-was-retained = ບໍ່ສາມາດສະແດງຜົນຮັບໄດ້ຄົບຖ້ວນ. ຮູບຊົງຕັນເດີມຍັງຖືກຮັກສາໄວ້. + .the-solid-could-not-be-updated = ບໍ່ສາມາດອັບເດດຮູບຊົງຕັນໄດ້. + .select-a-3d-solid-edge = ເລືອກຂອບຮູບຊົງຕັນ 3D. + .the-solid-geometry-could-not-be-restored = ບໍ່ສາມາດກູ້ຄືນເລຂາຄະນິດຂອງຮູບຊົງຕັນໄດ້. + .select-a-solid-edge = ເລືອກຂອບຮູບຊົງຕັນ. + .edge-operation-failed-use-a-convex-planar-solid-and-a-smaller-value = ການດຳເນີນງານກັບຂອບຫຼົ້ມເຫຼວ. ໃຫ້ໃຊ້ຮູບຊົງຕັນລະນາບແບບໂນນ ແລະ ຄ່າທີ່ນ້ອຍລົງ. + .solid-updated = __ocs_fmt_0__: ອັບເດດຮູບຊົງຕັນແລ້ວ. + .boolean-select-at-least-two-solids = Boolean: ເລືອກຮູບຊົງຕັນຢ່າງໜ້ອຍ 2 ອັນ. + .boolean-failed-while-combining-the-selected-solids = ບູນີນຫຼົ້ມເຫຼວຂະນະຮວມຮູບຊົງຕັນທີ່ເລືອກ. + .the-boolean-result-could-not-be-encoded-as-acis = ບໍ່ສາມາດເຂົ້າລະຫັດຜົນຮັບບູນີນເປັນ ACIS ໄດ້. + .subtract-select-at-least-one-base-solid-and-one-cutter-solid = SUBTRACT: ເລືອກຮູບຊົງຕັນຫຼັກຢ່າງໜ້ອຍ 1 ອັນ ແລະ ຮູບຊົງຕັນສຳລັບຕັດ 1 ອັນ. + .subtract-failed-while-combining-the-base-solids = SUBTRACT ຫຼົ້ມເຫຼວຂະນະຮວມຮູບຊົງຕັນຫຼັກ. + .subtract-failed-while-removing-the-selected-solids = SUBTRACT ຫຼົ້ມເຫຼວຂະນະຕັດຮູບຊົງຕັນທີ່ເລືອກອອກ. + .value-3drotate-could-not-turn-the-solid = 3DROTATE: ບໍ່ສາມາດໝຸນຮູບຊົງຕັນໄດ້. + .value-3dmirror-could-not-mirror-the-solid = 3DMIRROR: ບໍ່ສາມາດສະທ້ອນຮູບຊົງຕັນໄດ້. + .value-3dalign-could-not-align-the-solid = 3DALIGN: ບໍ່ສາມາດຈັດແນວຮູບຊົງຕັນໄດ້. + .presspull-object-s-updated = PRESSPULL: ອັບເດດແລ້ວ __ocs_fmt_0__ ວັດຖຸ. + .incomplete-shortcut-row-discarded = ຍົກເລີກແຖວປຸ່ມລັດທີ່ບໍ່ສົມບູນ. + .duplicate-shortcut-s-ignored-on-apply = ລະເລີຍປຸ່ມລັດທີ່ຊ້ຳກັນເມື່ອນຳໃຊ້: __ocs_fmt_0__ + .shortcut-s-applied = ນຳໃຊ້ແລ້ວ __ocs_fmt_0__ ປຸ່ມລັດ. + .expected-cartesian-polar-cylindrical-or-spherical-coordinates-or-a-number-got = ຕ້ອງການພິກັດແບບຄາຣ໌ທີຊຽນ, ໂພລາຣ໌, ຊົງກະບອກ, ຊົງກົມ ຫຼື ຕົວເລກ; ແຕ່ໄດ້ຮັບ: "__ocs_fmt_0__" + .specify-point-or-enter-distance = ກຳນົດຈຸດ ຫຼື ປ້ອນໄລຍະຫ່າງ: + .unknown-visual-style = ບໍ່ຮູ້ຈັກຮູບແບບການສະແດງພາບ "__ocs_fmt_0__". + .all-files = ໄຟລ໌ທັງໝົດ + .select-drawing-to-insert-as-block = ເລືອກຮູບແຕ້ມທີ່ຈະແຊກເປັນບລັອກ + .dwg-dxf-files = ໄຟລ໌ DWG/DXF + .inserting-from-file = ກຳລັງແຊກ "__ocs_fmt_0__" ຈາກໄຟລ໌. + .insert-file = INSERT FILE: __ocs_fmt_0__ + .xref-recovered-with-warnings = XREF ກູ້ຄືນພ້ອມຄຳເຕືອນ: "__ocs_fmt_0__" + .xref-recovery-failed = XREF ການກູ້ຄືນຫຼົ້ມເຫຼວ: "__ocs_fmt_0__" (__ocs_fmt_1__) + .select-at-least-one-layout = ເລືອກຢ່າງໜ້ອຍ 1 ເລເອົາຕ໌. + .printing-is-not-available-in-the-web-version = ການພິມບໍ່ມີໃຫ້ໃຊ້ງານໃນເວີຊັນເວັບ. + .sending-selected-layouts-to-the-system-printer = ກຳລັງສົ່ງເລເອົາຕ໌ທີ່ເລືອກໄປຍັງເຄື່ອງພິມຂອງລະບົບ… + .save-plot-style-table = ບັນທຶກຕາຕະລາງຮູບແບບການພລັອດ + .plot-style-files = ໄຟລ໌ຮູບແບບການພລັອດ + .opened-drawing-but-recent-copy-could-not-be-stored = ເປີດຮູບແຕ້ມແລ້ວ, ແຕ່ບໍ່ສາມາດບັນທຶກສຳເນົາຫຼ້າສຸດໄດ້: __ocs_fmt_0__ + .select-pdf-underlay = ເລືອກໄຟລ໌ຮອງພື້ນ PDF + .pdf-files = ໄຟລ໌ PDF + .dwf-files = ໄຟລ໌ DWF + .dgn-files = ໄຟລ໌ DGN + .select-dwf-file = ເລືອກໄຟລ໌ DWF + .select-dgn-file = ເລືອກໄຟລ໌ DGN + .pdfattach = PDFATTACH: __ocs_fmt_0__ + .select-external-reference-file = ເລືອກໄຟລ໌ອ້າງອີງພາຍນອກ + .cad-files = ໄຟລ໌ CAD + .dwg-files = ໄຟລ໌ DWG + .dxf-files = ໄຟລ໌ DXF + .backup-files = ໄຟລ໌ສຳຮອງ + .save-block-as = ບັນທຶກບລັອກເປັນ + .save-data-extraction = ບັນທຶກການສະກັດຂໍ້ມູນ + .csv = CSV + .export-stl = ສົ່ງອອກ STL + .stl-files = ໄຟລ໌ STL + .export-step-ap203 = ສົ່ງອອກ STEP AP203 + .step-files = ໄຟລ໌ STEP + .import-obj-mesh = ນຳເຂົ້າ OBJ Mesh + .wavefront-obj = Wavefront OBJ + .layer-s-set-to = __ocs_fmt_0__ ເລເຢີ ຖືກຕັ້ງເປັນ __ocs_fmt_1__ + .isometric-plane = ລະນາບໄອໂຊເມຕຣິກ: __ocs_fmt_0__. + .drafting-rotation-reset-to-world-at-0 = ຕັ້ງຄ່າການໝຸນການຂຽນແບບຄືນເປັນ World ທີ່ 0° ແລ້ວ. + .length-format-is-now = ຮູບແບບຄວາມຍາວຕອນນີ້ແມ່ນ __ocs_fmt_0__. + .drawing-unit-is-now-geometry-unchanged-use-dwgunits-to-convert-it = ຫົວໜ່ວຍຮູບແຕ້ມຕອນນີ້ແມ່ນ __ocs_fmt_0__. ເລຂາຄະນິດບໍ່ປ່ຽນແປງ — ໃຊ້ DWGUNITS ເພື່ອແປງມັນ. + .no-objects-are-available-in-this-scope = ບໍ່ມີວັດຖຸໃນຂອບເຂດນີ້. + .this-operator-requires-a-numeric-property = ຕົວດຳເນີນການນີ້ຕ້ອງການຄຸນສົມບັດທີ່ເປັນຕົວເລກ. + .enter-a-valid-number = ປ້ອນຕົວເລກທີ່ຖືກຕ້ອງ. + .choose-a-value = ເລືອກຄ່າ. + .registered-as-a-dwg-dxf-handler = ລົງທະບຽນເປັນຕົວຈັດການ .dwg/.dxf ແລ້ວ. + .no-longer-registered-as-a-file-handler = ບໍ່ໄດ້ລົງທະບຽນເປັນຕົວຈັດການໄຟລ໌ອີກຕໍ່ໄປ. + .file-association-failed = ການເຊື່ອມໂຍງໄຟລ໌ຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .recovery-cancelled = ຍົກເລີກການກູ້ຄືນ: "__ocs_fmt_0__" + .could-not-show-recovery-log = ບໍ່ສາມາດສະແດງບັນທຶກເຫດການການກູ້ຄືນໄດ້: __ocs_fmt_0__ + .pdf-export-is-not-available-in-the-web-version = ການສົ່ງອອກ PDF ບໍ່ມີໃຫ້ໃຊ້ງານໃນເວີຊັນເວັບ. + .the-edited-shape-could-not-be-displayed-the-original-geometry-was-restored = ຮູບຮ່າງທີ່ແກ້ໄຂບໍ່ສາມາດສະແດງໄດ້; ເລຂາຄະນິດເດີມຖືກກູ້ຄືນແລ້ວ. + .no-samples-yet = ຍັງບໍ່ມີຕົວຢ່າງ + .select-color = ເລືອກສີ + .layer-translator = ຕົວແປງເລເຢີ + .geometric-tolerance = ຄ່າຄວາມຄາດເຄື່ອນທາງເລຂາຄະນິດ + .drafting-settings = ການຕັ້ງຄ່າການຂຽນແບບ + .print-all = ພິມທັງໝົດ + .close-text-editor = ປິດຕົວແກ້ໄຂຂໍ້ຄວາມ + .scope = ຂອບເຂດ + .apply-to = ນຳໃຊ້ກັບ: + .candidate-object-s = __ocs_fmt_0__ ວັດຖຸທີ່ເຂົ້າເກນ + .filter = ຕົວກັ່ນຕອງ + .result = ຜົນຮັບ + .include-matching-objects = ຮວມວັດຖຸທີ່ກົງກັນ + .exclude-matching-objects = ຍົກເວັ້ນວັດຖຸທີ່ກົງກັນ + .append-result-to-current-selection = ເພີ່ມຜົນຮັບເຂົ້າໃນການເລືອກປັດຈຸບັນ + .open-cad-studio-2d-3d-cad-editor = Open CAD Studio — ໂປຣແກຣມແກ້ໄຂ CAD 2D/3D + .cad-files-to-open-at-startup-dwg-dxf = ໄຟລ໌ CAD ທີ່ຈະເປີດເມື່ອເລີ່ມຕົ້ນ (.dwg / .dxf). + .start-with-a-new-empty-drawing = ເລີ່ມຕົ້ນດ້ວຍຮູບແຕ້ມໃໝ່ທີ່ຫວ່າງເປົ່າ. + .start-a-new-editor-process = ເລີ່ມໂ प्रक्रियाຕົວແກ້ໄຂໃໝ່. + .open-read-only-saving-is-disabled = ເປີດແບບອ່ານຢ່າງດຽວ: ປິດການບັນທຶກ. + .gpu-backend-dx12-vulkan-gl-metal-sets-wgpu-backend = ແບັກເອັນ GPU (dx12, vulkan, gl, metal). ຕັ້ງຄ່າ WGPU_BACKEND. + .gpu-unavailable-rendering-in-software-on-the-cpu = ບໍ່ມີ GPU — ປະມວນຜົນດ້ວຍຊອບແວເທິງ CPU (__ocs_fmt_0__). ຮູບແຕ້ມຂະໜາດໃຫຍ່ຈະເລື່ອນຊ້າ; ກວດສອບໄດຣເວີກຣາຟິກ (ສາເຫດທົ່ວໄປແມ່ນການອັບເດດໄດຣເວີທີ່ຍັງບໍ່ໄດ້ຣີສະຕາດເຄື່ອງ). + .safe-mode-use-the-gl-backend = ໂໝດປອດໄພ: ໃຊ້ແບັກເອັນ GL. + .use-the-renderer-for-gpus-without-shader-storage-buffers = ໃຊ້ຕົວປະມວນຜົນສຳລັບ GPU ທີ່ບໍ່ມີ shader storage buffers. + .run-the-headless-json-automation-server-stdin-stdout-or-port = ເປີດໃຊ້ງານເຊີບເວີອັດຕະໂນມັດ JSON ແບບ headless (stdin/stdout, ຫຼື --port). + .connect-ai-clients-to-the-running-editor-over-mcp-stdio = ເຊື່ອມຕໍ່ AI client ເຂົ້າກັບຕົວແກ້ໄຂທີ່ກຳລັງເຮັດວຽກຜ່ານ MCP stdio. + .tcp-port-for-serve-defaults-to-stdin-stdout = ພອດ TCP ສຳລັບ --serve (ຄ່າເລີ່ມຕົ້ນແມ່ນ stdin/stdout). + .convert-in-to-out-using-the-output-file-extension-then-exit = ແປງ IN ເປັນ OUT ໂດຍໃຊ້ນາມສະກຸນໄຟລ໌ຜົນອອກ, ແລ້ວອອກຈາກໂປຣແກຣມ. + .run-a-command-script-at-startup-one-command-per-line = ເປີດໃຊ້ງານສະຄຣິບຄຳສັ່ງເມື່ອເລີ່ມຕົ້ນ, ໜຶ່ງຄຳສັ່ງຕໍ່ແຖວ. + .log-level-error-warn-info-debug-trace-also-reads-rust-log = ລະດັບບັນທຶກເຫດການ (error|warn|info|debug|trace). ອ່ານຈາກ RUST_LOG ເຊັ່ນກັນ. + .dim-line-angle = ມຸມເສັ້ນບອກຂະໜາດ + .extension-line-angle = ມຸມເສັ້ນຊ່ວຍບອກຂະໜາດ + .arrow-1 = ລູກສອນ 1 + .arrow-2 = ລູກສອນ 2 + .dim-line-linetype = ປະເພດເສັ້ນບອກຂະໜາດ + .ext-line-1-linetype = ປະເພດເສັ້ນຊ່ວຍບອກຂະໜາດ 1 + .ext-line-2-linetype = ປະເພດເສັ້ນຊ່ວຍບອກຂະໜາດ 2 + .fractional-type = ປະເພດເສດສ່ວນ + .text-position-x = ຕຳແໜ່ງຂໍ້ຄວາມ X + .text-position-y = ຕຳແໜ່ງຂໍ້ຄວາມ Y + .text-rotation = ການໝຸນຂໍ້ຄວາມ + .text-view-direction = ທິດທາງມຸມມອງຂໍ້ຄວາມ + .text-override = ຂຽນທັບຂໍ້ຄວາມ + .dim-prefix = ຄຳນຳໜ້າຂະໜາດ + .dim-suffix = ຄຳຕໍ່ທ້າຍຂະໜາດ + .dim-sub-units-suffix = ຄຳຕໍ່ທ້າຍຫົວໜ່ວຍຍ່ອຍຂະໜາດ + .dim-sub-units-scale = ມາດຕາສ່ວນຫົວໜ່ວຍຍ່ອຍຂະໜາດ + .suppress-zero-feet = ຕັດເລກສູນຟຸດ + .suppress-zero-inches = ຕັດເລກສູນນິ້ວ + .alt-sub-units-scale = ມາດຕາສ່ວນຫົວໜ່ວຍຍ່ອຍສຳຮອງ + .alt-round = ປັດເສດສຳຮອງ + .alt-prefix = ຄຳນຳໜ້າສຳຮອງ + .alt-suffix = ຄຳຕໍ່ທ້າຍສຳຮອງ + .alt-sub-units-suffix = ຄຳຕໍ່ທ້າຍຫົວໜ່ວຍຍ່ອຍສຳຮອງ + .alt-suppress-zero-feet = ຕັດເລກສູນຟຸດສຳຮອງ + .alt-suppress-zero-inches = ຕັດເລກສູນນິ້ວສຳຮອງ + .tolerance-alignment = ການຈັດແນວຄ່າຄວາມຄາດເຄື່ອນ + .tolerance-suppress-zero-feet = ຕັດເລກສູນຟຸດຄ່າຄວາມຄາດເຄື່ອນ + .tolerance-suppress-zero-inches = ຕັດເລກສູນນິ້ວຄ່າຄວາມຄາດເຄື່ອນ + .alt-tolerance-precision = ຄວາມລະອຽດຄ່າຄວາມຄາດເຄື່ອນສຳຮອງ + .alt-tolerance-suppress-leading-zeros = ຕັດເລກສູນທາງໜ້າຄ່າຄວາມຄາດເຄື່ອນສຳຮອງ + .alt-tolerance-suppress-trailing-zeros = ຕັດເລກສູນທາງຫຼັງຄ່າຄວາມຄາດເຄື່ອນສຳຮອງ + .alt-tolerance-suppress-zero-feet = ຕັດເລກສູນຟຸດຄ່າຄວາມຄາດເຄື່ອນສຳຮອງ + .alt-tolerance-suppress-zero-inches = ຕັດເລກສູນນິ້ວຄ່າຄວາມຄາດເຄື່ອນສຳຮອງ + .center-location-override-x = ຂຽນທັບຕຳແໜ່ງຈຸດໃຈກາງ X + .center-location-override-y = ຂຽນທັບຕຳແໜ່ງຈຸດໃຈກາງ Y + .jog-location-x = ຕຳແໜ່ງຮອຍຫັກ X + .jog-location-y = ຕຳແໜ່ງຮອຍຫັກ Y + .jog-angle = ມຸມຮອຍຫັກ + .dim-line = ເສັ້ນບອກຂະໜາດ + .ext-line = ເສັ້ນຊ່ວຍບອກຂະໜາດ + .ext-line-weight = ຄວາມໜາເສັ້ນຊ່ວຍບອກຂະໜາດ + .ext-line-type = ປະເພດເສັ້ນຊ່ວຍບອກຂະໜາດ + .color-mode = ໂໝດສີ + .tint-shade = ສີອ່ອນ/ສີເຂັ້ມ + .turn-height = ຄວາມສູງຮອບກ້ຽວ + .ccw = ທວນເຂັມໂມງ + .cw = ຕາມເຂັມໂມງ + .total-length = ຄວາມຍາວທັງໝົດ + .visible = ເບິ່ງເຫັນໄດ້ + .top-attachment-e7595c = ການຕິດດ້ານເທິງ + .bottom-attachment-28500d = ການຕິດດ້ານລຸ່ມ + .varies = ແຕກຕ່າງກັນ + .title-suppressed = ລະງັບຫົວຂໍ້ + .header-suppressed = ລະງັບຫົວຕາຕະລາງ + .row-height = ຄວາມສູງແຖວ + .horizontal-cell-margin = ໄລຍະຂອບຫ້ອງລວງນອນ + .vertical-cell-margin = ໄລຍະຂອບຫ້ອງລວງຕັ້ງ + .table-overrides = ການຂຽນທັບຄ່າຕາຕະລາງ + .insertion-x = ຈຸດແຊກ X + .insertion-y = ຈຸດແຊກ Y + .insertion-z = ຈຸດແຊກ Z + .cell = ຫ້ອງ + .current-cell = ຫ້ອງປັດຈຸບັນ + .row = ແຖວ + .column = ຖັນ + .cell-style = ຮູບແບບຫ້ອງ + .cell-type = ປະເພດຫ້ອງ + .content-color = ສີເນື້ອໃນ + .data-format = ຮູບແບບຂໍ້ມູນ + .background-fill = ການຕື່ມສີພື້ນຫຼັງ + .left-margin = ໄລຍະຂອບຊ້າຍ + .top-margin = ໄລຍະຂອບເທິງ + .right-margin = ໄລຍະຂອບຂວາ + .bottom-margin = ໄລຍະຂອບລຸ່ມ + .top-border = ຂອບເທິງ + .right-border = ຂອບຂວາ + .bottom-border = ຂອບລຸ່ມ + .left-border = ຂອບຊ້າຍ + .maximum-height = ຄວາມສູງສູງສຸດ + .open-cad-file = ເປີດໄຟລ໌ CAD + .backup-autosave = ສຳຮອງ / ບັນທຶກອັດຕະໂນມັດ + .load-layer-standard = ໂຫຼດມາດຕະຖານເລເຢີ + .drawings-and-standards = ຮູບແຕ້ມ ແລະ ມາດຕະຖານ + .layer-mappings = ການຈັບຄູ່ເລເຢີ + .load-plot-style-table = ໂຫຼດຕາຕະລາງຮູບແບບການພລັອດ + .plot-style-tables = ຕາຕະລາງຮູບແບບການພລັອດ + .ctb-files = ໄຟລ໌ CTB + .select-image-file = ເລືອກໄຟລ໌ຮູບພາບ + .images = ຮູບພາບ + .png = PNG + .jpeg = JPEG + .export-as-pdf = ສົ່ງອອກເປັນ PDF + .extrusion = ການດຶງຢືດຂຶ້ນຮູບ + .taper-angle = ມຸມສອບ + .profile-rotation = ການໝຸນໜ້າຕັດ + .bank = ຄວາມອຽງລ້ຽວ + .twist-along-path = ບິດກ້ຽວຕາມເສັ້ນທາງ + .scale-along-path = ປັບຂະໜາດຕາມເສັ້ນທາງ + .loft-with-path = ລັອບພ້ອມເສັ້ນທາງ + .loft-with-guides = ລັອບພ້ອມເສັ້ນນຳທາງ + .loft-with-cross-sections-only = ລັອບສະເພາະໜ້າຕັດເທົ່ານັ້ນ + .number-of-cross-sections = ຈຳນວນໜ້າຕັດ + .surface-normals = ເວັກເຕີຕັ້ງສາກພື້ນຜິວ + .start-draft-angle = ມຸມລາດເລີ່ມຕົ້ນ + .end-draft-angle = ມຸມລາດສິ້ນສຸດ + .start-magnitude = ຂະໜາດເລີ່ມຕົ້ນ + .end-magnitude = ຂະໜາດສິ້ນສຸດ + .torus-radius = ລັດສະໝີວົງແຫວນ + .tube-radius = ລັດສະໝີທໍ່ + .base-major-radius = ລັດສະໝີຫຼັກຖານ + .base-minor-radius = ລັດສະໝີຮອງຖານ + .top-major-radius = ລັດສະໝີຫຼັກຍອດ + .top-minor-radius = ລັດສະໝີຮອງຍອດ + .opencadstudio-renderer-error-copy-this-into-a-bug-report = ຂໍ້ຜິດພາດຕົວປະມວນຜົນ OpenCADStudio — ຄັດລອກຂໍ້ຄວາມນີ້ໃສ່ລາຍງານບັນຫາ: + .dismiss = ປິດອອກ + .value-8a8de8 = ? + .select-color-8dddf1 = ເລືອກສີ... + .no-recent-colors-yet = ຍັງບໍ່ມີສີຫຼ້າສຸດ + .true-color-rgb = True Color RGB __ocs_fmt_0__, __ocs_fmt_1__, __ocs_fmt_2__ + .index-color = ສີດັດຊະນີ + .true-color = True Color + .standard-colors = ສີມາດຕະຖານ + .cad-color-index-aci-10-249 = CAD Color Index (ACI) 10–249 + .color-family-shade-intensity = ກຸ່ມສີ → · ລະດັບສີ / ຄວາມເຂັ້ມ ↓ + .grayscale-250-255 = ລະດັບສີເທົາ 250–255 + .recently-used = ໃຊ້ຫຼ້າສຸດ + .isometric-drafting = ການຂຽນແບບໄອໂຊເມຕຣິກ + .f5-cycles-the-active-plane = F5 ປ່ຽນລະນາບທີ່ໃຊ້ງານວຽນໄປ. + .units-5430a1 = ຫົວໜ່ວຍ… + .convert-drawing = ແປງຮູບແຕ້ມ… + .open-or-create-a-drawing-to-use-ribbon-commands = ເປີດ ຫຼື ສ້າງຮູບແຕ້ມເພື່ອໃຊ້ຄຳສັ່ງຣິບບອນ. + .units-lunits-how-lengths-are-written-click-to-change = ຫົວໜ່ວຍ (LUNITS) + ຮູບແບບການຂຽນຄວາມຍາວ + ຄລິກເພື່ອປ່ຽນ + .use-object-lineweight = ໃຊ້ຄວາມໜາເສັ້ນຂອງວັດຖຸ + .plot-color = ສີພລັອດ: + .use-object-color = ໃຊ້ສີຂອງວັດຖຸ + .choose-color = ເລືອກສີ… + .reset = ຕັ້ງຄ່າໃໝ່ + .lineweight = ຄວາມໜາເສັ້ນ: + .layers-using-aci = ເລເຢີທີ່ໃຊ້ ACI __ocs_fmt_0__ + .layer-dac1d7 = ເລເຢີ + .layers-e3b816 = ເລເຢີ + .no-layers-use-this-aci-in-the-current-drawing = ບໍ່ມີເລເຢີໃດໃຊ້ ACI ນີ້ໃນຮູບແຕ້ມປັດຈຸບັນ. + .block-palette = ແຖບພາເລັດບລັອກ + .search-blocks = ຄົ້ນຫາບລັອກ… + .drafting-modes = ໂໝດການຂຽນແບບ + .grid-display = ການສະແດງເສັ້ນຕາຕະລາງ + .grid-snap = ການສະແນັບເສັ້ນຕາຕະລາງ + .polar-tracking = ການໄລ່ຕາມແນວໂພລາຣ໌ + .object-snap-tracking = ການໄລ່ຕາມຈຸດເກາະວັດຖຸ + .enable-isometric-drafting = ເປີດໃຊ້ງານການຂຽນແບບໄອໂຊເມຕຣິກ + .f5-cycles-left-top-and-right = F5 ສະຫຼັບ ຊ້າຍ, ເທິງ, ແລະ ຂວາ. + .rotation-67e334 = ການໝຸນ: __ocs_arg_angle__° + .reset-rotation = ຕັ້ງຄ່າການໝຸນໃໝ່ + .object-snap-modes = ໂໝດເກາະຈຸດວັດຖຸ + .enable-object-snap = ເປີດໃຊ້ງານການເກາະຈຸດວັດຖຸ + .zero-at = ສູນທີ່ + .degrees = ອົງສາ + .positive-angles-run-clockwise = ມຸມບວກໄປຕາມເຂັມໂມງ + .insertion-scale = ມາດຕາສ່ວນການແຊກ + .scales-blocks-and-drawings-inserted-from-elsewhere-unitless-inserts-them-unscaled = ປັບຂະໜາດບລັອກ ແລະ ຮູບແຕ້ມທີ່ແຊກມາຈາກບ່ອນອື່ນ. ແບບບໍ່ມີຫົວໜ່ວຍຈະແຊກໂດຍບໍ່ປັບຂະໜາດ. + .sample-output = ຕົວຢ່າງຜົນອອກ + .geometric-characteristic = ລັກສະນະສະເພາະທາງເລຂາຄະນິດ + .tolerance-values = ຄ່າຄວາມຄາດເຄື່ອນ + .datum = ຈຸດອ້າງອີງຫຼັກ (Datum) + .datum-references = ຈຸດອ້າງອີງດາຕຳ + .additional-information = ຂໍ້ມູນເພີ່ມເຕີມ + .projected-height = ຄວາມສູງສາຍແສງ + .projected-tolerance-zone = ເຂດຄວາມຄາດເຄື່ອນສາຍແສງ + .datum-identifier = ຕົວລະບຸດາຕຳ + .no-standard-loaded = ບໍ່ມີມາດຕະຖານຖືກໂຫຼດ. + .map = ຈັບຄູ່ + .map-same = ຈັບຄູ່ອັນດຽວກັນ + .force-objects-to-bylayer = ບັງຄັບວັດຖຸໃຫ້ເປັນ ByLayer + .write-translation-log = ຂຽນບັນທຶກເຫດການການແປງ + .save-mappings = ບັນທຶກການຈັບຄູ່… + .load-mappings = ໂຫຼດການຈັບຄູ່… + .translate = ແປງ + .translate-from = ແປງຈາກ + .translate-to = ແປງເປັນ + .mapping-s = __ocs_arg_n__ ການຈັບຄູ່ + .open-dwg-and-dxf-files-with-open-cad-studio = ເປີດໄຟລ໌ .dwg ແລະ .dxf ດ້ວຍ Open CAD Studio + .also-installs-the-application-and-file-type-icons-the-desktop-shows = ຕິດຕັ້ງໄອຄອນແອັບພລິເຄຊັນ ແລະ ໄອຄອນປະເພດໄຟລ໌ທີ່ສະແດງເທິງເດັສທັອບນຳເຊັ່ນກັນ. + .model-space-appearance = ຮູບລັກສະນະພື້ນທີ່ໂມເດວ + .restore-defaults = ຄືນຄ່າເລີ່ມຕົ້ນ + .canvas-mode = ໂໝດພື້ນຜ້າໃບ + .model-background = ພື້ນຫຼັງໂມເດວ + .paper-background = ພື້ນຫຼັງເຈ້ຍ + .desk-surround = ຂອບໂຕະ + .grid-opacity = ຄວາມທຶບແສງເສັ້ນຕາຕະລາງ + .canvas-background-grid-and-default-line-colors-automatically-adapt-to-the-active-them = ພື້ນຫຼັງຜ້າໃບ, ເສັ້ນຕາຕະລາງ, ແລະ ສີເສັ້ນເລີ່ມຕົ້ນຈະປັບຕາມຮູບແບບສີທີ່ໃຊ້ງານຢູ່ໂດຍອັດຕະໂນມັດ. + .model-space-canvas-remains-locked-to-classic-cad-dark-charcoal-212830 = ຜ້າໃບພື້ນທີ່ໂມເດວຍັງຄົງຖືກລັອກໄວ້ທີ່ສີເທົາເຂັ້ມ CAD ແບບຄລາດສິກ (#212830). + .custom-background-colors-set-above-are-applied-to-model-and-paper-space = ສີພື້ນຫຼັງກຳນົດເອງທີ່ຕັ້ງຂ້າງເທິງຈະຖືກນຳໃຊ້ກັບທັງພື້ນທີ່ໂມເດວ ແລະ ພື້ນທີ່ເຈ້ຍ. + .crosshair = ເສັ້ນກາກະບາດຊີ້ຕຳແໜ່ງ + .crosshair-size = ຂະໜາດເສັ້ນກາກະບາດ + .cursor-type = ປະເພດຕົວກະພິບ + .crosshair-color = ສີເສັ້ນກາກະບາດ + .leave-the-color-blank-to-keep-automatic-viewport-contrast = ປະສີຫວ່າງໄວ້ເພື່ອຮັກສາຄວາມຄົມຊັດຂອງວິວພອດໂດຍອັດຕະໂນມັດ. + .model-display-scale = ມາດຕາສ່ວນສະແດງຜົນໂມເດວ + .changes-the-on-screen-width-in-model-without-affecting-plotted-output = ປ່ຽນຄວາມກວ້າງເທິງໜ້າຈໍໃນໂມເດວໂດຍບໍ່ມີຜົນຕໍ່ຜົນອອກຕອນພລັອດ. + .selection = ການເລືອກ + .pick-box-size = ຂະໜາດກ່ອງເລືອກ + .controls-both-the-visible-selection-box-and-the-click-aperture = ຄວບຄຸມທັງກ່ອງເລືອກທີ່ເບິ່ງເຫັນ ແລະ ຊ່ອງຮັບການຄລິກ. + .visual-effect-settings = ການຕັ້ງຄ່າເອັບເຟັກພາບ + .applications = ແອັບພລິເຄຊັນ + .installed-plugins-and-their-sources = ປລັກອິນທີ່ຕິດຕັ້ງ ແລະ ແຫຼ່ງທີ່ມາ + .plugins-2 = ປລັກອິນ… + .keyboard-shortcuts-2 = ປຸ່ມລັດແປ້ນພິມ + .keyboard-shortcuts-3 = ປຸ່ມລັດແປ້ນພິມ… + .command-aliases-2 = ຊື່ຫຍໍ້ຄຳສັ່ງ + .command-aliases-3 = ຊື່ຫຍໍ້ຄຳສັ່ງ… + .file-safety = ຄວາມປອດໄພຂອງໄຟລ໌ + .automatic-save = ບັນທຶກອັດຕະໂນມັດ + .minutes-between-recovery-saves-to-a-sv-file-0-turns-it-off-savetime = ນາທີລະຫວ່າງການບັນທຶກກູ້ຄືນໄປຍັງໄຟລ໌ .sv$; 0 ແມ່ນປິດ (SAVETIME). + .keep-a-bak-copy-when-overwriting-a-drawing-isavebak = ຮັກສາສຳເນົາ .bak ໄວ້ເມື່ອຂຽນທັບຮູບແຕ້ມ (ISAVEBAK) + .plotting = ການພລັອດ + .plot-device-paper-scale-and-plot-styles = ອຸປະກອນພລັອດ, ເຈ້ຍ, ມາດຕາສ່ວນ ແລະ ຮູບແບບການພລັອດ + .plot-and-page-setup = ການພລັອດ ແລະ ການຕັ້ງຄ່າໜ້າເຈ້ຍ… + .fill-truetype-glyphs-textfill = ຕື່ມສີຕົວອັກສອນ TrueType (TEXTFILL) + .command-line = ແຖວຄຳສັ່ງ + .prompt-lines = ແຖວຂໍ້ຄວາມເຕືອນ + .temporary-prompt-lines-shown-above-the-command-window-clipromptlines = ແຖວຂໍ້ຄວາມເຕືອນຊົ່ວຄາວທີ່ສະແດງເໜືອປ່ອງຢ້ຽມຄຳສັ່ງ (CLIPROMPTLINES). + .history-fade-time = ເວລາຈາງຫາຍຂອງປະຫວັດ + .how-long-overlay-history-lines-stay-visible-0-never-fades-commandlinefadetime = ໄລຍະເວລາທີ່ແຖວປະຫວັດສະແດງຄ້າງໄວ້; 0 ໝາຍເຖິງບໍ່ຈາງຫາຍ (COMMANDLINEFADETIME). + .never = ບໍ່ເຄີຍ + .clicking-overlapping-objects-opens-a-picker = ການຄລິກວັດຖຸທີ່ຊ້ອນກັນຈະເປີດໜ້າຕ່າງເລືອກ + .block-edit = ແກ້ໄຂບລັອກ + .double-click-to-edit-block-in-place-refedit = ດັບເບິລຄລິກເພື່ອແກ້ໄຂບລັອກໃນບ່ອນ (REFEDIT) + .double-click-attributed-blocks-to-edit-attributes-attedit = ດັບເບິລຄລິກບລັອກທີ່ມີຄຸນລັກສະນະເພື່ອແກ້ໄຂຄຸນລັກສະນະ (ATTEDIT) + .user-preferences = ການຕັ້ງຄ່າສ່ວນຕົວຂອງຜູ້ໃຊ້ + .zoom = ຊູມ + .reverse-mouse-wheel-zoom-zoomwheel = ສະຫຼັບທິດທາງການໝຸນລໍ້ເມົາສ໌ຊູມ (ZOOMWHEEL) + .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = ທັດແພດ: ເລື່ອນເພື່ອຍ້າຍມຸມມອງ, Ctrl+ເລື່ອນ ຫຼື ບີບເພື່ອຊູມ + .interface-scale = ຂະໜາດສ່ວນຕິດຕໍ່ + .zoom-factor = ຕົວຄູນການຊູມ + .how-far-one-wheel-notch-zooms-zoomfactor = ໄລຍະຊູມຕໍ່ການໝຸນລໍ້ເມົາສ໌ໜຶ່ງຄລິກ (ZOOMFACTOR). + .text-and-dimensions = ຂໍ້ຄວາມ ແລະ ຂະໜາດ + .textedit-edits-one-object-and-ends-texteditmode = TEXTEDIT ແກ້ໄຂວັດຖຸ 1 ອັນແລ້ວສິ້ນສຸດ (TEXTEDITMODE) + .continued-dimensions-inherit-the-base-dimension-s-layer-and-style-dimcontinuemode = ເສັ້ນບອກຂະໜາດຕໍ່ເນື່ອງຈະສືບທອດເລເຢີ ແລະ ຮູບແບບຈາກເສັ້ນບອກຂະໜາດຫຼັກ (DIMCONTINUEMODE) + .qdim-origin-priority = ລຳດັບຄວາມສຳຄັນຂອງຈຸດກຳເນີດ QDIM + .which-points-qdim-measures-from-also-settable-inside-the-command = ຈຸດທີ່ QDIM ວັດແທກອອກໄປ. ສາມາດຕັ້ງຄ່າພາຍໃນຄຳສັ່ງໄດ້ເຊັ່ນກັນ. + .add-scales-automatically = ເພີ່ມມາດຕາສ່ວນໂດຍອັດຕະໂນມັດ + .which-annotative-objects-pick-up-a-newly-set-annotation-scale-annoautoscale = ວັດຖຸຄຳອະທິບາຍໃດແດ່ທີ່ຈະຮັບມາດຕາສ່ວນຄຳອະທິບາຍທີ່ຕັ້ງໃໝ່ (ANNOAUTOSCALE). + .length-angle-and-insertion-units = ຫົວໜ່ວຍຄວາມຍາວ, ມຸມ ແລະ ການແຊກ + .drawing-units-2 = ຫົວໜ່ວຍຮູບແຕ້ມ… + .drafting = ການຂຽນແບບ + .drafting-rotation = ການໝຸນການຂຽນແບບ + .rotates-the-crosshair-and-the-snap-grid-in-the-active-ucs-snapang = ໝຸນເສັ້ນກາກະບາດ ແລະ ເສັ້ນຕາຕະລາງສະແນັບໃນ UCS ທີ່ໃຊ້ງານຢູ່ (SNAPANG). + .polar-tracking-increment = ໄລຍະເພີ່ມການໄລ່ຕາມແນວໂພລາຣ໌ + .snap-and-grid = ສະແນັບ ແລະ ເສັ້ນຕາຕະລາງ + .snap-modes-grid-spacing-and-object-snap = ໂໝດສະແນັບ, ໄລຍະຫ່າງເສັ້ນຕາຕະລາງ ແລະ ການເກາະຈຸດວັດຖຸ + .drafting-settings-2 = ການຕັ້ງຄ່າການຂຽນແບບ… + .modeling-3d = ການສ້າງແບບຈຳລອງ 3D + .display-tools = ເຄື່ອງມືການສະແດງຜົນ + .show-the-navigation-cube-navvcube = ສະແດງກ້ອນນຳທາງ (NAVVCUBE) + .show-the-ucs-icon-ucsicon = ສະແດງໄອຄອນ UCS (UCSICON) + .place-the-ucs-icon-at-the-origin-ucsicon-origin = ວາງໄອຄອນ UCS ໄວ້ທີ່ຈຸດກຳເນີດ (UCSICON ORigin) + .display-resolution = ຄວາມລະອຽດການສະແດງຜົນ + .applies-to-the-current-drawing = ນຳໃຊ້ກັບຮູບແຕ້ມປັດຈຸບັນ + .isolines-per-surface = ເສັ້ນໄອໂຊລາຍຕໍ່ພື້ນຜິວ + .show-silhouette-edges-on-solids-dispsilh = ສະແດງຂອບໂຄງຮ່າງເທິງຮູບຊົງຕັນ (DISPSILH) + .surface-density-u = ຄວາມໜາແໜ້ນພື້ນຜິວ U + .surface-density-v = ຄວາມໜາແໜ້ນພື້ນຜິວ V + .surface-type = ປະເພດພື້ນຜິວ + .record-the-history-of-composite-solids-solidhist = ບັນທຶກປະຫວັດຂອງຮູບຊົງຕັນປະກອບ (SOLIDHIST) + .show-solid-history = ສະແດງປະຫວັດຮູບຊົງຕັນ + .not-available = ບໍ່ພ້ອມໃຊ້ງານ + .open-folder = ເປີດໂຟນເດີ + .where-the-application-keeps-its-own-files-these-locations-are-fixed = ບ່ອນທີ່ແອັບພລິເຄຊັນຈັດເກັບໄຟລ໌ຂອງຕົນເອງ. ທີ່ຕັ້ງເຫຼົ່ານີ້ຖືກກຳນົດຄົງທີ່. + .configuration = ການກຳນົດຄ່າ + .plot-styles = ຮູບແບບການພລັອດ + .autosave-files = ໄຟລ໌ບັນທຶກອັດຕະໂນມັດ + .selection-modes = ໂໝດການເລືອກ + .use-shift-to-add-to-selection-pickadd = ໃຊ້ Shift ເພື່ອເພີ່ມເຂົ້າໃນການເລືອກ (PICKADD) + .press-and-drag-draws-a-rectangle-instead-of-a-lasso-pickdrag = ກົດຄ້າງແລ້ວລາກຈະແຕ້ມສີ່ແຈສາກແທນເຊືອກບ້ວງ (PICKDRAG) + .show-selection-effect-selectioneffect = ສະແດງເອັບເຟັກການເລືອກ (SELECTIONEFFECT) + .preview-selection-when-no-command-is-active = ເບິ່ງຕົວຢ່າງການເລືອກເມື່ອບໍ່ມີຄຳສັ່ງເຮັດວຽກຢູ່ + .preview-selection-during-a-command = ເບິ່ງຕົວຢ່າງການເລືອກລະຫວ່າງໃຊ້ຄຳສັ່ງ + .highlights-the-object-under-the-cursor-before-it-is-picked-selectionpreview = ເນັ້ນວັດຖຸກ້ອງຕົວກະພິບກ່ອນທີ່ຈະຖືກເລືອກ (SELECTIONPREVIEW). + .object-limit-for-grips = ຈຳນວນວັດຖຸສູງສຸດສຳລັບຈຸດກຣິບ + .unlimited = ບໍ່ຈຳກັດ + .past-this-many-selected-objects-no-grips-are-drawn-0-removes-the-limit-gripobjlimit = ເມື່ອເກີນຈຳນວນວັດຖຸທີ່ເລືອກນີ້ຈະບໍ່ສະແດງຈຸດກຣິບ; 0 ແມ່ນຍົກເລີກຂີດຈຳກັດ (GRIPOBJLIMIT). + .indicate-selection-area-with-transparent-fill-selectionarea = ສະແດງພື້ນທີ່ເລືອກດ້ວຍສີຕື່ມແບບໂປ່ງໃສ (SELECTIONAREA) + .selection-area-opacity = ຄວາມທຶບແສງຂອງພື້ນທີ່ເລືອກ + .transparency-of-the-window-and-crossing-selection-areas-selectionareaopacity = ຄວາມໂປ່ງໃສຂອງພື້ນທີ່ເລືອກແບບ Window ແລະ Crossing (SELECTIONAREAOPACITY). + .window-selection-color = ສີການເລືອກແບບ Window + .crossing-selection-color = ສີການເລືອກແບບ Crossing + .selection-highlight-color = ສີການເນັ້ນການເລືອກ + .grip-settings = ການຕັ້ງຄ່າຈຸດກຣິບ + .grip-size = ຂະໜາດຈຸດກຣິບ + .unselected-grip-color = ສີຈຸດກຣິບທີ່ບໍ່ໄດ້ເລືອກ + .selected-hot-grip-color = ສີຈຸດກຣິບທີ່ເລືອກ/ກຳລັງໃຊ້ງານ + .hover-grip-color = ສີຈຸດກຣິບເມື່ອຊີເມົາສ໌ໃສ່ + .plot-with-plot-styles = ພລັອດພ້ອມຮູບແບບການພລັອດ + .display-plot-styles = ສະແດງຮູບແບບການພລັອດ + .edit = ແກ້ໄຂ… + .no-paper-layouts-are-available = ບໍ່ມີເລເອົາຕ໌ເຈ້ຍໃຫ້ໃຊ້ງານ. + .default-printer = ເຄື່ອງພິມເລີ່ມຕົ້ນ + .value-1-layout-selected = ເລືອກ 1 ເລເອົາຕ໌ແລ້ວ + .layouts-selected = ເລເອົາຕ໌ທີ່ເລືອກ + .pdf = PDF + .dwf = DWF + .dgn = DGN + .select-the-layouts-to-output-each-page-is-printed-in-layout-mode = ເລືອກເລເອົາຕ໌ທີ່ຈະສົ່ງອອກ. ແຕ່ລະໜ້າຈະຖືກພິມໃນໂໝດເລເອົາຕ໌. + .select-all-1fc9a3 = ເລືອກທັງໝົດ + .select-none = ບໍ່ເລືອກເລີຍ + .printer = ເຄື່ອງພິມ + .options = ຕົວເລືອກ… + .click-add-press-a-key-combination-type-the-command-then-apply-esc-cancels-the-pending = ຄລິກ + ເພີ່ມ, ກົດປຸ່ມປະສົມ, ພິມຄຳສັ່ງ, ແລ້ວກົດ ນຳໃຊ້. Esc ຈະຍົກເລີກແຖວທີ່ກຳລັງລໍຖ້າ. + .press-a-key-combination = ກົດປຸ່ມປະສົມ... + .click-then-press-keys = ຄລິກ, ແລ້ວກົດປຸ່ມ... + .cancel-add-esc = ຍົກເລີກການເພີ່ມ (Esc) + .number-of-shortcuts = ຈຳນວນປຸ່ມລັດ + .are-you-sure-you-want-to-reset-you-will-lose-all-your-current-shortcuts = ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການຕັ້ງຄ່າໃໝ່? ທ່ານຈະສູນເສຍປຸ່ມລັດທັງໝົດໃນປັດຈຸບັນ! + .yes-reset = ແມ່ນແລ້ວ, ຕັ້ງຄ່າໃໝ່ + .reset-to-default = ຕັ້ງຄືນເປັນຄ່າເລີ່ມຕົ້ນ + .apply-exit = ນຳໃຊ້ & ອອກ + .shortcut-already-used-for-command = ປຸ່ມລັດຖືກໃຊ້ກັບຄຳສັ່ງນີ້ແລ້ວ: __ocs_fmt_0__ → __ocs_fmt_1__ + .unsaved-changes-will-be-discarded = ການປ່ຽນແປງທີ່ຍັງບໍ່ໄດ້ບັນທຶກຈະຖືກຍົກເລີກ. + .discard-close = ຍົກເລີກ & ປິດ + .keep-editing = ແກ້ໄຂຕໍ່ໄປ + .mcp-control-is-off = ການຄວບຄຸມ MCP ປິດຢູ່ + .mcp-is-handling-a-request = MCP ກຳລັງຈັດການຄຳຮ້ອງຂໍ + .mcp-control-is-ready = ການຄວບຄຸມ MCP ພ້ອມແລ້ວ + .save-all = ບັນທຶກທັງໝົດ + .close-all = ປິດທັງໝົດ + .close-all-other-drawings = ປິດຮູບແຕ້ມອື່ນໆທັງໝົດ + .copy-full-file-path = ຄັດລອກເສັ້ນທາງໄຟລ໌ແບບເຕັມ + .open-file-location = ເປີດທີ່ຕັ້ງໄຟລ໌ + .style-default = [ຮູບແບບເລີ່ມຕົ້ນ] + .dynamic-auto = ໄດນາມິກອັດຕະໂນມັດ + .dynamic-manual = ໄດນາມິກກຳນົດເອງ + .any-type = (ທຸກປະເພດ) + .any-property = (ທຸກຄຸນສົມບັດ) + .no-blocks-in-this-drawing = ບໍ່ມີບລັອກໃນຮູບແຕ້ມນີ້ + .no-matches = ບໍ່ພົບສິ່ງທີ່ກົງກັນ + .profile-of-a-surface = ໂຄງຮູບຮ່າງຂອງພື້ນຜິວ + .profile-of-a-line = ໂຄງຮູບຮ່າງຂອງເສັ້ນ + .circular-runout = ການແກວ່ງຕົວໜ່ວຍກົມ (Circular runout) + .total-runout = ການແກວ່ງຕົວລວມ (Total runout) + .maximum-material-condition = ສະພາວະເນື້ອວັດສະດຸສູງສຸດ + .least-material-condition = ສະພາວະເນື້ອວັດສະດຸຕ່ຳສຸດ + .regardless-of-feature-size = ບໍ່ຂຶ້ນກັບຂະໜາດຮູບຮ່າງ + .decimal-degrees = ອົງສາທົດສະນິຍົມ + .deg-min-sec-55d08d = ອົງສາ/ລິບດາ/ຟິລິບດາ + .block-palette-9925cd = ແຖບ + ພາເລັດບລັອກ + .attach-pdf = ແນບ PDF + .no-leader = ບໍ່ມີເສັ້ນຊີ້ + .no-leader-5e6b2a = /ບໍ່ມີເສັ້ນຊີ້ + .leader-c5dd1e = /ເສັ້ນຊີ້ + .landing-first = ເສັ້ນພັກກ່ອນ + .select-mtext = ເລືອກ MText + .max-points = ຈຸດສູງສຸດ + .first-angle = ມຸມທຳອິດ + .second-angle = ມຸມທີສອງ + .keep-boundaries-on = ຮັກສາເສັ້ນຂອບ: ເປີດ + .keep-boundaries-off = ຮັກສາເສັ້ນຂອບ: ປິດ + .associative-on = ເຊື່ອມໂຍງ: ເປີດ + .associative-off = ເຊື່ອມໂຍງ: ປິດ + .separate-hatches-on = ແຍກລວດລາຍແຮເງົາ: ເປີດ + .separate-hatches-off = ແຍກລວດລາຍແຮເງົາ: ປິດ + .recreate-boundary = ສ້າງເສັ້ນຂອບຄືນໃໝ່ + .separate-hatches = ແຍກລວດລາຍແຮເງົາ + .draw-front = ແຕ້ມໄວ້ໜ້າ + .draw-back = ແຕ້ມໄວ້ຫຼັງ + .edge-no-extend = ຂອບ: ບໍ່ຕໍ່ຂະຫຍາຍ + .cross-sections-only = ສະເພາະໜ້າຕັດເທົ່ານັ້ນ + .bulge-magnitude = ຂະໜາດຄວາມໂກ່ງ + .first-normal = ເວັກເຕີຕັ້ງສາກທຳອິດ + .last-normal = ເວັກເຕີຕັ້ງສາກສຸດທ້າຍ + .ends-normal = ເວັກເຕີຕັ້ງສາກປາຍ + .all-normal = ເວັກເຕີຕັ້ງສາກທັງໝົດ + .use-draft-angles = ໃຊ້ມຸມລາດ + .wireframe-2d = ໂຄງລວດ 2D + .wireframe-3d = ໂຄງລວດ 3D + .value-0-theme-default = 0: ຄ່າເລີ່ມຕົ້ນຂອງຮູບແບບສີ + .value-7-white-black = 7: ຂາວ/ດຳ + .value-1-red = 1: ແດງ + .value-2-yellow = 2: ເຫຼືອງ + .value-3-green = 3: ຂຽວ + .value-4-cyan = 4: ຟ້າອ່ອນ + .value-5-blue = 5: ຟ້າ + .value-6-magenta = 6: ບົວເຂັ້ມ + .visual-style-wireframe-2d-wireframe-3d-hidden-line-flat-shaded-gouraud-shaded-flat-sh = ຮູບແບບການສະແດງພາບ [Wireframe 2D/Wireframe 3D/Hidden Line/Flat Shaded/Gouraud Shaded/Flat Shaded + Edges/Gouraud Shaded + Edges]: + .open-cad-studio-web-app = ເວັບແອັບ Open CAD Studio + .loading = ກຳລັງໂຫຼດ… + .a-cad-application-for-2d-3d-drawing-and-design = ແອັບພລິເຄຊັນ CAD ສຳລັບການແຕ້ມ ແລະ ອອກແບບ 2D/3D + .value-2d-3d-cad-application-for-dwg-and-dxf-drawings = ແອັບພລິເຄຊັນ CAD 2D/3D ສຳລັບຮູບແຕ້ມ DWG ແລະ DXF + .create-and-edit-technical-drawings-in-dwg-and-dxf-formats = ສ້າງ ແລະ ແກ້ໄຂຮູບແຕ້ມເຕັກນິກໃນຮູບແບບ DWG ແລະ DXF. + .launch-open-cad-studio = ເປີດໃຊ້ງານ Open CAD Studio + .open-cad-studio-installer = ຕົວຕິດຕັ້ງ Open CAD Studio + .dwg-drawing = ຮູບແຕ້ມ DWG + .dxf-drawing = ຮູບແຕ້ມ DXF + .desktop-shortcut = ປຸ່ມລັດເດັສທັອບ + .add-a-shortcut-to-open-cad-studio-on-the-desktop = ເພີ່ມປຸ່ມລັດສຳລັບ Open CAD Studio ເທິງເດັສທັອບ. + .maximum = ສູງສຸດ + .pickbox = ກ່ອງເລືອກວັດຖຸ + .highlight = ເນັ້ນ + .grips = ຈຸດກຣິບ + .unselected = ບໍ່ໄດ້ເລືອກ + .selected = ເລືອກແລ້ວ + .hover = ຊີ້ໃສ່ + .shortcuts = ປຸ່ມລັດ + .count = ຈຳນວນ + .profile = ໜ້າຕັດ + .points = ຈຸດ + .keep-boundaries = ຮັກສາເສັ້ນຂອບ + .black = ສີດຳ + .files = ໄຟລ໌ + .specify = ກຳນົດ + .base = ຖານ + .tube = ທໍ່ + .third-point = ຈຸດທີສາມ + .endpoint-1 = ຈຸດປາຍ 1 + .endpoint-2 = ຈຸດປາຍ 2 + .axis-1 = ແກນ 1 + .axis-2 = ແກນ 2 + .other-endpoint = ຈຸດປາຍອີກເບື້ອງ + .tangent-object-1 = ວັດຖຸສຳຜັດ 1 + .tangent-object-2 = ວັດຖຸສຳຜັດ 2 + .first-corner = ມຸມທຳອິດ + .other-corner = ມຸມອີກດ້ານ + .start-point = ຈຸດເລີ່ມຕົ້ນ + .next-point = ຈຸດຕໍ່ໄປ + .tangent-direction = ທິດທາງສຳຜັດ + .fourth-point = ຈຸດທີສີ່ + .create-triangular-face = ສ້າງໜ້າສາມແຈ + .location = ຕຳແໜ່ງ + .creation-mode = ໂໝດການສ້າງ + .path = ເສັ້ນທາງ + .expression = ນິພົດ + .reference-length = ຄວາມຍາວອ້າງອີງ + .new-length = ຄວາມຍາວໃໝ່ + .point-end = ຈຸດສິ້ນສຸດ + .end = ສິ້ນສຸດ + .cross-sections = ໜ້າຕັດ + .ruled = ຜິວແບບເສັ້ນກົງ (Ruled) + .smooth = ລຽບນຽນ + .guides = ເສັ້ນນຳທາງ + .new-origin = ຈຸດກຳເນີດໃໝ່ + .positive-direction = ທິດທາງບວກ + .tl = TL (ເທິງຊ້າຍ) + .rotated = ໝຸນແລ້ວ + .landing = ສ່ວນພັກ + .any = ໃດໆ + .source = ຕົ້ນສະບັບ + .finish = ສຳເລັດ + .next-leader-point = ຈຸດເສັ້ນຊີ້ຕໍ່ໄປ + .extension-origin-priority = ລຳດັບຄວາມສຳຄັນຂອງຈຸດກຳເນີດເສັ້ນຊ່ວຍບອກຂະໜາດ + .endpoints = ຈຸດປາຍ + .intersections = ຈຸດຕັດ + .distance-between-turns = ໄລຍະຫ່າງລະຫວ່າງຮອບກ້ຽວ + .new-location = ຕຳແໜ່ງໃໝ່ + .center-location-override = ຂຽນທັບຕຳແໜ່ງຈຸດໃຈກາງ + .jog-location = ຕຳແໜ່ງຮອຍຫັກ + .tint = ສີອ່ອນ + .shade = ສີເຂັ້ມ + .total = ລວມ + .overrides = ການຂຽນທັບ + .current = ປັດຈຸບັນ + .margin = ໄລຍະຂອບ + .border = ເສັ້ນຂອບ + .backup = ສຳຮອງ + .autosave = ບັນທຶກອັດຕະໂນມັດ + .mappings = ການຈັບຄູ່ + .image = ຮູບພາບ + .polyline-arc-segment = ທ່ອນເສັ້ນໂຄ້ງໂພລີລາຍ + .partial-arc = ເສັ້ນໂຄ້ງບາງສ່ວນ + .continued-dimension = ເສັ້ນບອກຂະໜາດຕໍ່ເນື່ອງ + .island-style = ຮູບແບບເກາະດອນ (Island style) + .internal-points = ຈຸດພາຍໃນ + .polygon = ຮູບຫຼາຍແຈ + .align = ຈັດແນວ + .draft-angle = ມຸມລາດ + .magnitude = ຂະໜາດ + .color-family = ກຸ່ມສີ + .grayscale = ລະດັບສີເທົາ + .convert = ແປງ + .use-object-value = ໃຊ້ຄ່າຂອງວັດຖຸ + .grid = ເສັ້ນຕາຕະລາງ + .snap = ສະແນັບ + .sample = ຕົວຢ່າງ + .values = ຄ່າ + .references = ຈຸດອ້າງອີງ + .projected = ສາຍແສງ + .tolerance-zone = ເຂດຄວາມຄາດເຄື່ອນ + .target = ເປົ້າໝາຍ + .appearance = ຮູບລັກສະນະ + .canvas = ຜ້າໃບ + .cursor = ຕົວກະພິບ + .visual-effects = ເອັບເຟັກພາບ + .full-file-path = ເສັ້ນທາງໄຟລ໌ແບບເຕັມ + .file-location = ທີ່ຕັ້ງໄຟລ໌ + .manual = ກຳນົດເອງ + .property = ຄຸນສົມບັດ + .first = ທຳອິດ + .recreate = ສ້າງຄືນໃໝ່ + .separate = ແຍກ + .no-extend = ບໍ່ຕໍ່ຂະຫຍາຍ + .ends = ສິ້ນສຸດ + .use = ໃຊ້ + .installer = ຕົວຕິດຕັ້ງ + .desktop = ເດັສທັອບ + .shortcut = ປຸ່ມລັດ + .match-theme = ກົງກັບຮູບແບບສີ + .classic-cad-dark = Classic CAD Dark + .under = ກ້ອງ + .current-space = ພື້ນທີ່ປັດຈຸບັນ + .current-selection = ສິ່ງທີ່ເລືອກປັດຈຸບັນ + .any-value = * ທຸກຄ່າ + .equals = = ເທົ່າກັບ + .not-equal = != ບໍ່ເທົ່າກັບ + .greater-than = > ໃຫຍ່ກວ່າ + .less-than = < ນ້ອຍກວ່າ + .desktop-pointer = ຕົວຊີ້ເດັສທັອບ + .available-themes = ຮູບແບບສີທີ່ມີ: __ocs_fmt_0__ + .top-front = ເທິງ-ໜ້າ + .top-back = ເທິງ-ຫຼັງ + .bot-front = ລຸ່ມ-ໜ້າ + .bot-back = ລຸ່ມ-ຫຼັງ + .bot-right = ລຸ່ມ-ຂວາ + .bot-left = ລຸ່ມ-ຊ້າຍ + .front-right = ໜ້າ-ຂວາ + .front-left = ໜ້າ-ຊ້າຍ + .back-right = ຫຼັງ-ຂວາ + .back-left = ຫຼັງ-ຊ້າຍ + .top-front-right = ເທິງ-ໜ້າ-ຂວາ + .top-front-left = ເທິງ-ໜ້າ-ຊ້າຍ + .top-back-right = ເທິງ-ຫຼັງ-ຂວາ + .top-back-left = ເທິງ-ຫຼັງ-ຊ້າຍ + .bot-front-right = ລຸ່ມ-ໜ້າ-ຂວາ + .bot-front-left = ລຸ່ມ-ໜ້າ-ຊ້າຍ + .bot-back-right = ລຸ່ມ-ຫຼັງ-ຂວາ + .bot-back-left = ລຸ່ມ-ຫຼັງ-ຊ້າຍ + .no-previous-selection-set = ບໍ່ມີຊຸດການເລືອກກ່ອນໜ້າ. + .nothing-to-select = ບໍ່ມີຫຍັງໃຫ້ເລືອກ. + .no-last-object = ບໍ່ມີວັດຖຸຫຼ້າສຸດ. + .mview-object-select-a-closed-paper-space-circle-ellipse-or-polyline = MVIEW Object: ເລືອກວົງມົນ, ວົງຮີ ຫຼື ໂພລີລາຍແບບປິດໃນພື້ນທີ່ເຈ້ຍ. + .centerreassociate-center-mark-associated = CENTERREASSOCIATE: ເຊື່ອມໂຍງໝາຍຈຸດໃຈກາງແລ້ວ. + .centerreassociate-the-selected-source-is-not-circular = CENTERREASSOCIATE: ຕົ້ນສະບັບທີ່ເລືອກບໍ່ແມ່ນຮູບວົງມົນ. + .command-cancelled = ຍົກເລີກຄຳສັ່ງແລ້ວ. + .join-objects-don-t-form-a-single-connected-chain-or-contain-an-unsupported-type- = JOIN: ວັດຖຸບໍ່ໄດ້ຕໍ່ກັນເປັນສາຍດຽວ, ຫຼື ມີປະເພດທີ່ບໍ່ຮອງຮັບ / ເສັ້ນໂຄ້ງອຽງ. + .wblock-select-entities-first-or-wblock-block-name-or-wblock = WBLOCK ເລືອກອົງປະກອບກ່ອນ, ຫຼື: WBLOCK <ຊື່ບລັອກ> ຫຼື WBLOCK * + .usage-justifytext-left-center-right-middle-tl-tc-tr-ml-mc-mr-bl-bc-br = ວິທີໃຊ້: JUSTIFYTEXT + .usage-textfit-target-width-fits-selected-text-to-that-width = ວິທີໃຊ້: TEXTFIT <ຄວາມກວ້າງເປົ້າໝາຍ> (ປັບຂໍ້ຄວາມທີ່ເລືອກໃຫ້ພໍດີກັບຄວາມກວ້າງນັ້ນ) + .usage-arctext-text-select-an-arc-first-the-text-follows-it = ວິທີໃຊ້: ARCTEXT <ຂໍ້ຄວາມ> (ເລືອກເສັ້ນໂຄ້ງກ່ອນ; ຂໍ້ຄວາມຈະແລ່ນຕາມເສັ້ນໂຄ້ງ) + .limits-checking-on = ການກວດສອບຂອບເຂດ ເປີດ. + .limits-checking-off = ການກວດສອບຂອບເຂດ ປິດ. + .usage-section-x-y-z-value-cross-sections-the-selected-solid = ວິທີໃຊ້: SECTION [X|Y|Z] <ຄ່າ> (ສ້າງໜ້າຕັດຮູບຊົງຕັນທີ່ເລືອກ) + .usage-3dalign-sx1-sy1-sz1-sx3-sy3-sz3-dx1-dy1-dz1-dx3-dy3-dz3-18-numbers-3-sourc = ວິທີໃຊ້: 3DALIGN (18 ຕົວເລກ: 3 ຈຸດຕົ້ນທາງ ແລ້ວຕາມດ້ວຍ 3 ຈຸດປາຍທາງ) + .usage-3dmirror-x-y-z-mirrors-the-selected-solid-across-that-plane = ວິທີໃຊ້: 3DMIRROR [X|Y|Z] (ສະທ້ອນຮູບຊົງຕັນທີ່ເລືອກຂ້າມລະນາບນັ້ນ) + .usage-3drotate-x-y-z-angle-rotates-the-selected-solid = ວິທີໃຊ້: 3DROTATE [X|Y|Z] <ມຸມ> (ໝຸນຮູບຊົງຕັນທີ່ເລືອກ) + .usage-slice-x-y-z-value-top-bottom-cuts-the-selected-solid = ວິທີໃຊ້: SLICE [X|Y|Z] <ຄ່າ> [TOP|BOTTOM] (ຕັດຮູບຊົງຕັນທີ່ເລືອກ) + .perf-panel-on-tracing-render-and-interaction-costs = ເປີດແຖບ PERF — ກຳລັງຕິດຕາມຕົ້ນທຶນການປະມວນຜົນ ແລະ ການຕອບສະໜອງ + .perf-panel-off = ປິດແຖບ PERF + .addselected-select-an-object-first-then-run-addselected-to-draw-a-new-one-like-i = ADDSELECTED: ເລືອກວັດຖຸກ່ອນ, ແລ້ວຈຶ່ງສັ່ງ ADDSELECTED ເພື່ອແຕ້ມຮູບໃໝ່ຄືກັນກັບມັນ. + .usage-dimstyle-list-new-name-set-name-prop-val = ວິທີໃຊ້: DIMSTYLE LIST | NEW <ຊື່> | SET <ຊື່> <ຄຸນສົມບັດ> <ຄ່າ> + .laydel-cannot-delete-the-current-layer-make-another-layer-current-first = LAYDEL: ບໍ່ສາມາດລຶບເລເຢີປັດຈຸບັນໄດ້. ຕັ້ງເລເຢີອື່ນເປັນປັດຈຸບັນກ່ອນ. + .laymrg-cannot-merge-the-current-layer-make-another-layer-current-first = LAYMRG: ບໍ່ສາມາດຮວມເລເຢີປັດຈຸບັນໄດ້. ຕັ້ງເລເຢີອື່ນເປັນປັດຈຸບັນກ່ອນ. + .layerstate-no-saved-states-use-layerstate-save-name = LAYERSTATE: ບໍ່ມີສະຖານະທີ່ບັນທຶກໄວ້. ໃຊ້ LAYERSTATE SAVE <ຊື່>. + .usage-color-bylayer-byblock-1-255-red-yellow-green-cyan-blue-magenta-white = ວິທີໃຊ້: COLOR + .usage-chprop-prop-val-props-layer-color-linetype-ltscale = ວິທີໃຊ້: CHPROP <ຄຸນສົມບັດ> <ຄ່າ> (ຄຸນສົມບັດ: LAYER COLOR LINETYPE LTSCALE) + .setvar-ltscale-celtscale-pdmode-pdsize-textsize-orthomode-fillmode-mirrtext-fram = SETVAR: LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR CURSORSIZE PICKBOX CURSORTYPE SNAPANG TEXTFILL CLIPROMPTLINES COMMANDLINEFADETIME ATTREQ ATTDIA DIMASSOC DIMCONTINUEMODE ANGBASE ANGDIR SKETCHINC SKPOLY SKTOLERANCE DONUTID DONUTOD CENTEREXE CENTERLAYER CENTERLTYPE CENTERLTSCALE CENTERLTYPEFILE CENTERCROSSSIZE CENTERCROSSGAP CENTERMARKEXE COLORTHEME SELECTIONAREA SELECTIONAREAOPACITY SELECTIONEFFECT SELECTIONEFFECTCOLOR WINDOWSAREACOLOR CROSSINGAREACOLOR SELECTIONPREVIEW GRIPSIZE GRIPCOLOR GRIPHOT GRIPHOVER GRIPOBJLIMIT | CLAYER CELTYPE TEXTSTYLE (ອ່ານຢ່າງດຽວ) + .setvar-dimcontinuemode-requires-0-or-1 = SETVAR: DIMCONTINUEMODE ຕ້ອງການ 0 ຫຼື 1. + .setvar-donutid-requires-a-finite-value-greater-than-or-equal-to-zero = SETVAR: DONUTID ຕ້ອງການຄ່າຈຳກັດທີ່ໃຫຍ່ກວ່າ ຫຼື ເທົ່າກັບສູນ. + .setvar-donutod-requires-a-finite-value-greater-than-zero = SETVAR: DONUTOD ຕ້ອງການຄ່າຈຳກັດທີ່ໃຫຍ່ກວ່າສູນ. + .usage-pdmode-value-0-dot-1-none-2-3-x-4-tick-32-circle-64-square = ວິທີໃຊ້: PDMODE [ຄ່າ] (0=ຈຸດ 1=ບໍ່ມີ 2=+ 3=x 4=ຂີດ; +32 ວົງມົນ, +64 ສີ່ແຈ) + .fileassoc-set-to-1-registered-as-a-dwg-dxf-bak-handler = FILEASSOC ຖືກຕັ້ງເປັນ 1 — ລົງທະບຽນເປັນຕົວຈັດການ .dwg/.dxf/.bak ແລ້ວ + .usage-pdsize-value-0-absolute-size-0-percent-of-viewport-0-default = ວິທີໃຊ້: PDSIZE [ຄ່າ] (>0 ຂະໜາດແທ້ຈິງ, <0 ເປີເຊັນຂອງວິວພອດ, 0 ຄ່າເລີ່ມຕົ້ນ) + .usage-cuiexport-path-save-the-keyboard-shortcuts-to-a-file = ວິທີໃຊ້: CUIEXPORT <ເສັ້ນທາງໄຟລ໌> — ບັນທຶກປຸ່ມລັດແປ້ນພິມລົງໃນໄຟລ໌. + .usage-cuiimport-path-load-keyboard-shortcuts-from-a-file = ວິທີໃຊ້: CUIIMPORT <ເສັ້ນທາງໄຟລ໌> — ໂຫຼດປຸ່ມລັດແປ້ນພິມມາຈາກໄຟລ໌. + .colorscheme-0-and-1-are-colortheme-options-enter-a-theme-name-e-g-dark-light-dra = COLORSCHEME: 0 ແລະ 1 ແມ່ນຕົວເລືອກຂອງ COLORTHEME. ປ້ອນຊື່ຮູບແບບສີ (ຕົວຢ່າງ: DARK, LIGHT, DRACULA) ຫຼື ໃຊ້ COLORTHEME. + .vports-unknown-option-use-vports-2h-2v-4-single = VPORTS: ບໍ່ຮູ້ຈັກຕົວເລືອກ. ໃຊ້ VPORTS 2H | 2V | 4 | SINGLE + .syncpviewports-select-two-or-more-viewports-the-first-is-the-master = SYNCPVIEWPORTS: ເລືອກຕັ້ງແຕ່ສອງວິວພອດຂຶ້ນໄປ (ວິວພອດທຳອິດແມ່ນຕົວຫຼັກ). + .automation-target-document-closed = ເອກະສານເປົ້າໝາຍຂອງລະບົບອັດຕະໂນມັດຖືກປິດແລ້ວ + .projection-orthographic = ການສາຍພາບ: ອໍໂທກຣາຟິກ + .projection-perspective = ການສາຍພາບ: ເພີສເປັກທີບ + .named-plot-style-tables-are-not-supported-by-the-vector-plotter = ຕາຕະລາງຮູບແບບການພລັອດແບບລະບຸຊື່ບໍ່ຮອງຮັບໂດຍຕົວພລັອດເວັກເຕີ. + .disassociate = ຕັດການເຊື່ອມໂຍງ + .connected = ເຊື່ອມຕໍ່ແລ້ວ + .absolute = ຄ່າສຳບູນ + .orthographic = ອໍໂທກຣາຟິກ + .solids = ຮູບຊົງຕັນ + .square = ຮູບຈະຕຸລັດ + .tick = ຂີດໝາຍ + .read-only = ອ່ານຢ່າງດຽວ + .paper-space = ພື້ນທີ່ເຈ້ຍ + .perspective = ເພີສເປັກທີບ + .dot = ຈຸດ + .save-layer-mappings = ບັນທຶກການຈັບຄູ່ເລເຢີ + .load-layer-mappings = ໂຫຼດການຈັບຄູ່ເລເຢີ + .ocs-web = OCS ເວັບ + .ocs-desktop = OCS ເດັສທັອບ + .design = ອອກແບບ + .newer-version-already-installed = ເວີຊັນທີ່ໃໝ່ກວ່າຂອງ [ProductName] ໄດ້ຖືກຕິດຕັ້ງແລ້ວ. ໂປຣແກຣມຕິດຕັ້ງຈະປິດລົງ. + .active = ໃຊ້ງານຢູ່: __ocs_fmt_0__ + .plot-style-color-overrides = ຮູບແບບການພລັອດ: __ocs_fmt_0__ (__ocs_fmt_1__ ການຂຽນທັບສີ) + .paper-space-2 = ພື້ນທີ່ເຈ້ຍ '__ocs_fmt_0__' + .selhandles-selected-in-types = SELHANDLES: ເລືອກແລ້ວ __ocs_fmt_0__ ໃນ __ocs_fmt_1__ + ປະເພດ: __ocs_fmt_2__ + .blocks-2 = { "\u000A" }ບລັອກ: __ocs_fmt_0__ + .handles = { "\u000A" }ແຮນເດິລ: __ocs_fmt_0__ + .from-4-4-4-to-4-4-4-len-4 = ຈາກ (__ocs_fmt_0__,__ocs_fmt_1__,__ocs_fmt_2__) ຫາ (__ocs_fmt_3__,__ocs_fmt_4__,__ocs_fmt_5__) ຄວາມຍາວ=__ocs_fmt_6__ + .center-4-4-4-r-4-area-4 = ຈຸດໃຈກາງ (__ocs_fmt_0__,__ocs_fmt_1__,__ocs_fmt_2__) r=__ocs_fmt_3__ ເນື້ອທີ່=__ocs_fmt_4__ + .center-4-4-4-r-4-start-2-end-2 = ຈຸດໃຈກາງ (__ocs_fmt_0__,__ocs_fmt_1__,__ocs_fmt_2__) r=__ocs_fmt_3__ ເລີ່ມ=__ocs_fmt_4__° ສິ້ນສຸດ=__ocs_fmt_5__° + .vertices-closed-elevation-4 = __ocs_fmt_0__ ຈຸດຍອດ ແບບປິດ=__ocs_fmt_1__ ລະດັບຄວາມສູງ=__ocs_fmt_2__ + .h-4-at-4-4 = "__ocs_fmt_0__" h=__ocs_fmt_1__ ທີ່ (__ocs_fmt_2__,__ocs_fmt_3__) + .block-at-4-4-4-scale-4-4-4-rot-2 = ບລັອກ="__ocs_fmt_0__" ທີ່ (__ocs_fmt_1__,__ocs_fmt_2__,__ocs_fmt_3__) ມາດຕາສ່ວນ=(__ocs_fmt_4__,__ocs_fmt_5__,__ocs_fmt_6__) ໝຸນ=__ocs_fmt_7__° + .ctrl-pts-degree-closed = __ocs_fmt_0__ ຈຸດຄວບຄຸມ ອົງສາ=__ocs_fmt_1__ ແບບປິດ=__ocs_fmt_2__ + .center-4-4-major-len-4-ratio-4 = ຈຸດໃຈກາງ (__ocs_fmt_0__,__ocs_fmt_1__) ຄວາມຍາວແກນຫຼັກ=__ocs_fmt_2__ ອັດຕາສ່ວນ=__ocs_fmt_3__ + .vertices = __ocs_fmt_0__ ຈຸດຍອດ + .ctrl-pts = __ocs_fmt_0__ ຈຸດຄວບຄຸມ + .unexpected = ບໍ່ຄາດຄິດ '__ocs_fmt_0__' + .unexpected-c = ບໍ່ຄາດຄິດ '__ocs_fmt_0__' + .bad-number-s = ຕົວເລກບໍ່ຖືກຕ້ອງ '__ocs_fmt_0__' + .h-margin-2-v-margin-2 = __ocs_fmt_0__ (ໄລຍະຂອບລວງນອນ:__ocs_fmt_1__ ໄລຍະຂອບລວງຕັ້ງ:__ocs_fmt_2__) + .txt-2-asz-2 = __ocs_fmt_0__(ຂໍ້ຄວາມ:__ocs_fmt_1__ ຂະໜາດລູກສອນ:__ocs_fmt_2__) + .font-w-2-oblique-1 = __ocs_fmt_0__ (ຟອນ: __ocs_fmt_1__, ກວ້າງ: __ocs_fmt_2__, ມຸມອຽງ: __ocs_fmt_3__°) + .index-n = ດັດຊະນີ __ocs_fmt_0__ + .n-layers-layer-s = __ocs_fmt_0__ ເລເຢີ + .n-styles-text-style-s = __ocs_fmt_0__ ຮູບແບບຂໍ້ຄວາມ + .n-lts-linetype-s = __ocs_fmt_0__ ປະເພດເສັ້ນ + .n-blocks-block-s = __ocs_fmt_0__ ບລັອກ + .n-sortents-stale-draw-order-table-s = __ocs_fmt_0__ ຕາຕະລາງລຳດັບການແຕ້ມທີ່ລ້າສະໄໝ + .setvar-unknown-variable-name = SETVAR: ບໍ່ຮູ້ຈັກຕົວແປ "__ocs_fmt_0__". + .savetime-set-to-v-minute-s = SAVETIME ຖືກຕັ້ງເປັນ __ocs_fmt_0__ ນາທີ + .of = __ocs_fmt_0__ ຈາກ __ocs_fmt_1__ — __ocs_fmt_2__ + .replaced-1-occurrence-in-remaining-matching-object-s-remain = ແທນບ່ອນ 1 ຈຸດໃນ __ocs_fmt_0__; ຍັງເຫຼືອ __ocs_fmt_1__ ວັດຖຸທີ່ກົງກັນ. + .replaced-replaced-occurrence-s-in-object-s = ແທນບ່ອນແລ້ວ __ocs_fmt_0__ ຈຸດ ໃນ __ocs_fmt_1__ ວັດຖຸ. + .search-was-not-found = ບໍ່ພົບ "__ocs_fmt_0__". + .handle-x = ແຮນເດິລ __ocs_fmt_0__ + .block-x-text-x = ບລັອກ __ocs_fmt_0__, ຂໍ້ຄວາມ __ocs_fmt_1__ + .block-x-attribute = ບລັອກ __ocs_fmt_0__, ຄຸນລັກສະນະ __ocs_fmt_1__ + .exported = ສົ່ງອອກແລ້ວ: __ocs_fmt_0__ + .export-failed-e = ສົ່ງອອກຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .plotted-window-to = ພລັອດປ່ອງຢ້ຽມໄປທີ່ __ocs_fmt_0__ ແລ້ວ + .plot-failed-e = ພລັອດຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .layout-name-has-no-page-setup = ເລເອົາຕ໌ '__ocs_fmt_0__' ບໍ່ມີການຕັ້ງຄ່າໜ້າເຈ້ຍ. + .layout-name-plot-style-table-is-not-loaded = ຕາຕະລາງຮູບແບບການພລັອດ '__ocs_fmt_1__' ຂອງເລເອົາຕ໌ '__ocs_fmt_0__' ຍັງບໍ່ໄດ້ໂຫຼດ. + .layout-name-plot-area-is-empty = ພື້ນທີ່ພລັອດຂອງເລເອົາຕ໌ '__ocs_fmt_0__' ຫວ່າງເປົ່າ. + .exported-layouts-to = ສົ່ງອອກ __ocs_fmt_0__ ເລເອົາຕ໌ ໄປທີ່ __ocs_fmt_1__ ແລ້ວ + .export-failed-error = ສົ່ງອອກຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .sent-layouts-to-printer-printer = ສົ່ງ __ocs_fmt_0__ ເລເອົາຕ໌ ໄປເຄື່ອງພິມແລ້ວ: __ocs_fmt_1__ + .print-failed-error = ພິມຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .preview-failed-e = ສະແດງຕົວຢ່າງຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .repo-is-already-in-the-catalog = __ocs_fmt_0__ ມີຢູ່ໃນລາຍການແລ້ວ. + .repo-is-already-installed = __ocs_fmt_0__ ຖືກຕິດຕັ້ງແລ້ວ. + .fetching-releases-for-repo = ກຳລັງດຶງຂໍ້ມູນລຸ້ນປ່ອຍສຳລັບ __ocs_fmt_0__… + .repository-added-installable-release-s-found = ເພີ່ມບ່ອນເກັບໂຄດແລ້ວ. ພົບ __ocs_fmt_0__ ລຸ້ນທີ່ຕິດຕັ້ງໄດ້. + .installing-repo-tag = ກຳລັງຕິດຕັ້ງ __ocs_fmt_0__ __ocs_fmt_1__… + .updating-repo-to-tag = ກຳລັງອັບເດດ __ocs_fmt_0__ ເປັນ __ocs_fmt_1__… + .installed-id-restart-to-load-it = ຕິດຕັ້ງ '__ocs_fmt_0__' ແລ້ວ. ກະລຸນາຣີສະຕາດເພື່ອໂຫຼດໃຊ້ງານ. + .install-failed-e = ຕິດຕັ້ງຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .uninstall-failed-plugin-id-did-not-stop-in-time = ຖອນການຕິດຕັ້ງຫຼົ້ມເຫຼວ: ປລັກອິນ '__ocs_fmt_0__' ບໍ່ຢຸດເຮັດວຽກຕາມເວລາທີ່ກຳນົດ + .uninstalled-id = ຖອນການຕິດຕັ້ງ '__ocs_fmt_0__' ແລ້ວ. + .uninstall-failed-e = ຖອນການຕິດຕັ້ງຫຼົ້ມເຫຼວ: __ocs_fmt_0__ + .from-to-moved-object-s = __ocs_fmt_0__ -> __ocs_fmt_1__ (__ocs_fmt_2__ ວັດຖຸ){ "\u000A" } + .layer-skipped-reason = __ocs_fmt_0__: ຂ້າມ — __ocs_fmt_1__{ "\u000A" } + .lw-2-mm-idx = __ocs_fmt_0__ ມມ (ດັດຊະນີ __ocs_fmt_1__) + .idx = ດັດຊະນີ __ocs_fmt_0__ + .color-n = ສີ __ocs_fmt_0__ + .view-name = ມຸມມອງ: __ocs_fmt_0__ + .no-plot-style-loaded = ບໍ່ມີຮູບແບບການພລັອດຖືກໂຫຼດ. + .no-plot-style-table-loaded = ບໍ່ມີຕາຕະລາງຮູບແບບການພລັອດຖືກໂຫຼດ. + .model-space = ພື້ນທີ່ໂມເດວ + .the-matching-object-is-on-a-locked-layer = ວັດຖຸທີ່ກົງກັນຢູ່ໃນເລເຢີທີ່ລັອກໄວ້. + .enter-text-to-find = ປ້ອນຂໍ້ຄວາມທີ່ຈະຊອກຫາ. + .enter-a-github-url-or-repository-in-owner-repo-format = ປ້ອນ URL ຂອງ GitHub ຫຼື ບ່ອນເກັບໂຄດໃນຮູບແບບ ເຈົ້າຂອງ/ຊື່ບ່ອນເກັບ. + .dwgunits-convert-drawing-to = DWGUNITS ແປງຮູບແຕ້ມເປັນ: + .opened-plot-preview = ເປີດຕົວຢ່າງການພລັອດແລ້ວ. + + .auto-constrain = ກຳນົດຂໍ້ຜູກມັດອັດຕະໂນມັດ + .show-all = ສະແດງທັງໝົດ + .hide-all = ເຊື່ອງທັງໝົດ + .delete-constraints = ລຶບຂໍ້ຜູກມັດ + .parameters-manager = ຕົວຈັດການພາຣາມີເຕີ + .geometric = ເລຂາຄະນິດ + .dimensional = ມິຕິຂະໜາດ + .show-hide = ສະແດງ/ເຊື່ອງ + + .enter-a-valid-cell-range-such-as-a1-d10 = ປ້ອນຂອບເຂດຫ້ອງທີ່ຖືກຕ້ອງ ເຊັ່ນ A1:D10. + .named-ranges-require-a-spreadsheet-file = ຂອບເຂດທີ່ມີຊື່ຕ້ອງການໄຟລ໌ສະເປຣດຊີດ. + .select-a-named-range = ເລືອກຂອບເຂດທີ່ມີຊື່. + .the-named-range-was-not-found = ບໍ່ພົບຂອບເຂດທີ່ມີຊື່. + .the-named-range-does-not-refer-to-a-worksheet-cell-range = ຂອບເຂດທີ່ມີຊື່ບໍ່ໄດ້ອ້າງອີງເຖິງຂອບເຂດຫ້ອງໃນແຜ່ນງານ. + .the-spreadsheet-contains-no-worksheets = ສະເປຣດຊີດບໍ່ມີແຜ່ນງານໃດໆ. + .the-selected-data-source-is-empty = ແຫຼ່ງຂໍ້ມູນທີ່ເລືອກຫວ່າງເປົ່າ. + .the-data-link-no-longer-exists = ລິ້ງຂໍ້ມູນບໍ່ມີຢູ່ນີ້ອີກຕໍ່ໄປ. + .writing-to-this-data-link-is-disabled = ປິດການຂຽນລົງໃນລິ້ງຂໍ້ມູນນີ້. + .writing-linked-data-is-supported-for-csv-and-text-sources = ການຂຽນຂໍ້ມູນທີ່ເຊື່ອມໂຍງຮອງຮັບສຳລັບໄຟລ໌ CSV ແລະ ໄຟລ໌ຂໍ້ຄວາມ. + .data-source-not-found = ບໍ່ພົບແຫຼ່ງຂໍ້ມູນ: __ocs_arg_path__ + .select-at-least-one-property = ເລືອກຢ່າງໜ້ອຍ 1 ຄຸນສົມບັດ. + .select-a-data-link = ເລືອກລິ້ງຂໍ້ມູນ. + .the-selected-data-link-no-longer-exists = ລິ້ງຂໍ້ມູນທີ່ເລືອກບໍ່ມີຢູ່ນີ້ອີກຕໍ່ໄປ. + .columns-rows-width-and-height-must-be-positive-values = ຖັນ, ແຖວ, ຄວາມກວ້າງ, ແລະ ຄວາມສູງ ຕ້ອງເປັນຄ່າບວກ. + .choose-a-spreadsheet-file = ເລືອກໄຟລ໌ສະເປຣດຊີດ + .spreadsheet-files = ໄຟລ໌ສະເປຣດຊີດ + .enter-a-data-link-name = ປ້ອນຊື່ລິ້ງຂໍ້ມູນ. + .a-data-link-with-that-name-already-exists = ຊື່ລິ້ງຂໍ້ມູນນີ້ມີຢູ່ແລ້ວ. + .data-link-deleted-existing-table-values-were-retained = ລຶບລິ້ງຂໍ້ມູນແລ້ວ; ຄ່າຕາຕະລາງທີ່ມີຢູ່ຍັງຖືກຮັກສາໄວ້. + .no-objects-were-selected-when-the-wizard-opened = ບໍ່ມີວັດຖຸຖືກເລືອກເມື່ອຕົວຊ່ວຍສ້າງເປີດຂຶ້ນ. + .add-a-drawing-or-include-the-current-drawing = ເພີ່ມຮູບແຕ້ມ ຫຼື ຮວມຮູບແຕ້ມປັດຈຸບັນນຳ. + .no-extractable-objects-were-found-in-the-selected-source = ບໍ່ພົບວັດຖຸທີ່ສາມາດສະກັດໄດ້ໃນແຫຼ່ງຂໍ້ມູນທີ່ເລືອກ. + .select-at-least-one-object-type = ເລືອກຢ່າງໜ້ອຍ 1 ປະເພດວັດຖຸ. + .choose-at-least-one-output = ເລືອກຢ່າງໜ້ອຍ 1 ຜົນອອກ. + .data-extraction-settings = ການຕັ້ງຄ່າການສະກັດຂໍ້ມູນ + .add-drawings = ເພີ່ມຮູບແຕ້ມ + .select-objects-or-settings-selected-enter-to-apply = __ocs_arg_cmd__ ເລືອກວັດຖຸ ຫຼື [Settings] (ເລືອກແລ້ວ __ocs_arg_count__ ອັນ, ກົດ Enter ເພື່ອນຳໃຊ້): + .select-objects-or-settings = __ocs_arg_cmd__ ເລືອກວັດຖຸ ຫຼື [Settings]: + .add-parameter = + ເພີ່ມພາຣາມີເຕີ + .object-snap-3d = ການເກາະຈຸດວັດຖຸ 3D + .object-snap-on-f4-3d = ເປີດການເກາະຈຸດວັດຖຸ 3D (F4) + .object-snap-modes-3d = ໂໝດເກາະຈຸດວັດຖຸ 3D + .a-numeric-shell-offset-distance-is-required = ຕ້ອງການໄລຍະຫ່າງອອບເຊັດເປືອກຫຸ້ມເປັນຕົວເລກ. + .a-selected-edge-no-longer-belongs-to-the-body = ຂອບທີ່ເລືອກບໍ່ໄດ້ເປັນສ່ວນໜຶ່ງຂອງຮູບຊົງອີກຕໍ່ໄປ. + .a-selected-face-could-not-be-resolved = ບໍ່ສາມາດປະມວນຜົນໜ້າທີ່ເລືອກໄດ້. + .a-selected-face-does-not-belong-to-the-solid = ໜ້າທີ່ເລືອກບໍ່ໄດ້ເປັນສ່ວນໜຶ່ງຂອງຮູບຊົງຕັນ. + .align-scale-objects-based-on-alignment-points-yes-no-no = ALIGN ປັບຂະໜາດວັດຖຸຕາມຈຸດຈັດແນວບໍ່? [Yes / No] : + .annoreset-no-selected-object-has-alternate-scale-representations = ANNORESET: ບໍ່ມີວັດຖຸທີ່ເລືອກມີການສະແດງມາດຕາສ່ວນສຳຮອງ. + .annoreset-synchronized-object-s-from-the-current-scale = ANNORESET: ຊິງຄ໌ແລ້ວ __ocs_fmt_0__ ວັດຖຸ ຈາກມາດຕາສ່ວນປັດຈຸບັນ. + .adaptive-grid = ເສັ້ນຕາຕະລາງປັບຕົວຕາມສະພາບ + .add-drawing-folder = ເພີ່ມໂຟນເດີຮູບແຕ້ມ + .add-drawings-2 = ເພີ່ມຮູບແຕ້ມ… + .add-folder = ເພີ່ມໂຟນເດີ… + .add-parameter-2 = ເພີ່ມພາຣາມີເຕີ + .all-other-rows = ແຖວອື່ນໆທັງໝົດ + .allow-selection-cycling = ອະນຸຍາດການເລືອກວຽນຮອບ + .allow-writing-to-source-file = ອະນຸຍາດການຂຽນລົງໃນໄຟລ໌ຕົ້ນສະບັບ + .break-specify-second-break-point-or-first-point = BREAK ກຳນົດຈຸດຕັດຂາດທີສອງ ຫຼື [First point]: + .begin = ເລີ່ມ + .chamferedge-select-a-base-face-edge = CHAMFEREDGE: ເລືອກຂອບໜ້າຖານ. + .cannot-delete-beyond-this = ບໍ່ສາມາດລຶບເກີນນີ້ໄດ້. + .cell-range = ຂອບເຂດຫ້ອງ + .center-of-face = ຈຸດໃຈກາງຂອງໜ້າ + .choose-output = ເລືອກຜົນອອກ + .clipboard-access-is-unavailable-press-ctrl-c-or-command-c-to-copy-the-selected-history = ບໍ່ສາມາດເຂົ້າເຖິງຄລິບບອດໄດ້. ກົດ Ctrl+C ຫຼື Command+C ເພື່ອຄັດລອກປະຫວັດທີ່ເລືອກ. + .column-row-settings = ການຕັ້ງຄ່າຖັນ & ແຖວ + .combine-identical-rows = ຮວມແຖວທີ່ຄືກັນ + .constraint-settings = ການຕັ້ງຄ່າຂໍ້ຜູກມັດ + .constraint-type = ປະເພດຂໍ້ຜູກມັດ + .copied = ຄັດລອກແລ້ວ + .create-a-new-data-link = ສ້າງລິ້ງຂໍ້ມູນໃໝ່ + .create-a-new-data-extraction = ສ້າງການສະກັດຂໍ້ມູນໃໝ່ + .current-drawing = ຮູບແຕ້ມປັດຈຸບັນ + .cursor-dependent-position = ຕຳແໜ່ງຕາມຕົວກະພິບ + .dataextraction-specify-insertion-point = DATAEXTRACTION ກຳນົດຈຸດແຊກ: + .dataextraction-external-file-created = DATAEXTRACTION: ສ້າງໄຟລ໌ພາຍນອກແລ້ວ. + .dimbreak-select-dimension-to-add-remove-break-or-multiple = DIMBREAK ເລືອກເສັ້ນບອກຂະໜາດເພື່ອເພີ່ມ/ລຶບຮອຍຕັດຂາດ ຫຼື [Multiple]: + .dimbreak-select-dimensions-selected-press-enter-when-done = DIMBREAK ເລືອກເສັ້ນບອກຂະໜາດ (ເລືອກແລ້ວ __ocs_fmt_0__ ອັນ, ກົດ Enter ເມື່ອແລ້ວ): + .dimbreak-select-object-to-break-dimension-or-auto-manual-remove-auto = DIMBREAK ເລືອກວັດຖຸເພື່ອຕັດຂາດເສັ້ນບອກຂະໜາດ ຫຼື [Auto/Manual/Remove] : + .dimbreak-specify-first-break-point = DIMBREAK ກຳນົດຈຸດຕັດຂາດທຳອິດ: + .dimbreak-specify-second-break-point = DIMBREAK ກຳນົດຈຸດຕັດຂາດທີສອງ: + .dimbreak-no-editable-dimensions-were-selected = DIMBREAK: ບໍ່ມີເສັ້ນບອກຂະໜາດທີ່ແກ້ໄຂໄດ້ຖືກເລືອກ. + .dimbreak-no-intersections-were-found = DIMBREAK: ບໍ່ພົບຈຸດຕັດ. + .dimbreak-removed-breaks-from-dimension-s = DIMBREAK: ລຶບຮອຍຕັດຂາດອອກຈາກ __ocs_fmt_0__ ເສັ້ນບອກຂະໜາດແລ້ວ. + .dimbreak-select-a-dimension = DIMBREAK: ເລືອກເສັ້ນບອກຂະໜາດ. + .dimbreak-updated-dimension-s = DIMBREAK: ອັບເດດແລ້ວ __ocs_fmt_0__ ເສັ້ນບອກຂະໜາດ. + .dimedit-enter-new-dimension-text-measured-value = DIMEDIT ປ້ອນຂໍ້ຄວາມເສັ້ນບອກຂະໜາດໃໝ່ <ຄ່າທີ່ວັດແທກໄດ້>: + .dimedit-enter-obliquing-angle-press-enter-for-none = DIMEDIT ປ້ອນມຸມອຽງ (ກົດ Enter ຖ້າບໍ່ມີ): + .dimedit-enter-type-of-dimension-editing-home-new-rotate-oblique-home = DIMEDIT ປ້ອນປະເພດການແກ້ໄຂເສັ້ນບອກຂະໜາດ [Home/New/Rotate/Oblique] : + .dimedit-select-objects = DIMEDIT ເລືອກວັດຖຸ: + .dimedit-specify-angle-for-dimension-text = DIMEDIT ກຳນົດມຸມສຳລັບຂໍ້ຄວາມເສັ້ນບອກຂະໜາດ: + .dimedit-no-editable-dimensions-were-selected = DIMEDIT: ບໍ່ມີເສັ້ນບອກຂະໜາດທີ່ແກ້ໄຂໄດ້ຖືກເລືອກ. + .dimedit-updated-dimension-s = DIMEDIT: ອັບເດດແລ້ວ __ocs_fmt_0__ ເສັ້ນບອກຂະໜາດ. + .dimjogline-select-dimension-to-add-jog-or-remove = DIMJOGLINE ເລືອກເສັ້ນບອກຂະໜາດເພື່ອເພີ່ມຮອຍຫັກ ຫຼື [Remove]: + .dimjogline-select-dimension-to-remove-jog = DIMJOGLINE ເລືອກເສັ້ນບອກຂະໜາດເພື່ອລຶບຮອຍຫັກ: + .dimjogline-specify-jog-location-or-press-enter = DIMJOGLINE ກຳນົດຕຳແໜ່ງຮອຍຫັກ (ຫຼື ກົດ Enter): + .dimjogline-jog-added = DIMJOGLINE: ເພີ່ມຮອຍຫັກແລ້ວ. + .dimjogline-jog-removed = DIMJOGLINE: ລຶບຮອຍຫັກແລ້ວ. + .dimjogline-select-a-linear-or-aligned-dimension = DIMJOGLINE: ເລືອກເສັ້ນບອກຂະໜາດແບບເສັ້ນຊື່ ຫຼື ແບບຈັດແນວ. + .dimspace-specify-distance-between-dimensions-or-auto-auto = DIMSPACE ກຳນົດໄລຍະຫ່າງລະຫວ່າງເສັ້ນບອກຂະໜາດ ຫຼື [Auto] : + .dimspace-enter-a-non-negative-spacing-value-or-auto = DIMSPACE: ປ້ອນຄ່າໄລຍະຫ່າງທີ່ບໍ່ເປັນຄ່າລົບ ຫຼື Auto. + .dimspace-select-a-linear-or-aligned-dimension = DIMSPACE: ເລືອກເສັ້ນບອກຂະໜາດແບບເສັ້ນຊື່ ຫຼື ແບບຈັດແນວ. + .dimspace-select-one-or-more-linear-or-aligned-dimensions = DIMSPACE: ເລືອກເສັ້ນບອກຂະໜາດແບບເສັ້ນຊື່ ຫຼື ແບບຈັດແນວຕັ້ງແຕ່ໜຶ່ງອັນຂຶ້ນໄປ. + .dimspace-spacing-value-cannot-be-negative = DIMSPACE: ຄ່າໄລຍະຫ່າງຕ້ອງບໍ່ເປັນຄ່າລົບ. + .dimtedit-specify-angle-for-dimension-text = DIMTEDIT ກຳນົດມຸມສຳລັບຂໍ້ຄວາມເສັ້ນບອກຂະໜາດ: + .dimtedit-specify-new-location-for-dimension-text-or-left-right-center-home-angle = DIMTEDIT ກຳນົດຕຳແໜ່ງໃໝ່ສຳລັບຂໍ້ຄວາມເສັ້ນບອກຂະໜາດ ຫຼື [Left/Right/Center/Home/Angle]: + .dimtedit-select-a-dimension = DIMTEDIT: ເລືອກເສັ້ນບອກຂະໜາດ. + .divide-marker-s-placed = DIVIDE: ວາງແລ້ວ __ocs_fmt_0__ ເຄື່ອງໝາຍ. + .dof = DOF: __ocs_fmt_0__ + .draworder-selection-reordered-relative-to-reference-objects = DRAWORDER: ຈັດລຳດັບສິ່ງທີ່ເລືອກໃໝ່ທຽບກັບວັດຖຸອ້າງອີງແລ້ວ. + .data-extraction = ການສະກັດຂໍ້ມູນ + .data-extraction-wizard = ຕົວຊ່ວຍສ້າງການສະກັດຂໍ້ມູນ + .data-link-manager = ຕົວຈັດການລິ້ງຂໍ້ມູນ + .data-extraction-2 = ການສະກັດຂໍ້ມູນ + .data-link = ລິ້ງຂໍ້ມູນ + .data-link-name = ຊື່ລິ້ງຂໍ້ມູນ + .data-rows = ແຖວຂໍ້ມູນ + .define-data-source = ກຳນົດແຫຼ່ງຂໍ້ມູນ + .display-quick-properties-palette-on-selection = ສະແດງແຖບຄຸນສົມບັດດ່ວນເມື່ອມີການເລືອກ + .display-selection-cycling-list-box = ສະແດງກ່ອງລາຍການເລືອກແບບວຽນຮອບ + .display-coordinate-input-near-crosshairs = ສະແດງຊ່ອງປ້ອນພິກັດໃກ້ເສັ້ນກາກະບາດ + .display-grid-beyond-limits = ສະແດງເສັ້ນຕາຕະລາງເກີນຂອບເຂດ (Limits) + .display-selection-candidate-list-box-on-click = ສະແດງກ່ອງລາຍການຕົວເລືອກທີ່ອາດຈະເລືອກເມື່ອກົດຄລິກ + .distance-and-angle-tolerances-must-be-non-negative-numbers = ຄ່າຄວາມຄາດເຄື່ອນຂອງໄລຍະຫ່າງ ແລະ ມຸມ ຕ້ອງບໍ່ເປັນຄ່າລົບ. + .distance1-distance2 = ໄລຍະຫ່າງ1 = __ocs_fmt_0__, ໄລຍະຫ່າງ2 = __ocs_fmt_1__ + .draw-section = ແຕ້ມໜ້າຕັດ + .drawing-table = ຕາຕະລາງແບບແຕ້ມ + .drawings-folders = ແບບແຕ້ມ / ໂຟລເດີ + .dynamic-prompts = ຂໍ້ຄວາມແນະນຳແບບໄດນາມິກ + .edit-an-existing-data-extraction = ແກ້ໄຂການດຶງຂໍ້ມູນທີ່ມີຢູ່ + .editing-weighted-or-tolerance-fitted-interpolation-data-is-not-supported = ບໍ່ຮອງຮັບການແກ້ໄຂຂໍ້ມູນການປະມານຄ່າທີ່ຖ່ວງນ້ຳໜັກ ຫຼື ປັບຕາມຄ່າຄວາມຄາດເຄື່ອນ. + .empty-table = ຕາຕະລາງຫວ່າງເປົ່າ + .enable-pointer-input-f12 = ເປີດໃຊ້ງານການປ້ອນຂໍ້ມູນດ້ວຍເມົາສ໌ (F12) + .enable-dimension-input-fields = ເປີດໃຊ້ງານຊ່ອງປ້ອນຄ່າຂະໜາດ + .enter-a-body-editing-option-shell-exit = ປ້ອນຕົວເລືອກການແກ້ໄຂຮູບຊົງ [Shell/eXit]: + .enter-a-solid-editing-option-body-exit = ປ້ອນຕົວເລືອກການແກ້ໄຂວັດຖຸແຂງ [Body/eXit]: + .entire-sheet = ທັງໝົດແຜ່ນງານ + .equal-x-and-y-spacing = ໄລຍະຫ່າງ X ແລະ Y ເທົ່າກັນ + .external-file = ໄຟລ໌ພາຍນອກ + .extraction-settings = ການຕັ້ງຄ່າການດຶງຂໍ້ມູນ + .fillet-select-2d-polyline-r = FILLET ເລືອກໂພລີລາຍ 2D [R=__ocs_fmt_0__]: + .fillet-select-first-object-or-polyline-radius-r = FILLET ເລືອກວັດຖຸທຳອິດ ຫຼື [Polyline/Radius] [R=__ocs_fmt_0__]: + .fillet-specify-fillet-radius-or-first-point = FILLET ກຳນົດລັດສະໝີມຸມມົນ ຫຼື ຈຸດທຳອິດ <__ocs_fmt_0__>: + .fillet-specify-second-point-for-radius = FILLET ກຳນົດຈຸດທີສອງສຳລັບລັດສະໝີ: + .file = ໄຟລ໌ + .file-not-found-check-the-saved-path = ບໍ່ພົບໄຟລ໌ — ກະລຸນາກວດສອບທີ່ຢູ່ໄຟລ໌ທີ່ບັນທຶກໄວ້. + .first-row = ແຖວທຳອິດ + .fit-data-cannot-be-purged-without-a-stored-control-curve = ບໍ່ສາມາດລ້າງຂໍ້ມູນ fit ໄດ້ ຖ້າບໍ່ມີເສັ້ນໂຄ້ງຄວບຄຸມທີ່ບັນທຶກໄວ້. + .fit-data-does-not-define-a-valid-control-curve = ຂໍ້ມູນ fit ບໍ່ໄດ້ກຳນົດເສັ້ນໂຄ້ງຄວບຄຸມທີ່ຖືກຕ້ອງ. + .fit-points-do-not-define-a-valid-spline = ຈຸດ fit ບໍ່ໄດ້ກຳນົດເສັ້ນ spline ທີ່ຖືກຕ້ອງ. + .formula = ສູດຄິດໄລ່ + .full-path = ທີ່ຢູ່ໄຟລ໌ເຕັມ + .fully-constrained-no-remaining-degrees-of-freedom = ຜູກມັດຄົບຖ້ວນສົມບູນ — ບໍ່ມີລະດັບຄວາມເປັນອິດສະຫຼະເຫຼືອຢູ່ + .grid-on-f7 = ເປີດຕາໜ່າງ (F7) + .grid-x-spacing = ໄລຍະຫ່າງຕາໜ່າງແກນ X: + .grid-y-spacing = ໄລຍະຫ່າງຕາໜ່າງແກນ Y: + .grid-behavior = ພຶດຕິກຳຂອງຕາໜ່າງ + .grid-spacing = ໄລຍະຫ່າງຕາໜ່າງ + .hatchedit-pattern-scale-angle-style-origin-properties-color-layer-transparency-draw-order-associate-disassociate-annotative-recreate-boundary-separate-hatches-properties = HATCHEDIT ລວດລາຍ:__ocs_arg_name__ ມາດຕາສ່ວນ:__ocs_arg_scale__ ມຸມ:__ocs_arg_angle__ [Style/Origin/Properties/COlor/LAyer/Transparency/DRaw order/ASsociate/DIsassociate/ANnotative/recreate Boundary/separate Hatches] : + .intersect-created-result-object-s = INTERSECT: ສ້າງວັດຖຸຜົນລັບແລ້ວ __ocs_arg_count__ ອັນ. + .intersect-no-positive-area-common-result-original-object-s-retained = INTERSECT: ບໍ່ມີພື້ນທີ່ຮ່ວມກັນ; ຮັກສາວັດຖຸດັ້ງເດີມໄວ້ __ocs_arg_count__ ອັນ. + .intersect-select-at-least-two-solids-regions-or-coplanar-surfaces-of-a-compatible-type = INTERSECT: ເລືອກວັດຖຸແຂງ, Regions, ຫຼື ພື້ນຜິວຮ່ວມລະນາບ ຢ່າງໜ້ອຍສອງອັນທີ່ເຂົ້າກັນໄດ້. + .intersect-the-retained-object-could-not-be-updated = INTERSECT: ບໍ່ສາມາດອັບເດດວັດຖຸທີ່ຮັກສາໄວ້ໄດ້. + .intersect-the-selected-coincident-geometry-is-ambiguous = INTERSECT: ຮູບຊົງເລຂາຄະນິດທີ່ທັບຊ້ອນກັນທີ່ເລືອກນັ້ນບໍ່ຊັດເຈນ. + .intersect-the-selected-objects-do-not-have-a-supported-coplanar-surface-intersection = INTERSECT: ວັດຖຸທີ່ເລືອກບໍ່ມີຈຸດຕັດກັນຂອງພື້ນຜິວຮ່ວມລະນາບທີ່ຮອງຮັບ. + .intersect-the-selected-topology-could-not-be-intersected-safely = INTERSECT: ໂຄງສ້າງຮູບຮ່າງທີ່ເລືອກບໍ່ສາມາດຕັດກັນໄດ້ຢ່າງປອດໄພ. + .include-current-drawing = ລວມແບບແຕ້ມປັດຈຸບັນ + .include-subfolders = ລວມໂຟລເດີຍ່ອຍ + .increment-angle = ມຸມເພີ່ມຂຶ້ນເທື່ອລະ: + .indicator-fill-color = ສີພື້ນຕົວຊີ້ບອກ + .indicator-transparency = ຄວາມໂປ່ງໃສຂອງຕົວຊີ້ບອກ + .insert-table = ແຊກຕາຕະລາງ + .insert-data-extraction-table-into-drawing = ແຊກຕາຕະລາງການດຶງຂໍ້ມູນລົງໃນແບບແຕ້ມ + .insert-options = ຕົວເລືອກການແຊກ + .insert-table-after-saving-link = ແຊກຕາຕະລາງຫຼັງຈາກບັນທຶກການເຊື່ອມໂຍງ + .insertion-behavior = ພຶດຕິກຳການແຊກ + .keep-table-updated-to-source-formatting = ຮັກສາຕາຕະລາງໃຫ້ອັບເດດຕາມຮູບແບບຕົ້ນສະບັບ + .knot = ຈຸດ Knot + .leader-enter-an-annotation-option-none-mtext = LEADER ປ້ອນຕົວເລືອກຄຳອະທິບາຍ [None/Mtext]: + .leader-enter-first-line-of-annotation-text-or-options = LEADER ປ້ອນແຖວທຳອິດຂອງຂໍ້ຄວາມອະທິບາຍ ຫຼື : + .leader-enter-leader-formatting-option-spline-straight-arrow-none-exit = LEADER ປ້ອນຕົວເລືອກຮູບແບບເສັ້ນຊີ້ [Spline/Straight/Arrow/None] : + .leader-enter-next-line-of-annotation-text = LEADER ປ້ອນແຖວຖັດໄປຂອງຂໍ້ຄວາມອະທິບາຍ: + .leader-specify-leader-start-point = LEADER ກຳນົດຈຸດເລີ່ມຕົ້ນຂອງເສັ້ນຊີ້: + .leader-specify-next-point-or-annotation-format-undo-annotation = LEADER ກຳນົດຈຸດຖັດໄປ ຫຼື [Annotation/Format/Undo] : + .leader-specify-next-point = LEADER ກຳນົດຈຸດຖັດໄປ: + .lengthen-select-an-object-to-change-or-undo = LENGTHEN ເລືອກວັດຖຸທີ່ຈະປ່ຽນແປງ ຫຼື [Undo]: + .lengthen-select-an-object-to-measure-or-delta-percent-total-dynamic = LENGTHEN ເລືອກວັດຖຸທີ່ຈະວັດແທກ ຫຼື [Delta/Percent/Total/Dynamic]: + .loft-enter-an-option-guides-path-cross-sections-only-settings-continuity-bulge-magnitude-cross-sections-only = LOFT ປ້ອນຕົວເລືອກ [Guides/Path/Cross sections only/Settings/Continuity/Bulge magnitude] : + .loft-enter-an-option-guides-path-cross-sections-only-settings-cross-sections-only = LOFT ປ້ອນຕົວເລືອກ [Guides/Path/Cross sections only/Settings] : + .loft-select-cross-sections-in-order-or-point-join-mode-enter-to-finish = LOFT ເລືອກໜ້າຕັດຕາມລຳດັບ ຫຼື [Point/Join/Mode] (ກົດ Enter ເພື່ອສຳເລັດ) + .link-option = ຕົວເລືອກການເຊື່ອມໂຍງ + .link-settings = ການຕັ້ງຄ່າການເຊື່ອມໂຍງ + .live-section-enabled = ເປີດໃຊ້ງານໜ້າຕັດສົດແລ້ວ + .ltype-gen = ການສ້າງຮູບແບບເສັ້ນ (Ltype gen) + .measure-0-markers-placed = MEASURE: ບໍ່ມີການວາງເຄື່ອງໝາຍໃດໆ (0 ອັນ). + .measure-marker-s-placed = MEASURE: ວາງເຄື່ອງໝາຍແລ້ວ __ocs_fmt_0__ ອັນ. + .maintain-associativity = ຮັກສາຄວາມເຊື່ອມໂຍງໄວ້ + .major-line-every = ເສັ້ນຫຼັກທຸກໆ: + .midpoint-on-edge = ຈຸດເຄິ່ງກາງເທິງຂອບ + .moment-of-inertia = ໂມເມັນຄວາມເສື່ອຍ (Moment of inertia) + .more-options = ຕົວເລືອກເພີ່ມເຕີມ + .move-down = ຍ້າຍລົງ + .move-up = ຍ້າຍຂຶ້ນ + .named-parameters = ພາລາມິເຕີທີ່ມີຊື່ + .named-range = ຂອບເຂດທີ່ມີຊື່ + .nearest-to-face = ໃກ້ທີ່ສຸດກັບໜ້າພື້ນຜິວ + .no-path = ບໍ່ມີທີ່ຢູ່ໄຟລ໌ + .no-preview-available = ບໍ່ມີຕົວຢ່າງໃຫ້ເບິ່ງ. + .no-surfaces-selected = ບໍ່ມີພື້ນຜິວຖືກເລືອກ. + .objectscale-the-selected-objects-already-have-the-current-scale-or-do-not-support-annotation-scales = OBJECTSCALE: ວັດຖຸທີ່ເລືອກມີມາດຕາສ່ວນປັດຈຸບັນຢູ່ແລ້ວ, ຫຼື ບໍ່ຮອງຮັບມາດຕາສ່ວນຄຳອະທິບາຍ. + .objectscale-the-selected-objects-do-not-have-the-current-scale = OBJECTSCALE: ວັດຖຸທີ່ເລືອກບໍ່ມີມາດຕາສ່ວນປັດຈຸບັນ. + .objectscale-the-current-scale-on-object-s = OBJECTSCALE: __ocs_fmt_0__ ມາດຕາສ່ວນປັດຈຸບັນເທິງ __ocs_fmt_1__ ວັດຖຸ. + .object-snap-on-f3 = ເປີດຈຸດດູດວັດຖຸ (F3) + .object-snap-tracking-on-f11 = ເປີດການຕິດຕາມຈຸດດູດວັດຖຸ (F11) + .object-snap-modes-2 = ຮູບແບບຈຸດດູດວັດຖຸ + .on-by-default-turn-off-to-show-just-the-constraint-glyph-in-the-viewport-enough-to-see-that-a-constraint-is-present-without-the-driven-value-or-named-parameter-text-covering-nearby-geometry = ເປີດໃຊ້ງານໂດຍເລີ່ມຕົ້ນ. ປິດເພື່ອສະແດງສະເພາະສັນຍະລັກການຜູກມັດໃນມຸມມອງ ເພື່ອໃຫ້ພໍເຫັນວ່າມີການຜູກມັດຢູ່ ໂດຍບໍ່ໃຫ້ຄ່າ ຫຼື ຂໍ້ຄວາມພາລາມິເຕີບັງຮູບຊົງໃກ້ຄຽງ. + .one-or-more-parametric-constraints-conflict-or-are-redundant-click-to-remove-one-and-re-solve = ມີການຜູກມັດທາງພາລາມິເຕີໜຶ່ງຢ່າງ ຫຼື ຫຼາຍກວ່ານັ້ນຂັດແຍ້ງກັນ ຫຼື ຊ້ຳຊ້ອນ + ກົດຄລິກເພື່ອລຶບອອກໜຶ່ງອັນ ແລະ ຄິດໄລ່ໃໝ່ + .ortho-mode-f8 = ໂໝດຕັ້ງສາກ (F8) + .output-data-to-external-file = ສົ່ງອອກຂໍ້ມູນໄປຍັງໄຟລ໌ພາຍນອກ + .output-file = ໄຟລ໌ຜົນລັບ + .pedit-convert-lines-and-arcs-to-polylines-yes-no-yes = PEDIT ແປງເສັ້ນຊື່ ແລະ ສ່ວນໂຄ້ງເປັນໂພລີລາຍ [Yes/No] : + .pedit-enter-polyline-linetype-generation-option-on-off = PEDIT ປ້ອນຕົວເລືອກການສ້າງຮູບແບບເສັ້ນໂພລີລາຍ [ON/OFF]: + .pedit-select-polyline-or-a-line-arc-to-convert-or-multiple = PEDIT ເລືອກໂພລີລາຍ (ຫຼື ເສັ້ນຊື່/ສ່ວນໂຄ້ງ ເພື່ອແປງ) ຫຼື [Multiple]: + .palette-location = ຕຳແໜ່ງແຜ່ນເຄື່ອງມື + .parameter-already-exists = ພາລາມິເຕີ '__ocs_fmt_0__' ມີຢູ່ແລ້ວ. + .parameter = ພາລາມິເຕີ '__ocs_fmt_0__': __ocs_fmt_1__ + .parameters = ພາລາມິເຕີ + .perpendicular-objects-must-share-an-intersection-point = ວັດຖຸທີ່ຕັ້ງສາກກັນຕ້ອງມີຈຸດຕັດຮ່ວມກັນ + .perpendicular-to-face = ຕັ້ງສາກກັບໜ້າພື້ນຜິວ + .plane = ລະນາບ + .pointer-input = ການປ້ອນຂໍ້ມູນດ້ວຍຕົວຊີ້ + .polar-angle-settings = ການຕັ້ງຄ່າມຸມໂພລ່າ + .polar-tracking-on-f10 = ເປີດການຕິດຕາມແນວມຸມໂພລ່າ (F10) + .polar-tracking-guides-cursor-movement-along-specified-angles-ortho-mode-constrains-movement-to-orthogonal-axes = ການຕິດຕາມແນວມຸມໂພລ່າຊ່ວຍນຳທາງເມົາສ໌ໄປຕາມມຸມທີ່ກຳນົດ. ໂໝດຕັ້ງສາກຈະຈຳກັດການເຄື່ອນທີ່ສະເພາະແກນຕັ້ງສາກເທົ່ານັ້ນ. + .press-enter-to-accept-the-chamfer-or-distance = ກົດ Enter ເພື່ອຍອມຮັບມຸມຕັດ ຫຼື [Distance]: + .principal-directions = ທິດທາງຫຼັກ + .principal-moments = ໂມເມັນຫຼັກ + .priority = ລະດັບຄວາມສຳຄັນ + .product-of-inertia = ຜົນຄູນຄວາມເສື່ອຍ (Product of inertia) + .reverse-select-line-polyline-spline-or-helix-to-reverse = REVERSE ເລືອກເສັ້ນຊື່, ໂພລີລາຍ, spline ຫຼື helix ເພື່ອກັບທິດທາງ: + .radii-of-gyration = ລັດສະໝີໄຈເລຊັນ (Radii of gyration) + .range = ຂອບເຂດ + .recent-colors = ສີທີ່ໃຊ້ຫວ່າງມໍ່ໆນີ້ + .reference-changes-are-not-available-on-web-the-reference-list-is-read-only = ການປ່ຽນແປງການອ້າງອີງບໍ່ສາມາດໃຊ້ງານໄດ້ເທິງເວັບ — ລາຍການອ້າງອີງສາມາດອ່ານໄດ້ເທົ່ານັ້ນ. + .refine-data = ປັບແຕ່ງຂໍ້ມູນ + .relative-path = ທີ່ຢູ່ໄຟລ໌ແບບສຳພັດ + .remaining-degrees-of-freedom-in-the-current-parametric-constraint-scope = ລະດັບຄວາມເປັນອິດສະຫຼະທີ່ເຫຼືອຢູ່ໃນຂອບເຂດການຜູກມັດພາລາມິເຕີປັດຈຸບັນ + .requires-an-integer-between-0-and-99 = ຕ້ອງການຈຳນວນຖ້ວນລະຫວ່າງ 0 ຫາ 99. + .requires-numeric-distance-or-two-points = ຕ້ອງການຄ່າໄລຍະຫ່າງເປັນຕົວເລກ ຫຼື ສອງຈຸດ. + .sectionplane-align-section-to-front-back-top-bottom-left-right-top = SECTIONPLANE ຈັດລະນາບໜ້າຕັດໄປທີ່ [Front/bAck/Top/Bottom/Left/Right] : + .sectionplane-enter-section-plane-type-plane-slice-boundary-volume-plane = SECTIONPLANE ປ້ອນປະເພດລະນາບໜ້າຕັດ [Plane/Slice/Boundary/Volume] : + .sectionplane-specify-next-point-or-enter-to-complete = SECTIONPLANE ກຳນົດຈຸດຖັດໄປ ຫຼື ກົດ ENTER ເພື່ອສຳເລັດ: + .sectionplane-specify-next-point = SECTIONPLANE ກຳນົດຈຸດຖັດໄປ: + .sectionplane-specify-point-in-direction-of-section-view = SECTIONPLANE ກຳນົດຈຸດໃນທິດທາງຂອງມຸມມອງໜ້າຕັດ: + .sectionplane-specify-start-point = SECTIONPLANE ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .sectionplane-specify-through-point = SECTIONPLANE ກຳນົດຈຸດທີ່ຜ່ານ: + .setbylayer-no-properties-required-changes = SETBYLAYER: ບໍ່ມີຄຸນສົມບັດໃດທີ່ຕ້ອງປ່ຽນແປງ. + .setbylayer-reset-entity-entities-to-bylayer = SETBYLAYER: ຕັ້ງຄ່າ __ocs_fmt_0__ ອົງປະກອບຄືນເປັນ ByLayer. + .setvar-cetransparency-ltscale-celtscale-pdmode-pdsize-textsize-orthomode-fillmode-mirrtext-frame-imageframe-pdfframe-wipeoutframe-xclipframe-pointcloudclipframe-zoomwheel-zoomfactor-cursorsize-pickbox-cursortype-snapang-textfill-clipromptlines-commandlinefadetime-attreq-attdia-dimassoc-dimcontinuemode-constraintsolvemode-constraintinfer-constraintbardisplay-angbase-angdir-sketchinc-skpoly-sktolerance-donutid-donutod-centerexe-centerlayer-centerltype-centerltscale-centerltypefile-centercrosssize-centercrossgap-centermarkexe-colortheme-selectionarea-selectionareaopacity-selectioneffect-selectioneffectcolor-windowsareacolor-crossingareacolor-selectionpreview-gripsize-gripcolor-griphot-griphover-gripobjlimit-clayer-celtype-textstyle-read-only = SETVAR: CETRANSPARENCY LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR SHORTCUTMENU SHORTCUTMENUDURATION CURSORSIZE PICKBOX CURSORTYPE SNAPANG TEXTFILL CLIPROMPTLINES COMMANDLINEFADETIME ATTREQ ATTDIA DIMASSOC DIMCONTINUEMODE CONSTRAINTSOLVEMODE CONSTRAINTINFER CONSTRAINTBARDISPLAY ANGBASE ANGDIR SKETCHINC SKPOLY SKTOLERANCE DONUTID DONUTOD CENTEREXE CENTERLAYER CENTERLTYPE CENTERLTSCALE CENTERLTYPEFILE CENTERCROSSSIZE CENTERCROSSGAP CENTERMARKEXE COLORTHEME SELECTIONAREA SELECTIONAREAOPACITY SELECTIONEFFECT SELECTIONEFFECTCOLOR WINDOWSAREACOLOR CROSSINGAREACOLOR SELECTIONPREVIEW GRIPSIZE GRIPCOLOR GRIPHOT GRIPHOVER GRIPOBJLIMIT | CLAYER CELTYPE TEXTSTYLE (ອ່ານໄດ້ເທົ່ານັ້ນ) + .shell-supports-rectangular-boxes-circular-cylinders-and-spheres = SHELL ຮອງຮັບກ່ອງສີ່ຫຼ່ຽມ, ຮູບທໍ່ກົມ, ແລະ ໜ່ວຍມົນ. + .shell-solid-updated = SHELL: ອັບເດດວັດຖຸແຂງແລ້ວ. + .splinedit-fit-data-add-delete-move-purge-tangents-exit-exit = SPLINEDIT ຂໍ້ມູນ Fit [Add/Delete/Move/Purge/Tangents/eXit] : + .splinedit-fit-data-add-delete-move-purge-exit-exit = SPLINEDIT ຂໍ້ມູນ Fit [Add/Delete/Move/Purge/eXit] : + .splinedit-select-any-open-curves-to-join-to-source = SPLINEDIT ເລືອກເສັ້ນໂຄ້ງປາຍເປີດໃດໆ ເພື່ອເຊື່ອມຕໍ່ເຂົ້າກັບຕົ້ນສະບັບ: + .splinedit-specify-a-point-on-the-spline-exit = SPLINEDIT ກຳນົດຈຸດເທິງເສັ້ນ spline : + .splinedit-specify-control-vertex-to-delete = SPLINEDIT ກຳນົດຈຸດຄວບຄຸມ (control vertex) ທີ່ຈະລຶບ: + .splinedit-specify-control-vertex = SPLINEDIT ກຳນົດຈຸດຄວບຄຸມ (control vertex): + .splinedit-specify-end-tangent-or-system-default = SPLINEDIT ກຳນົດເສັ້ນສຳຜັດປາຍ ຫຼື [System default]: + .splinedit-specify-existing-fit-point-on-spline-exit = SPLINEDIT ກຳນົດຈຸດ fit ທີ່ມີຢູ່ເທິງເສັ້ນ spline : + .splinedit-specify-new-fit-point-to-add-exit = SPLINEDIT ກຳນົດຈຸດ fit ໃໝ່ທີ່ຈະເພີ່ມ : + .splinedit-specify-new-location-or-next-previous-select-point-exit-next = SPLINEDIT ກຳນົດຕຳແໜ່ງໃໝ່ ຫຼື [Next/Previous/Select point/eXit] : + .splinedit-specify-new-point-or-after-before-exit = SPLINEDIT ກຳນົດຈຸດໃໝ່ ຫຼື [After/Before] : + .splinedit-specify-precision-0-99-10-straight-segments = SPLINEDIT ກຳນົດຄວາມລະອຽດ 0-99 <10> (ທ່ອນເສັ້ນຊື່): + .splinedit-specify-start-tangent-or-system-default = SPLINEDIT ກຳນົດເສັ້ນສຳຜັດເລີ່ມຕົ້ນ ຫຼື [System default]: + .splinedit-add-delete-elevate-order-move-weight-exit-exit = SPLINEDIT [Add/Delete/Elevate order/Move/Weight/eXit] : + .splinedit-close-join-move-vertex-refine-reverse-convert-to-polyline-undo-exit-exit = SPLINEDIT [Close/Join/Move vertex/Refine/rEverse/convert to Polyline/Undo/eXit] : + .splinedit-fit-data-close-join-move-vertex-refine-reverse-convert-to-polyline-undo-exit-exit = SPLINEDIT [Fit data/Close/Join/Move vertex/Refine/rEverse/convert to Polyline/Undo/eXit] : + .splinedit-fit-data-open-move-vertex-refine-reverse-convert-to-polyline-undo-exit-exit = SPLINEDIT [Fit data/Open/Move vertex/Refine/rEverse/convert to Polyline/Undo/eXit] : + .splinedit-open-move-vertex-refine-reverse-convert-to-polyline-undo-exit-exit = SPLINEDIT [Open/Move vertex/Refine/rEverse/convert to Polyline/Undo/eXit] : + .subtract-convert-selected-closed-mesh-objects-to-solids-yes-no-yes = SUBTRACT ແປງວັດຖຸ Mesh ແບບປິດທີ່ເລືອກໃຫ້ເປັນວັດຖຸແຂງບໍ່? [Yes/No] : + .subtract-select-solids-regions-surfaces-or-meshes-to-subtract-then-press-enter = SUBTRACT ເລືອກວັດຖຸແຂງ, Regions, ພື້ນຜິວ, ຫຼື Meshes ທີ່ຈະລົບອອກ ແລ້ວກົດ Enter: + .subtract-select-base-solids-regions-surfaces-or-meshes-then-press-enter = SUBTRACT ເລືອກວັດຖຸແຂງ, Regions, ພື້ນຜິວ, ຫຼື Meshes ຫຼັກ ແລ້ວກົດ Enter: + .subtract-a-selected-mesh-is-not-a-supported-closed-single-shell-mesh-no-object-was-changed = SUBTRACT: Mesh ທີ່ເລືອກບໍ່ແມ່ນ mesh ແບບເປືອກດ່ຽວປິດທີ່ຮອງຮັບ; ບໍ່ມີວັດຖຸໃດຖືກປ່ຽນແປງ. + .subtract-created-result-object-s = SUBTRACT: ສ້າງວັດຖຸຜົນລັບແລ້ວ __ocs_arg_count__ ອັນ. + .subtract-no-compatible-base-and-cutter-types-or-planes-were-selected = SUBTRACT: ບໍ່ໄດ້ເລືອກປະເພດ ຫຼື ລະນາບຫຼັກ ແລະ ຕົວຕັດ ທີ່ເຂົ້າກັນໄດ້. + .subtract-select-at-least-one-base-and-one-cutter-solid-region-or-surface = SUBTRACT: ເລືອກວັດຖຸແຂງ, Region, ຫຼື ພື້ນຜິວ ຫຼັກ ແລະ ຕົວຕັດ ຢ່າງໜ້ອຍຢ່າງລະໜຶ່ງອັນ. + .subtract-the-retained-base-object-could-not-be-updated = SUBTRACT: ບໍ່ສາມາດອັບເດດວັດຖຸຫຼັກທີ່ຮັກສາໄວ້ໄດ້. + .subtract-the-selected-coincident-geometry-is-ambiguous = SUBTRACT: ຮູບຊົງເລຂາຄະນິດທີ່ທັບຊ້ອນກັນທີ່ເລືອກນັ້ນບໍ່ຊັດເຈນ. + .subtract-the-selected-geometry-includes-an-unsupported-surface-intersection = SUBTRACT: ຮູບຊົງທີ່ເລືອກລວມມີການຕັດກັນຂອງພື້ນຜິວທີ່ບໍ່ຮອງຮັບ. + .subtract-the-selected-topology-could-not-be-cut-safely = SUBTRACT: ໂຄງສ້າງຮູບຮ່າງທີ່ເລືອກບໍ່ສາມາດຕັດໄດ້ຢ່າງປອດໄພ. + .second-row = ແຖວທີສອງ + .section-plane = ລະນາບໜ້າຕັດ + .section-plane-offset = ໄລຍະເລື່ອນຂະໜານຂອງລະນາບໜ້າຕັດ + .select-a-3d-solid-or-surface-edge = ເລືອກຂອບຂອງວັດຖຸແຂງ 3D ຫຼື ຂອບພື້ນຜິວ. + .select-a-3d-solid = ເລືອກວັດຖຸແຂງ 3D. + .select-a-3d-solid-2 = ເລືອກວັດຖຸແຂງ 3D: + .select-a-data-link-or-create-a-new-one = ເລືອກການເຊື່ອມໂຍງຂໍ້ມູນ ຫຼື ສ້າງອັນໃໝ່. + .select-an-edge-or-loop-distance = ເລືອກຂອບ ຫຼື [Loop/Distance]: + .select-edge-of-loop-or-edge-distance = ເລືອກຂອບຂອງຮອບປິດ ຫຼື [Edge/Distance]: + .select-face-or-any-point-to-locate-section-line-or-draw-section-orthographic-type = ເລືອກໜ້າພື້ນຜິວ ຫຼື ຈຸດໃດກໍໄດ້ເພື່ອກຳນົດຕຳແໜ່ງເສັ້ນໜ້າຕັດ ຫຼື [Draw section/Orthographic/Type]: + .select-faces-to-add-undo-remove-all-done = ເລືອກໜ້າພື້ນຜິວທີ່ຈະເພີ່ມ [Undo/Remove/ALL] : + .select-faces-to-remove-undo-add-all-done = ເລືອກໜ້າພື້ນຜິວທີ່ຈະລຶບອອກ [Undo/Add/ALL] : + .select-objects-in-the-current-drawing = ເລືອກວັດຖຸໃນແບບແຕ້ມປັດຈຸບັນ + .select-properties = ເລືອກຄຸນສົມບັດ + .select-spline-fit-polylines = ເລືອກໂພລີລາຍແບບ spline-fit: + .select-surfaces-to-thicken = ເລືອກພື້ນຜິວທີ່ຈະເພີ່ມຄວາມໜາ: + .selected-objects-in-current-drawing = ວັດຖຸທີ່ເລືອກໃນແບບແຕ້ມປັດຈຸບັນ + .set-cell-styles = ຕັ້ງຄ່າຮູບແບບຫ້ອງ + .sheet = ແຜ່ນງານ + .show-associativity = ສະແດງຄວາມເຊື່ອມໂຍງ + .show-command-prompting-and-command-input-near-crosshairs = ສະແດງຂໍ້ຄວາມແນະນຳ ແລະ ຊ່ອງປ້ອນຄຳສັ່ງໃກ້ກັບເຄີເຊີກາກະບາດ + .show-count-column = ສະແດງຖັນຈຳນວນ + .show-cycling-badge-when-objects-overlap = ສະແດງເຄື່ອງໝາຍເລືອກວົນຊ້ຳເມື່ອວັດຖຸທັບຊ້ອນກັນ + .show-name-column = ສະແດງຖັນຊື່ + .show-values-and-parameter-names-on-constraint-markers = ສະແດງຄ່າ ແລະ ຊື່ພາລາມິເຕີເທິງເຄື່ອງໝາຍການຜູກມັດ + .slice = ຕັດແບ່ງ (Slice) + .slice-depth = ຄວາມເລິກຂອງການຕັດແບ່ງ + .snap-on-f9 = ເປີດຈຸດດູດພິກັດ (F9) + .snap-x-spacing = ໄລຍະຫ່າງຈຸດດູດແກນ X: + .snap-y-spacing = ໄລຍະຫ່າງຈຸດດູດແກນ Y: + .snap-spacing = ໄລຍະຫ່າງຈຸດດູດ + .snap-type = ປະເພດຈຸດດູດ + .specify-distance1-or-expression = ກຳນົດໄລຍະຫ່າງ1 ຫຼື [Expression] <__ocs_fmt_0__>: + .specify-distance2-or-expression = ກຳນົດໄລຍະຫ່າງ2 ຫຼື [Expression] <__ocs_fmt_0__>: + .specify-insertion-point = ກຳນົດຈຸດແຊກ + .specify-point-or-enter-arc-length = ກຳນົດຈຸດ ຫຼື ປ້ອນຄວາມຍາວສ່ວນໂຄ້ງ: + .specify-point-or-enter-radius = ກຳນົດຈຸດ ຫຼື ປ້ອນລັດສະໝີ: + .specify-replacement-path-xref-path-find-old-new = ກຳນົດທີ່ຢູ່ໄຟລ໌ໃໝ່: ຄົ້ນຫາທີ່ຢູ່ໄຟລ໌ XREF + .specify-second-point = ກຳນົດຈຸດທີສອງ: + .specify-the-shell-offset-distance = ກຳນົດໄລຍະຫ່າງ shell offset <__ocs_fmt_0__>: + .specify-thickness = ກຳນົດຄວາມໜາ + .specify-window = ກຳນົດຂອບເຂດໜ້າຕ່າງ + .spline-cannot-be-converted-within-the-requested-precision = ບໍ່ສາມາດແປງ spline ພາຍໃນຄວາມລະອຽດທີ່ຮ້ອງຂໍໄດ້. + .spreadsheet-links = ການເຊື່ອມໂຍງຕາຕະລາງຄິດໄລ່ + .start-from-data-extraction = ເລີ່ມຈາກການດຶງຂໍ້ມູນ + .start-from-data-link = ເລີ່ມຈາກການເຊື່ອມໂຍງຂໍ້ມູນ + .start-from-empty-table = ເລີ່ມຈາກຕາຕະລາງຫວ່າງເປົ່າ + .state2 = ສະຖານະ2 + .static-quadrant-location = ຕຳແໜ່ງຈຕຸພາກຄົງທີ່ + .status-linked = ສະຖານະ: ເຊື່ອມໂຍງແລ້ວ + .status-source-not-found = ສະຖານະ: ບໍ່ພົບແຫຼ່ງຂໍ້ມູນ + .surface-associativity = ຄວາມເຊື່ອມໂຍງຂອງພື້ນຜິວ + .surface-type-2 = ປະເພດພື້ນຜິວ + .thicken-created-solid-s-surface-s-could-not-be-thickened-exactly = THICKEN: ສ້າງວັດຖຸແຂງແລ້ວ __ocs_fmt_0__ ອັນ; ບໍ່ສາມາດເພີ່ມຄວາມໜາໃຫ້ __ocs_fmt_1__ ພື້ນຜິວ ໄດ້ຢ່າງຊັດເຈນ. + .tab-separated-text = ຂໍ້ຄວາມຂັ້ນດ້ວຍແຖບ (Tab) + .tangent-objects-must-share-an-intersection-point = ວັດຖຸສຳຜັດກັນຕ້ອງມີຈຸດຕັດຮ່ວມກັນ + .tangent-point-must-differ-from-the-fit-endpoint = ຈຸດສຳຜັດຕ້ອງແຕກຕ່າງຈາກຈຸດປາຍ fit. + .the-intersect-result-could-not-be-displayed-completely-the-original-objects-were-retained = ບໍ່ສາມາດສະແດງຜົນລັບ INTERSECT ໄດ້ຢ່າງສົມບູນ. ໄດ້ຮັກສາວັດຖຸດັ້ງເດີມໄວ້. + .the-intersect-result-could-not-be-encoded-as-acis = ຜົນລັບ INTERSECT ບໍ່ສາມາດເຂົ້າລະຫັດເປັນ ACIS ໄດ້. + .the-subtract-result-could-not-be-displayed-completely-the-original-objects-were-retained = ບໍ່ສາມາດສະແດງຜົນລັບ SUBTRACT ໄດ້ຢ່າງສົມບູນ. ໄດ້ຮັກສາວັດຖຸດັ້ງເດີມໄວ້. + .the-subtract-result-could-not-be-encoded-as-acis = ຜົນລັບ SUBTRACT ບໍ່ສາມາດເຂົ້າລະຫັດເປັນ ACIS ໄດ້. + .the-union-result-could-not-be-encoded-as-acis = ຜົນລັບ UNION ບໍ່ສາມາດເຂົ້າລະຫັດເປັນ ACIS ໄດ້. + .the-chamfer-history-could-not-be-recorded = ບໍ່ສາມາດບັນທຶກປະຫວັດການຕັດມຸມໄດ້. + .the-edge-body-could-not-be-updated = ບໍ່ສາມາດອັບເດດຮູບຊົງຂອບໄດ້. + .the-edge-body-history-could-not-be-created = ບໍ່ສາມາດສ້າງປະຫວັດຮູບຊົງຂອບໄດ້. + .the-offset-removes-all-material-use-a-smaller-distance-or-add-a-face-back = ການ offset ເຮັດໃຫ້ເນື້ອວັດຖຸຫາຍໝົດ. ກະລຸນາໃຊ້ໄລຍະຫ່າງທີ່ນ້ອຍລົງ ຫຼື ເພີ່ມໜ້າພື້ນຜິວກັບຄືນ. + .the-region-could-not-be-encoded-as-acis = Region ບໍ່ສາມາດເຂົ້າລະຫັດເປັນ ACIS ໄດ້. + .the-result-could-not-be-displayed-completely-the-original-edge-body-was-retained = ບໍ່ສາມາດສະແດງຜົນລັບໄດ້ຢ່າງສົມບູນ. ໄດ້ຮັກສາຮູບຊົງຂອບດັ້ງເດີມໄວ້. + .the-selected-base-face-no-longer-belongs-to-the-body = ໜ້າພື້ນຜິວຫຼັກທີ່ເລືອກບໍ່ໄດ້ຂຶ້ນກັບຮູບຊົງນີ້ອີກຕໍ່ໄປ. + .the-selected-drawing-data-is-ready-to-be-extracted = ຂໍ້ມູນແບບແຕ້ມທີ່ເລືອກພ້ອມແລ້ວທີ່ຈະດຶງອອກມາ. + .the-selected-object-is-no-longer-available = ວັດຖຸທີ່ເລືອກບໍ່ມີໃຫ້ໃຊ້ງານແລ້ວ. + .the-shell-could-not-be-constructed-from-the-selected-solid-and-distance = ບໍ່ສາມາດສ້າງ shell ຈາກວັດຖຸແຂງ ແລະ ໄລຍະຫ່າງທີ່ເລືອກໄດ້. + .the-shell-offset-distance-must-be-nonzero = ໄລຍະຫ່າງ shell offset ຕ້ອງບໍ່ເທົ່າກັບສູນ. + .trimmed-surface = ພື້ນຜິວທີ່ຖືກຕັດ + .trimming-edges = ຂອບທີ່ໃຊ້ຕັດ + .trims = ການຕັດ + .type-a-name-and-a-formula-e-g-hole-dia-12-hole-spacing-2-hole-dia-1-5-apply-to-save-and-re-solve-closing-discards-unapplied-edits = ພິມຊື່ ແລະ ສູດຄິດໄລ່ (ຕົວຢ່າງ: hole_dia = 12, hole_spacing = 2 * hole_dia + 1.5). ກົດ Apply ເພື່ອບັນທຶກ ແລະ ຄິດໄລ່ໃໝ່; ການປິດຈະຍົກເລີກການແກ້ໄຂທີ່ຍັງບໍ່ໄດ້ບັນທຶກ. + .u-isolines-2 = ເສັ້ນໂຄງສ້າງ U (U isolines) + .union-created-result-object-s = UNION: ສ້າງວັດຖຸຜົນລັບແລ້ວ __ocs_arg_count__ ອັນ. + .union-select-at-least-two-solids-regions-or-planar-surfaces-of-the-same-type = UNION: ເລືອກວັດຖຸແຂງ, Regions, ຫຼື ພື້ນຜິວລະນາບ ປະເພດດຽວກັນຢ່າງໜ້ອຍສອງອັນ. + .union-the-selected-coincident-geometry-is-ambiguous = UNION: ຮູບຊົງເລຂາຄະນິດທີ່ທັບຊ້ອນກັນທີ່ເລືອກນັ້ນບໍ່ຊັດເຈນ. + .union-the-selected-geometry-includes-an-unsupported-surface-intersection = UNION: ຮູບຊົງທີ່ເລືອກລວມມີການຕັດກັນຂອງພື້ນຜິວທີ່ບໍ່ຮອງຮັບ. + .union-the-selected-topology-could-not-be-closed-safely = UNION: ໂຄງສ້າງຮູບຮ່າງທີ່ເລືອກບໍ່ສາມາດປິດໄດ້ຢ່າງປອດໄພ. + .use-a-previous-extraction-as-a-template = ໃຊ້ການດຶງຂໍ້ມູນກ່ອນໜ້ານີ້ເປັນແມ່ແບບ + .use-source-formatting = ໃຊ້ຮູບແບບຕາມຕົ້ນສະບັບ + .used-by = ຖືກນຳໃຊ້ໂດຍ + .v-isolines-2 = ເສັ້ນໂຄງສ້າງ V (V isolines) + .value-must-be-nonzero = ຄ່າຕ້ອງບໍ່ເທົ່າກັບສູນ. + .viewing-direction = ທິດທາງການເບິ່ງ + .wipeout-specify-next-point-or-close-undo = WIPEOUT ກຳນົດຈຸດຖັດໄປ ຫຼື [Close/Undo]: + .wipeout-specify-next-point-or-undo = WIPEOUT ກຳນົດຈຸດຖັດໄປ ຫຼື [Undo]: + .wipeout-specify-next-point = WIPEOUT ກຳນົດຈຸດຖັດໄປ: + .wireframe-type = ປະເພດໂຄງລວດ (Wireframe) + .mtp-specify-first-point-of-mid = _mtp ກຳນົດຈຸດທຳອິດຂອງເຄິ່ງກາງ: + .mtp-specify-second-point-of-mid = _mtp ກຳນົດຈຸດທີສອງຂອງເຄິ່ງກາງ: + .formula-2 = ສູດຄິດໄລ່ + .name = ຊື່ + .aligned-object-s = __ocs_fmt_0__: ຈັດລຽງວັດຖຸແລ້ວ __ocs_fmt_1__ ອັນ. + .select-annotative-objects = __ocs_fmt_0__ ເລືອກວັດຖຸຄຳອະທິບາຍ: + .parameter-s-applied = ນຳໃຊ້ __ocs_fmt_0__ ພາລາມິເຕີແລ້ວ. + .row-s-skipped-duplicate-parameter-name = ຂ້າມ __ocs_fmt_0__ ແຖວ: ຊື່ພາລາມິເຕີຊ້ຳກັນ. + .surface-s-cannot-be-thickened-because-the-offset-intersects-itself = ບໍ່ສາມາດເພີ່ມຄວາມໜາໃຫ້ __ocs_fmt_0__ ພື້ນຜິວ ຍ້ອນວ່າໄລຍະ offset ຕັດກັນເອງ. + .conflicting = ⚠ __ocs_fmt_0__ ຂັດແຍ້ງກັນ + .allow-exploding = ອະນຸຍາດໃຫ້ແຍກສ່ວນ + .angstroms = ອັງສະຕຣອມ + .astronomical-units = ຫົວໜ່ວຍດາຣາສາດ + .behavior = ພຶດຕິກຳ + .block-already-exists-will-redefine-upon-ok = ບລັອກ "__ocs_fmt_0__" ມີຢູ່ແລ້ວ — ຈະຖືກກຳນົດໃໝ່ເມື່ອກົດ OK + .block-creates-a-block-definition-from-objects-you-select = BLOCK ສ້າງຄຳນິຍາມບລັອກຈາກວັດຖຸທີ່ທ່ານເລືອກ. + .block-definition = ຄຳນິຍາມບລັອກ + .block-name = ຊື່ບລັອກ + .block-name-already-exists-do-you-want-to-redefine-it = + ບລັອກ "__ocs_fmt_0__" ມີຢູ່ແລ້ວ. + ທ່ານຕ້ອງການກຳນົດມັນໃໝ່ບໍ່? + .block-name-cannot-be-empty = ຊື່ບລັອກຕ້ອງບໍ່ຫວ່າງເປົ່າ. + .block-name-cannot-contain-invalid-chars = ຊື່ບລັອກຕ້ອງບໍ່ມີຕົວອັກສອນ: \ / : * ? " < > | = ` + .block-name-cannot-start-with-star = ຊື່ບລັອກຕ້ອງບໍ່ເລີ່ມຕົ້ນດ້ວຍ '*'. + .block-unit-label = ຫົວໜ່ວຍບລັອກ: + .convert-to-block = ແປງເປັນບລັອກ + .count-objects-selected = ເລືອກວັດຖຸແລ້ວ __ocs_fmt_0__ ອັນ + .decameters = ເດຄາແມັດ + .decimeters = ເດຊີແມັດ + .enter-block-description = ປ້ອນຄຳອະທິບາຍບລັອກ... + .gigameters = ກິກາແມັດ + .hectometers = ເຮັກໂຕແມັດ + .hyperlink-ellipsis = ໄຮເປີລິ້ງ... + .hyperlink-star = ໄຮເປີລິ້ງ* + .invalid-character-in-block-name = ຕົວອັກສອນບໍ່ຖືກຕ້ອງ '__ocs_fmt_0__' ໃນຊື່ບລັອກ (ບໍ່ອະນຸຍາດໃຫ້ມີ \ / : * ? " < > | = `) + .light-years = ປີແສງ + .match-block-orientation = ຈັດທິດທາງໃຫ້ກົງກັບບລັອກ + .microinches = ໄມໂຄຣນິ້ວ + .microns = ໄມຄຣອນ + .mils = ມິນ (Mils) + .nanometers = ນາໂນແມັດ + .no-objects-selected = ບໍ່ມີວັດຖຸຖືກເລືອກ + .no-objects-selected-you-must-select-objects-to-define-a-block = ບໍ່ມີວັດຖຸຖືກເລືອກ. ທ່ານຕ້ອງເລືອກວັດຖຸເພື່ອສ້າງບລັອກ. + .objects-title-case = ວັດຖຸ + .parsecs = ພາຣ໌ເຊັກ + .pick-point = ເລືອກຈຸດ + .retain = ຮັກສາໄວ້ + .scale-uniformly = ປັບມາດຕາສ່ວນໃຫ້ເທົ່າກັນທຸກດ້ານ + .specify-on-screen = ກຳນົດເທິງໜ້າຈໍ + .to-layout = ໄປທີ່ໜ້າຈັດວາງ (Layout) + .us-survey-feet = ຟຸດສຳຫຼວດສະຫະລັດ + .entire-drawing = ແບບແຕ້ມທັງໝົດ + .delete-from-drawing = ລຶບອອກຈາກແບບແຕ້ມ + .destination = ປາຍທາງ + .file-name-and-path = ຊື່ໄຟລ໌ ແລະ ທີ່ຢູ່: + .insert-units-label = ຫົວໜ່ວຍການແຊກ: + .wblock-err-no-block-name = ກະລຸນາເລືອກ ຫຼື ປ້ອນຊື່ບລັອກ. + .wblock-err-block-not-found = ບໍ່ພົບຊື່ບລັອກ "__ocs_fmt_0__" ໃນແບບແຕ້ມ. + .wblock-err-no-path = ກະລຸນາກຳນົດຊື່ໄຟລ໌ ແລະ ທີ່ຢູ່ໄຟລ໌. + .wblock-err-current-drawing = ບໍ່ສາມາດຂຽນທັບໄຟລ໌ແບບແຕ້ມປັດຈຸບັນໄດ້. + .wblock-help-info = WBLOCK ຂຽນວັດຖຸ, ບລັອກ, ຫຼື ແບບແຕ້ມທັງໝົດ ອອກໄປເປັນໄຟລ໌ແບບແຕ້ມໃໝ່. + .blank-drawing = ແບບແຕ້ມຫວ່າງເປົ່າ + .default-template-new-drawings = ແມ່ແບບເລີ່ມຕົ້ນສຳລັບແບບແຕ້ມໃໝ່ + .fillet-trim-mode-trim = FILLET ປ້ອນຕົວເລືອກໂໝດຕັດ [Trim/No trim] : + .fillet-trim-mode-no-trim = FILLET ປ້ອນຕົວເລືອກໂໝດຕັດ [Trim/No trim] : + .linetype-files = ໄຟລ໌ຮູບແບບເສັ້ນ + .load-linetypes = ໂຫຼດຮູບແບບເສັ້ນ + .requires-two-positive-values = ຕ້ອງການຄ່າບວກສອງຄ່າ (x,y) ຫຼື ຄ່າດຽວສຳລັບທັງສອງ. + .snap-marker-color = ສີເຄື່ອງໝາຍຈຸດດູດ + .command-line-text-color = ສີຂໍ້ຄວາມແຖວຄຳສັ່ງ + .operand = ຕົວດຳເນີນການ + .linetypes-loaded = ໂຫຼດຮູບແບບເສັ້ນແລ້ວ __ocs_fmt_0__ ຮູບແບບ. + .geometric-center-snap = ຈຸດໃຈກາງເລຂາຄະນິດ + .no-trim-option = ບໍ່ຕັດ (No trim) + .chspace-model-tab = CHSPACE: ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ຄຳສັ່ງນີ້ໃນແທັບ Model. + .chspace-no-viewport = CHSPACE: ບໍ່ມີຊ່ອງເບິ່ງແບບມຸມມອງຮາບເພື່ອປ່ຽນພື້ນທີ່ຜ່ານ. + .chspace-to-paper = CHSPACE: ປ່ຽນ __ocs_fmt_0__ ວັດຖຸ ໄປຍັງພື້ນທີ່ເຈ້ຍ (Paper space) ແລ້ວ. + .chspace-to-model = CHSPACE: ປ່ຽນ __ocs_fmt_0__ ວັດຖຸ ໄປຍັງພື້ນທີ່ແບບ (Model space) ແລ້ວ. + .select-template-title = ເລືອກແມ່ແບບ + .threedrotate-specify-base-point-count-objects = 3DROTATE ກຳນົດຈຸດຫຼັກ [__ocs_arg_count__ ວັດຖຸ]: + .threedrotate-specify-rotation-axis = 3DROTATE ກຳນົດແກນໝຸນ [X/Y/Z] ຫຼື ຈຸດທີສອງເທິງແກນໝຸນ : + .threedrotate-specify-rotation-angle-a = 3DROTATE ກຳນົດມຸມໝຸນ <__ocs_arg_a__>: +errors = + .save-warning = ແຈ້ງເຕືອນການບັນທຶກ + .unable-to-save-drawing = ບໍ່ສາມາດບັນທຶກແບບແຕ້ມໄດ້ + .warning = ແຈ້ງເຕືອນ + +command = + .name-select-objects-then-press-enter = __ocs_arg_name__ ເລືອກວັດຖຸ, ແລ້ວກົດ Enter: + .name-which-register-1-5 = __ocs_arg_name__ ຊ່ອງບັນທຶກໃດ? [1-5]: + .name-slot-new-value = __ocs_arg_name____ocs_arg_slot__ ຄ່າໃໝ່: + .cancel = *ຍົກເລີກ* + .add-alias = + ເພີ່ມຊື່ຫຍໍ້ + .alias-title-case = ຊື່ຫຍໍ້ + .cannot-add-a-vertex-here = ບໍ່ສາມາດເພີ່ມຈຸດຍອດຢູ່ນີ້ໄດ້. + .cannot-break-at-this-vertex = ບໍ່ສາມາດຕັດຂາດຢູ່ຈຸດຍອດນີ້ໄດ້. + .cannot-place-the-new-vertex = ບໍ່ສາມາດວາງຈຸດຍອດໃໝ່ໄດ້. + .command = ຄຳສັ່ງ + .command-aliases = ຊື່ຫຍໍ້ຄຳສັ່ງ + .key = ປຸ່ມ + .keyboard-shortcuts = ປຸ່ມລັດແປ້ນພິມ + .layer-0-and-the-current-layer-can-t-be-deleted = ຊັ້ນງານ "0" ແລະ ຊັ້ນງານປັດຈຸບັນບໍ່ສາມາດລຶບໄດ້ — ຂ້າມ. + .no-drawing-open-use-new-or-open-to-start-a-dra = ບໍ່ມີແບບແຕ້ມເປີດຢູ່. ໃຊ້ NEW ຫຼື OPEN ເພື່ອເລີ່ມຕົ້ນແບບແຕ້ມ. + .object-is-on-locked-layer-layer-unlock-the-lay = ວັດຖຸຢູ່ໃນຊັ້ນງານທີ່ລັອກໄວ້ "__ocs_fmt_0__" — ປົດລັອກຊັ້ນງານເພື່ອແກ້ໄຂຄຸນລັກສະນະຂອງມັນ. + .pan-ended = ສິ້ນສຸດການເລື່ອນມຸມມອງ (PAN). + .polyline-broken-at-vertex = ຕັດໂພລີລາຍຂາດຢູ່ຈຸດຍອດແລ້ວ. + .rename-select-the-object-type-to-rename = RENAME ເລືອກປະເພດວັດຖຸທີ່ຈະປ່ຽນຊື່: + .rename-type-enter-the-current-name = RENAME __ocs_arg_type__ ປ້ອນຊື່ປັດຈຸບັນ: + .rename-type-rename-old-to = RENAME __ocs_arg_type__ ປ່ຽນຊື່ "__ocs_arg_old__" ເປັນ: + .specify-new-vertex-location = ກຳນົດຕຳແໜ່ງຈຸດຍອດໃໝ່: + .type-shortcuts-set-key-cmd-to-add-custom-short = ພິມ SHORTCUTS SET ເພື່ອເພີ່ມປຸ່ມລັດຕາມໃຈ. + .type-an-alias-and-the-command-it-runs-e-g-l-li = ພິມຊື່ຫຍໍ້ ແລະ ຄຳສັ່ງທີ່ມັນສັ່ງການ (ຕົວຢ່າງ: L → LINE). ກົດ Apply ເພື່ອບັນທຶກລົງ ocad.pgp; ການປິດຈະຍົກເລີກການແກ້ໄຂທີ່ຍັງບໍ່ໄດ້ບັນທຶກ. + .unknown-command-cmd = ຄຳສັ່ງບໍ່ຮູ້ຈັກ: __ocs_fmt_0__ + .alias-lowercase = ຊື່ຫຍໍ້ + .label = __ocs_fmt_0__: + .expected-a-number-got-raw = __ocs_fmt_0__: ຕ້ອງການຕົວເລກ, ແຕ່ໄດ້ຮັບ "__ocs_fmt_1__" + .tcount-select-text-objects-then-press-enter = TCOUNT ເລືອກວັດຖຸຂໍ້ຄວາມ, ແລ້ວກົດ Enter: + .tcount-starting-number-start = TCOUNT ຕົວເລກເລີ່ມຕົ້ນ <__ocs_fmt_0__>: + .tcount-increment-increment = TCOUNT ຄ່າເພີ່ມຂຶ້ນເທື່ອລະ <__ocs_fmt_0__>: + .tcount-placement-overwrite-prefix-suffix-overw = TCOUNT ຕຳແໜ່ງວາງ [Overwrite/Prefix/Suffix] : + +file = + .parse-ms-purge-ms-caches-ms-xref-ms-total-ms = ວິເຄາະ __ocs_fmt_0__ms · ລ້າງຂໍ້ມູນ __ocs_fmt_1__ms · ແຄຊ __ocs_fmt_2__ms · xref __ocs_fmt_3__ms · ລວມທັງໝົດ __ocs_fmt_4__ms + .archive-save-the-drawing-first-it-has-no-file = ARCHIVE: ບັນທຶກແບບແຕ້ມກ່ອນ (ຍັງບໍ່ທັນມີທີ່ຢູ່ໄຟລ໌). + .autosaved-1-drawing = ບັນທຶກແບບແຕ້ມອັດຕະໂນມັດແລ້ວ 1 ແບບແຕ້ມ + .background-colour-default-black-darkgray-gray = BACKGROUND ສີ [Default/Black/DarkGray/Gray/LightGray/White] ຫຼື R G B (0–255): + .background-reset-to-default = ຕັ້ງຄ່າສີພື້ນຫຼັງຄືນເປັນຄ່າເລີ່ມຕົ້ນແລ້ວ. + .background-rgb = ພື້ນຫຼັງ: rgb(__ocs_fmt_0__, __ocs_fmt_1__, __ocs_fmt_2__) + .created-new-ctb-table-aci-aci-updated = ສ້າງຕາຕະລາງ CTB ໃໝ່ແລ້ວ, ອັບເດດ ACI __ocs_fmt_0__ ແລ້ວ. + .default-save-format = ຮູບແບບການບັນທຶກເລີ່ມຕົ້ນ: + .do-you-want-to-save-changes-to-name = ທ່ານຕ້ອງການບັນທຶກການປ່ຽນແປງໃນ "__ocs_arg_name__" ບໍ່? + .drawing-changed-on-disk = ແບບແຕ້ມມີການປ່ຽນແປງໃນດິສກ໌ + .drawing-units-insunits-click-to-change = + ຫົວໜ່ວຍແບບແຕ້ມ (INSUNITS) + ກົດຄລິກເພື່ອປ່ຽນແປງ + .drawing-changed-on-disk-while-it-was-opening-s = ແບບແຕ້ມມີການປ່ຽນແປງໃນດິສກ໌ຂະນະທີ່ກຳລັງເປີດຢູ່; ການບັນທຶກຈະຕ້ອງແກ້ໄຂຂໍ້ຂັດແຍ້ງກ່ອນ. + .edit-lease-active-warning-external-change-chec = ການຄອບຄອງສິດແກ້ໄຂຍັງເຮັດວຽກຢູ່; __ocs_fmt_0__. ການກວດສອບການປ່ຽນແປງຈາກພາຍນອກຍັງຄົງເຮັດວຽກ. + .files-you-open-will-show-up-here = ໄຟລ໌ທີ່ທ່ານເປີດຈະສະແດງຢູ່ນີ້. + .new-drawing = ແບບແຕ້ມໃໝ່ + .no-plot-style-table-loaded-load-or-create-one = ບໍ່ມີຕາຕະລາງຮູບແບບການພິມຖືກໂຫຼດ. ກະລຸນາໂຫຼດ ຫຼື ສ້າງໃໝ່ກ່ອນ. + .nothing-to-plot-model-space-contains-no-printa = ບໍ່ມີສິ່ງໃດໃຫ້ພິມ: ພື້ນທີ່ Model ບໍ່ມີຮູບຊົງທີ່ສາມາດພິມໄດ້. + .oops-nothing-to-restore = OOPS: ບໍ່ມີຫຍັງໃຫ້ກູ້ຄືນ. + .oops-restored-n-object-s = OOPS: ກູ້ຄືນວັດຖຸແລ້ວ __ocs_fmt_0__ ອັນ. + .open-cad-studio-ready = Open CAD Studio ພ້ອມໃຊ້ງານແລ້ວ. + .open-discussions-on-github = ເປີດຫົວຂໍ້ສົນທະນາເທິງ GitHub + .open-file-title-case = ເປີດໄຟລ໌ + .open-file-picker-title-case = ເປີດໄຟລ໌… + .open-and-save = ເປີດ ແລະ ບັນທຶກ + .open-playlist-on-youtube = ເປີດລາຍການວິດີໂອເທິງ YouTube + .opened-name-entity-count-entities = ເປີດ "__ocs_fmt_0__" ແລ້ວ — ມີ __ocs_fmt_1__ ອົງປະກອບ + .opened-printer-properties = ເປີດຄຸນສົມບັດເຄື່ອງພິມແລ້ວ. + .opened-read-only-against-other-editors-error = ເປີດແບບອ່ານຢ່າງດຽວເນື່ອງຈາກມີໂປຣແກຣມອື່ນເປີດຢູ່: __ocs_fmt_0__ + .page-setup-applied-to-the-layout = ນຳໃຊ້ການຕັ້ງຄ່າໜ້າເຈ້ຍກັບ Layout ແລ້ວ. + .path-type = ປະເພດທີ່ຢູ່ໄຟລ໌: + .plot-area-is-empty-pick-a-larger-window = ພື້ນທີ່ພິມຫວ່າງເປົ່າ. ກະລຸນາເລືອກຂອບເຂດໜ້າຕ່າງທີ່ກວ້າງກວ່າເກົ່າ. + .plot-style-aci-aci-updated = ອັບເດດຮູບແບບການພິມ ACI __ocs_fmt_0__ ແລ້ວ. + .plot-style-table-is-not-loaded = ຕາຕະລາງຮູບແບບການພິມ '__ocs_fmt_0__' ຍັງບໍ່ໄດ້ໂຫຼດ. + .quick-print-works-in-model-space = ການພິມດ່ວນໃຊ້ງານໄດ້ໃນພື້ນທີ່ Model. + .reload-failed-error = ການໂຫຼດຄືນໃໝ່ລົ້ມເຫຼວ: __ocs_fmt_0__ + .saveall-save-each-tab-individually-in-the-web = SAVEALL: ບັນທຶກແຕ່ລະແທັບແຍກກັນໃນເວີຊັນເວັບ. + .saveall-saved-saved-drawing-s = SAVEALL: ບັນທຶກແບບແຕ້ມແລ້ວ __ocs_fmt_0__ ແບບແຕ້ມ__ocs_fmt_1__. + .saveall-already-has-a-save-running = SAVEALL: __ocs_fmt_0__ ກຳລັງຢູ່ໃນລະຫວ່າງການບັນທຶກຢູ່ແລ້ວ + .saveall-failed-e = SAVEALL: __ocs_fmt_0__ ລົ້ມເຫຼວ: __ocs_fmt_1__ + .script-path-to-the-scr-file = SCRIPT ທີ່ຢູ່ຂອງໄຟລ໌ .scr: + .script-cannot-read-p-e = SCRIPT: ບໍ່ສາມາດອ່ານ __ocs_fmt_0__ ໄດ້: __ocs_fmt_1__ + .script-running-command-s-from-p = SCRIPT: ກຳລັງດຳເນີນການ __ocs_fmt_0__ ຄຳສັ່ງ ຈາກ __ocs_fmt_1__. + .save-failed-error = ການບັນທຶກລົ້ມເຫຼວ: __ocs_fmt_0__ + .save-failed-e = ການບັນທຶກລົ້ມເຫຼວ: __ocs_fmt_0__ + .save-stopped-file-name-changed-outside-open-ca = ຢຸດການບັນທຶກ: "__ocs_fmt_0__" ມີການປ່ຽນແປງຈາກພາຍນອກ Open CAD Studio. + .saved-but-the-refreshed-drawing-is-locked-by-a = ບັນທຶກແລ້ວ, ແຕ່ແບບແຕ້ມທີ່ໂຫຼດໃໝ່ຖືກລັອກໂດຍໂປຣແກຣມອື່ນ: __ocs_fmt_0__ + .saved-filename = ບັນທຶກແລ້ວ: __ocs_fmt_0__ + .saved = ບັນທຶກແລ້ວ: __ocs_fmt_0__ + .selection-has-no-printable-area = ພາກສ່ວນທີ່ເລືອກບໍ່ມີພື້ນທີ່ທີ່ສາມາດພິມໄດ້. + .selection-has-no-printable-geometry = ພາກສ່ວນທີ່ເລືອກບໍ່ມີຮູບຊົງເລຂາຄະນິດທີ່ສາມາດພິມໄດ້. + .sending-to-system-printer = ກຳລັງສົ່ງໄປຍັງເຄື່ອງພິມຂອງລະບົບ… + .type-a-command-or-use-the-ribbon-open-obj-inse = ພິມຄຳສັ່ງ ຫຼື ໃຊ້ແຖບ Ribbon. ເປີດ OBJ: ແທັບ INSERT. + .unable-to-acquire-edit-lock-error = ບໍ່ສາມາດຂໍສິດລັອກການແກ້ໄຂໄດ້: __ocs_fmt_0__ + .unable-to-save-file-name-file-is-in-use-by-ano = ບໍ່ສາມາດບັນທຶກ "__ocs_fmt_0__" ໄດ້: ໄຟລ໌ກຳລັງຖືກໃຊ້ງານໂດຍໂປຣແກຣມອື່ນ. + .unable-to-save-error = ບໍ່ສາມາດບັນທຶກ "__ocs_fmt_0__" ໄດ້: __ocs_fmt_1__ + .usage-script-path-to-scr-file = ວິທີໃຊ້: SCRIPT <ທີ່ຢູ່ໄຟລ໌ .scr> + .usage-undo-number-of-steps = ວິທີໃຊ້: UNDO [ຈຳນວນຂັ້ນຕອນ] + .used-for-the-first-save-of-a-new-drawing-exist = ໃຊ້ສຳລັບການບັນທຶກຄັ້ງທຳອິດຂອງແບບແຕ້ມໃໝ່. ແບບແຕ້ມທີ່ມີຢູ່ແລ້ວຈະຮັກສາປະເພດໄຟລ໌ ແລະ ເວີຊັນເດີມໄວ້. + .visual-style-label = ຮູບແບບມຸມມອງ: __ocs_fmt_0__. + .warning-corrupt-entities-dropped-parser-junk-b = ຄຳເຕືອນ: ຕັດອົງປະກອບທີ່ເສຍຫາຍຖິ້ມ __ocs_fmt_0__ ອັນ (ຂໍ້ມູນເສດຈາກການວິເຄາະ — ຄ່າແນວຕັ້ງສາກ/ຈຳນວນຜິດພາດ) + .warning-corrupt-xref-entities-dropped = ຄຳເຕືອນ: ຕັດອົງປະກອບ xref ທີ່ເສຍຫາຍຖິ້ມ __ocs_fmt_0__ ອັນ + .xref-loaded = XREF ໂຫຼດແລ້ວ "__ocs_fmt_0__" + .cmd-cannot-create-folder-e = __ocs_fmt_0__: ບໍ່ສາມາດສ້າງໂຟລເດີໄດ້ (__ocs_fmt_1__). + .cmd-packaged-copied-file-s-into = __ocs_fmt_0__: ລວມໄຟລ໌ ແລະ ສຳເນົາ __ocs_fmt_1__ ໄຟລ໌ ລົງໃນ __ocs_fmt_2__ + .error-external-change-checks-remain-active = __ocs_fmt_0__. ການກວດສອບການປ່ຽນແປງຈາກພາຍນອກຍັງຄົງເຮັດວຽກ. + .warning-external-change-checks-remain-active = __ocs_fmt_0__. ການກວດສອບການປ່ຽນແປງຈາກພາຍນອກຍັງຄົງເຮັດວຽກ. + .opening-a-drawing-by-path-is-not-available-in = ການເປີດແບບແຕ້ມດ້ວຍທີ່ຢູ່ໄຟລ໌ບໍ່ສາມາດໃຊ້ງານໄດ້ເທິງເວັບບຣາວເຊີ. + +selection = + .cmd-select-objects-count-selected-enter-to-app = __ocs_arg_cmd__ ເລືອກວັດຖຸ (ເລືອກແລ້ວ __ocs_arg_count__ ອັນ, ກົດ Enter ເພື່ອນຳໃຊ້): + .cmd-select-objects = __ocs_arg_cmd__ ເລືອກວັດຖຸ: + .append-to-current-selection = ເພີ່ມໃສ່ສ່ວນທີ່ເລືອກປັດຈຸບັນ + .last = ຫຼ້າສຸດ + +inquiry = + .handle-x = Handle __ocs_fmt_0__: "__ocs_fmt_1__" + .k-n = __ocs_fmt_0__: __ocs_fmt_1__ + .v = = __ocs_fmt_0__ + .addselected-creating-a-new-kind-is-not-support = ADDSELECTED: ບໍ່ຮອງຮັບການສ້າງ __ocs_fmt_0__ ໃໝ່. + .addselected-selected-object-not-found = ADDSELECTED: ບໍ່ພົບວັດຖຸທີ່ເລືອກ. + .add-selected-drawing-a-new-kind-on-layer-layer = Add Selected: ກຳລັງແຕ້ມ __ocs_fmt_0__ ໃໝ່ ເທິງຊັ້ນງານ "__ocs_fmt_1__". + .angle-angle = ມຸມ = __ocs_arg_angle__° + .angle-0-0000-degenerate-rays = ມຸມ = 0.0000° (ລັງສີຮ່ວມແນວດຽວກັນ) + .area-area-perimeter-perimeter = ເນື້ອທີ່ = __ocs_arg_area__, ລວງຮອບ = __ocs_arg_perimeter__ + .bedit-block-edit-discarded = BEDIT: ຍົກເລີກການແກ້ໄຂບລັອກ "__ocs_fmt_0__" ແລ້ວ. + .bedit-block-saved-all-references-updated = BEDIT: ບັນທຶກບລັອກ "__ocs_fmt_0__" ແລ້ວ. ອັບເດດທຸກການອ້າງອີງແລ້ວ. + .bedit-editing-block-use-save-block-or-discard = BEDIT: ກຳລັງແກ້ໄຂບລັອກ "__ocs_fmt_0__". ໃຊ້ Save Block ຫຼື Discard ເພື່ອສຳເລັດ. + .bedit-block-not-found = BEDIT: ບໍ່ພົບຊື່ບລັອກ "__ocs_fmt_0__". + .bedit-cannot-edit-an-external-reference-xref = BEDIT: ບໍ່ສາມາດແກ້ໄຂການອ້າງອີງພາຍນອກ (xref) ໄດ້. + .bedit-finish-the-active-refedit-refclose-first = BEDIT: ກະລຸນາສຳເລັດ REFEDIT (REFCLOSE) ທີ່ກຳລັງເປີດຢູ່ກ່ອນ. + .bedit-selected-object-is-not-a-block-reference = BEDIT: ວັດຖຸທີ່ເລືອກບໍ່ແມ່ນການອ້າງອີງບລັອກ. + .bedit-discard-invalid-block-editor-state = BEDIT_DISCARD: ສະຖານະຕົວແກ້ໄຂບລັອກບໍ່ຖືກຕ້ອງ. + .bedit-discard-no-block-editor-is-open = BEDIT_DISCARD: ບໍ່ມີຕົວແກ້ໄຂບລັອກເປີດຢູ່. + .bedit-save-invalid-block-editor-state = BEDIT_SAVE: ສະຖານະຕົວແກ້ໄຂບລັອກບໍ່ຖືກຕ້ອງ. + .bedit-save-no-block-editor-is-open = BEDIT_SAVE: ບໍ່ມີຕົວແກ້ໄຂບລັອກເປີດຢູ່. + .cal-expression-e-g-2-3-4 = CAL ນິພົດ (ຕົວຢ່າງ: (2+3)*4): + .cal-e = CAL: __ocs_fmt_0__ + .count-tally-all-by-type-by-layer = COUNT ນັບລວມ [All (ແຍກຕາມປະເພດ) / by Layer (ແຍກຕາມຊັ້ນງານ)]: + .count-total-entity-ies-total = COUNT: ລວມທັງໝົດ __ocs_fmt_0__ ອົງປະກອບ. + .dblist-drawing-has-no-entities = DBLIST: ແບບແຕ້ມບໍ່ມີອົງປະກອບໃດໆ. + .dblist-count-entit = DBLIST: __ocs_fmt_0__ ວັດຖຸ. + .dist-specify-first-point = DIST ກຳນົດຈຸດທຳອິດ: + .dist-specify-second-point = DIST ກຳນົດຈຸດທີສອງ: + .divide-enter-number-of-segments = DIVIDE ປ້ອນຈຳນວນສ່ວນແບ່ງ: + .divide-select-object-to-divide = DIVIDE ເລືອກວັດຖຸທີ່ຈະແບ່ງ: + .deselected = ຍົກເລີກການເລືອກແລ້ວ. + .distance-dist-angle-in-xy-plane-angle-xy-delta = + ໄລຍະຫ່າງ = __ocs_arg_dist__, ມຸມໃນລະນາບ XY = __ocs_arg_angle_xy__° + Delta X = __ocs_arg_dx__, Delta Y = __ocs_arg_dy__, Delta Z = __ocs_arg_dz__ + .distance-dist-angle-in-xy-plane-angle-xy-angle = + ໄລຍະຫ່າງ = __ocs_arg_dist__, ມຸມໃນລະນາບ XY = __ocs_arg_angle_xy__°, ມຸມຈາກລະນາບ XY = __ocs_arg_angle_z__° + Delta X = __ocs_arg_dx__, Delta Y = __ocs_arg_dy__, Delta Z = __ocs_arg_dz__ + .find-replace-replaced-occurrence-s-of = FIND/REPLACE: ປ່ຽນແທນ __ocs_fmt_0__ ບ່ອນ ຈາກ "__ocs_fmt_1__" → "__ocs_fmt_2__". + .find-not-found = FIND: ບໍ່ພົບ "__ocs_fmt_0__". + .find-specify-search-text = FIND: ກຳນົດຂໍ້ຄວາມຄົ້ນຫາ. + .find-match-es-for = FIND: ພົບ __ocs_fmt_0__ ບ່ອນທີ່ກົງກັບ "__ocs_fmt_1__". + .findall-text-to-find-add-replace-text-by-typin = FINDALL ຂໍ້ຄວາມທີ່ຕ້ອງການຄົ້ນຫາ (ເພີ່ມ REPLACE <ຂໍ້ຄວາມ> ໂດຍການພິມ): + .flatten-no-entities = FLATTEN: ບໍ່ມີອົງປະກອບ. + .flatten-entity-ies-moved-to-z-0 = FLATTEN: ຍ້າຍ __ocs_fmt_0__ ອົງປະກອບ ໄປຍັງ Z=0 ແລ້ວ. + .id-specify-point = ID ກຳນົດຈຸດ: + .list-no-entities-selected-select-entities-firs = LIST: ບໍ່ມີອົງປະກອບຖືກເລືອກ. ກະລຸນາເລືອກອົງປະກອບກ່ອນ. + .massprop-no-entities-selected-select-entities = MASSPROP: ບໍ່ມີອົງປະກອບຖືກເລືອກ. ກະລຸນາເລືອກອົງປະກອບກ່ອນ. + .massprop-has-no-closed-body-to-measure = MASSPROP: __ocs_fmt_0__ ບໍ່ມີຕົວປິດໃຫ້ວັດແທກ. + .volume-4-centroid-4-4-4 = __ocs_fmt_0__ ປະລິມາດ=__ocs_fmt_1__ ຈຸດສູນກາງມວນ=(__ocs_fmt_2__,__ocs_fmt_3__,__ocs_fmt_4__) + .moments-of-inertia-x-4-y-4-z-4 = ໂມເມັນຄວາມເສື່ອຍ X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .products-of-inertia-xy-4-yz-4-zx-4 = ຜົນຄູນຄວາມເສື່ອຍ XY=__ocs_fmt_0__ YZ=__ocs_fmt_1__ ZX=__ocs_fmt_2__ + .radii-of-gyration-x-4-y-4-z-4 = ລັດສະໝີການໝູນ X=__ocs_fmt_0__ Y=__ocs_fmt_1__ Z=__ocs_fmt_2__ + .principal-moments-about-centroid-i-4-j-4-k-4 = ໂມເມັນຫຼັກອ້ອມຈຸດສູນກາງມວນ I=__ocs_fmt_0__ J=__ocs_fmt_1__ K=__ocs_fmt_2__ + .measure-select-object-to-measure = MEASURE ເລືອກວັດຖຸທີ່ຈະວັດແທກ: + .measure-specify-segment-length = MEASURE ກຳນົດຄວາມຍາວຂອງສ່ວນແບ່ງ: + .measuregeom-enter-an-option-distance-radius-an = MEASUREGEOM ປ້ອນຕົວເລືອກ [Distance/Radius/Angle/ARea]: + .measuregeom-select-arc-or-circle = MEASUREGEOM ເລືອກສ່ວນໂຄ້ງ ຫຼື ວົງມົນ: + .measuregeom-specify-first-corner-point-enter-t = MEASUREGEOM ກຳນົດຈຸດມຸມທຳອິດ (ກົດ Enter ເພື່ອຍົກເລີກ): + .measuregeom-specify-first-point = MEASUREGEOM ກຳນົດຈຸດທຳອິດ: + .measuregeom-specify-first-ray-point = MEASUREGEOM ກຳນົດຈຸດເລີ່ມຕົ້ນຂອງລັງສີ: + .measuregeom-specify-next-point-n-picked-enter = MEASUREGEOM ກຳນົດຈຸດຖັດໄປ (ເລືອກແລ້ວ __ocs_arg_n__ ຈຸດ, ກົດ Enter ເພື່ອຄິດໄລ່): + .measuregeom-specify-second-point = MEASUREGEOM ກຳນົດຈຸດທີສອງ: + .measuregeom-specify-second-ray-point = MEASUREGEOM ກຳນົດຈຸດທີສອງຂອງລັງສີ: + .measuregeom-specify-vertex-point = MEASUREGEOM ກຳນົດຈຸດຍອດມຸມ: + .measure-geometry = ວັດແທກຮູບຊົງເລຂາຄະນິດ + .qselect-no-matching-entities = QSELECT: ບໍ່ມີອົງປະກອບທີ່ກົງກັນ. + .qselect-entity-ies-selected = QSELECT: ເລືອກແລ້ວ __ocs_fmt_0__ ອົງປະກອບ. + .refclose-block-saved-all-references-updated = REFCLOSE: ບັນທຶກບລັອກ "__ocs_fmt_0__" ແລ້ວ. ອັບເດດທຸກການອ້າງອີງແລ້ວ. + .refclose-changes-discarded = REFCLOSE: ຍົກເລີກການປ່ຽນແປງແລ້ວ. + .refclose-no-refedit-session-active = REFCLOSE: ບໍ່ມີຊ່ວງເວລາການເຮັດວຽກ REFEDIT ເປີດຢູ່. + .refedit-editing-block-run-refclose-to-save-ref = REFEDIT: ກຳລັງແກ້ໄຂບລັອກ "__ocs_fmt_0__". ພິມ REFCLOSE ເພື່ອບັນທຶກ, ຫຼື REFCLOSE_DISCARD ເພື່ອຍົກເລີກ. + .refedit-a-session-is-already-active-use-refclo = REFEDIT: ມີການເຮັດວຽກເປີດຢູ່ແລ້ວ. ກະລຸນາໃຊ້ REFCLOSE ກ່ອນ. + .refedit-block-not-found = REFEDIT: ບໍ່ພົບຊື່ບລັອກ "__ocs_fmt_0__". + .refedit-block-is-empty = REFEDIT: ບລັອກຫວ່າງເປົ່າ. + .refedit-selected-object-is-not-an-insert = REFEDIT: ວັດຖຸທີ່ເລືອກບໍ່ແມ່ນ INSERT. + .radius-radius-diameter-diameter = ລັດສະໝີ = __ocs_arg_radius__, ເສັ້ນຜ່າກາງ = __ocs_arg_diameter__ + .selectall-object-s-selected = SELECTALL: ເລືອກແລ້ວ __ocs_fmt_0__ ວັດຖຸ. + .selhandles-no-entities-selected-select-entitie = SELHANDLES: ບໍ່ມີອົງປະກອບຖືກເລືອກ. ກະລຸນາເລືອກອົງປະກອບກ່ອນ. + .usage-cal-expression-e-g-cal-2-3-4 = ວິທີໃຊ້: CAL <ນິພົດ> ຕົວຢ່າງ: CAL (2+3)*4 + .usage-qselect-type-layer-color-linetype-value = ວິທີໃຊ້: QSELECT TYPE|LAYER|COLOR|LINETYPE <ຄ່າ> + .x-x-y-y-z-z = X = __ocs_arg_x__, Y = __ocs_arg_y__, Z = __ocs_arg_z__ + .type-name-handle-x-layer-color-lt = __ocs_fmt_0__ Handle:__ocs_fmt_1__ ຊັ້ນງານ:__ocs_fmt_2__ ສີ:__ocs_fmt_3__ LT:__ocs_fmt_4____ocs_fmt_5__ + .area-4-perimeter-4-centroid-4-4 = __ocs_fmt_0__ ເນື້ອທີ່=__ocs_fmt_1__ ລວງຮອບ=__ocs_fmt_2__ ຈຸດສູນຖ່ວງ=(__ocs_fmt_3__,__ocs_fmt_4__) + +draw = + .insert-x-no-attributes = ແຊກ __ocs_fmt_0__: ບໍ່ມີຄຸນລັກສະນະ. + .tag-val = [__ocs_fmt_0__] = __ocs_fmt_1__ + .no-closed-boundary-found = ⚠ ບໍ່ພົບຂອບເຂດປິດ. + .inverted = , ກັບດ້ານ + .loaded-multiline-styles = ຮູບແບບເສັ້ນຄູ່ທີ່ໂຫຼດແລ້ວ: __ocs_fmt_0__ + .mline-current-settings = MLINE ການຕັ້ງຄ່າປັດຈຸບັນ: ການຈັດແນວ = __ocs_fmt_0__, ມາດຕາສ່ວນ = __ocs_fmt_1__, ຮູບແບບ = __ocs_fmt_2__ + ກຳນົດຈຸດເລີ່ມຕົ້ນ ຫຼື [Justification/Scale/STyle]: + .mline-enter-justification = MLINE ປ້ອນປະເພດການຈັດແນວ [Top/Zero/Bottom] <__ocs_fmt_0__>: + .mline-enter-scale-factor-default = MLINE ປ້ອນຕົວຄູນມາດຕາສ່ວນ <__ocs_fmt_0__>: + .mline-enter-style-name = MLINE ປ້ອນຊື່ຮູບແບບ ຫຼື [?] <__ocs_fmt_0__>: + .mline-specify-next-point-close-undo = MLINE ກຳນົດຈຸດຖັດໄປ ຫຼື [Close/Undo]: + .mline-specify-next-point-undo = MLINE ກຳນົດຈຸດຖັດໄປ ຫຼື [Undo]: + .multiline-style-was-not-found = ບໍ່ພົບຮູບແບບເສັ້ນຄູ່ "__ocs_arg_style__". + .value-2-point = 2 ຈຸດ + .value-2d-solid = ຮູບແຂງ 2D + .value-3-point = 3 ຈຸດ + .value-3d-polyline = ໂພລີລາຍ 3D + .value-3dmirror-mirror-plane-x-y-z = 3DMIRROR ລະນາບສະທ້ອນ [X / Y / Z]: + .value-3dpoly-specify-next-point-close-undo = 3DPOLY ກຳນົດຈຸດຖັດໄປ [Close/Undo]: + .value-3dpoly-specify-next-point-undo = 3DPOLY ກຳນົດຈຸດຖັດໄປ [Undo]: + .value-3dpoly-specify-start-point-of-polyline = 3DPOLY ກຳນົດຈຸດເລີ່ມຕົ້ນຂອງໂພລີລາຍ: + .value-3drotate-angle-in-degrees = 3DROTATE ມຸມເປັນອົງສາ: + .value-3drotate-rotation-axis-x-y-z = 3DROTATE ແກນໝຸນ [X / Y / Z]: + .arc-specify-center = ARC ກຳນົດຈຸດໃຈກາງ: + .arc-specify-end-point-c-cx-cy-r-r = ARC ກຳນົດຈຸດສິ້ນສຸດ [c=(__ocs_arg_cx__,__ocs_arg_cy__) r=__ocs_arg_r__]: + .arc-specify-start-point = ARC ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .arc-3p-specify-end-point = ARC 3P ກຳນົດຈຸດສິ້ນສຸດ: + .arc-3p-specify-second-point-on-arc = ARC 3P ກຳນົດຈຸດທີສອງເທິງສ່ວນໂຄ້ງ: + .arc-3p-specify-start-point = ARC 3P ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .arc-csa-click-end-direction-or-type-arc-span-i = ARC CSA ກົດເລືອກທິດທາງສິ້ນສຸດ ຫຼື ພິມຂະໜາດມຸມສ່ວນໂຄ້ງເປັນອົງສາ [start=__ocs_arg_sa__]: + .arc-csa-specify-center = ARC CSA ກຳນົດຈຸດໃຈກາງ: + .arc-csa-specify-start-point = ARC CSA ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .arc-csl-click-chord-end-or-type-chord-length-r = ARC CSL ກົດຈຸດປາຍສາຍກົ່ງ ຫຼື ພິມຄວາມຍາວສາຍກົ່ງ [r=__ocs_arg_r__]: + .arc-csl-specify-center = ARC CSL ກຳນົດຈຸດໃຈກາງ: + .arc-csl-specify-start-point = ARC CSL ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .arc-continue-no-previous-line-or-arc-to-contin = ARC Continue ບໍ່ມີເສັ້ນຊື່ ຫຼື ສ່ວນໂຄ້ງກ່ອນໜ້ານີ້ທີ່ຈະຕໍ່ເນື່ອງ. + .arc-continue-specify-end-point-ctrl-flip-direc = ARC Continue ກຳນົດຈຸດສິ້ນສຸດ [Ctrl = ປ່ຽນທິດທາງ]: + .arc-sca-click-end-direction-or-type-arc-span-i = ARC SCA ກົດເລືອກທິດທາງສິ້ນສຸດ ຫຼື ພິມຂະໜາດມຸມສ່ວນໂຄ້ງເປັນອົງສາ [start=__ocs_arg_sa__]: + .arc-sca-specify-center = ARC SCA ກຳນົດຈຸດໃຈກາງ: + .arc-sca-specify-start-point = ARC SCA ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .arc-sce-specify-center = ARC SCE ກຳນົດຈຸດໃຈກາງ: + .arc-sce-specify-end-point-r-r = ARC SCE ກຳນົດຈຸດສິ້ນສຸດ [r=__ocs_arg_r__]: + .arc-sce-specify-start-point = ARC SCE ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .arc-scl-click-chord-end-or-type-chord-length-r = ARC SCL ກົດຈຸດປາຍສາຍກົ່ງ ຫຼື ພິມຄວາມຍາວສາຍກົ່ງ [r=__ocs_arg_r__]: + .arc-scl-specify-center = ARC SCL ກຳນົດຈຸດໃຈກາງ: + .arc-scl-specify-start-point = ARC SCL ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .arc-sea-specify-angle-move-cursor-perpendicula = ARC SEA ກຳນົດມຸມ (ຍ້າຍເຄີເຊີຕັ້ງສາກກັບສາຍກົ່ງ): + .arc-sea-specify-end-point = ARC SEA ກຳນົດຈຸດສິ້ນສຸດ: + .arc-sea-specify-start-point = ARC SEA ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .arc-sed-specify-end-point = ARC SED ກຳນົດຈຸດສິ້ນສຸດ: + .arc-sed-specify-start-point = ARC SED ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .arc-sed-specify-tangent-direction-at-start = ARC SED ກຳນົດທິດທາງເສັ້ນສຳຜັດຕອນເລີ່ມຕົ້ນ: + .arc-ser-click-radius-point-or-type-radius-valu = ARC SER ກົດເລືອກຈຸດລັດສະໝີ ຫຼື ພິມຄ່າລັດສະໝີ: + .arc-ser-specify-end-point = ARC SER ກຳນົດຈຸດສິ້ນສຸດ: + .arc-ser-specify-start-point = ARC SER ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .area-specify-first-corner-point-enter-to-cance = AREA ກຳນົດຈຸດມຸມທຳອິດ (ກົດ Enter ເພື່ອຍົກເລີກ): + .area-specify-next-point-n-picked-enter-to-calc = AREA ກຳນົດຈຸດຖັດໄປ (ເລືອກແລ້ວ __ocs_arg_n__ ຈຸດ, ກົດ Enter ເພື່ອຄິດໄລ່): + .attdef-enter-attribute-tag-no-spaces = ATTDEF ປ້ອນແທັກຄຸນລັກສະນະ (ຫ້າມມີຍະຫວ່າງ): + .attdef-enter-default-value-for-tag-enter-blank = ATTDEF ປ້ອນຄ່າເລີ່ມຕົ້ນສຳລັບ '__ocs_arg_tag__' (ກົດ Enter=ຫວ່າງເປົ່າ): + .attdef-enter-prompt-for-tag-enter-use-tag = ATTDEF ປ້ອນຂໍ້ຄວາມແນະນຳສຳລັບ '__ocs_arg_tag__' (ກົດ Enter=ໃຊ້ແທັກ): + .attdef-specify-insertion-point-for-tag = ATTDEF ກຳນົດຈຸດແຊກສຳລັບ '__ocs_arg_tag__': + .attdisp-attribute-display-on-off-normal = ATTDISP ການສະແດງຄຸນລັກສະນະ [On / Off / Normal]: + .attdisp-sub-count-attribute-definition-s-updat = ATTDISP __ocs_fmt_0__: ອັບເດດ __ocs_fmt_1__ ຄຳນິຍາມຄຸນລັກສະນະແລ້ວ. + .attedit-no-insert-entities-in-selection = ATTEDIT: ບໍ່ມີອົງປະກອບ Insert ໃນສ່ວນທີ່ເລືອກ. + .attedit-select-an-insert-entity-first = ATTEDIT: ເລືອກອົງປະກອບ Insert ກ່ອນ. + .attedit-tag-tag-up-not-found-in-selection = ATTEDIT: ບໍ່ພົບແທັກ '__ocs_fmt_0__' ໃນສ່ວນທີ່ເລືອກ. + .attedit-updated-changed-attribute-s-tag-up-new = ATTEDIT: ອັບເດດ __ocs_fmt_0__ ຄຸນລັກສະນະແລ້ວ [__ocs_fmt_1__] = __ocs_fmt_2__. + .arc-length-symbol-dimarcsym = ສັນຍະລັກຄວາມຍາວສ່ວນໂຄ້ງ (DIMARCSYM) + .axis = ແກນ + .axis-end = ແກນ, ຈຸດສິ້ນສຸດ + .boundary-pick-internal-point-miss = BOUNDARY ເລືອກຈຸດພາຍໃນ:__ocs_arg_miss__ + .boundary = ຂອບເຂດ + .centerline-select-first-line = CENTERLINE ເລືອກເສັ້ນທຳອິດ: + .centerline-select-second-line = CENTERLINE ເລືອກເສັ້ນທີສອງ: + .circle-specify-center-point = CIRCLE ກຳນົດຈຸດໃຈກາງ: + .circle-specify-radius-or-type-value-r-center-c = CIRCLE ກຳນົດລັດສະໝີ ຫຼື ພິມຄ່າ <__ocs_arg_r__> [ໃຈກາງ (__ocs_arg_cx__,__ocs_arg_cy__)]: + .circle-2p-specify-first-end-of-diameter = CIRCLE 2P ກຳນົດຈຸດທຳອິດຂອງເສັ້ນຜ່າກາງ: + .circle-2p-specify-second-end-of-diameter = CIRCLE 2P ກຳນົດຈຸດທີສອງຂອງເສັ້ນຜ່າກາງ: + .circle-3p-specify-first-point = CIRCLE 3P ກຳນົດຈຸດທຳອິດ: + .circle-3p-specify-second-point = CIRCLE 3P ກຳນົດຈຸດທີສອງ: + .circle-3p-specify-third-point = CIRCLE 3P ກຳນົດຈຸດທີສາມ: + .circle-cd-specify-center-point = CIRCLE CD ກຳນົດຈຸດໃຈກາງ: + .circle-cd-specify-diameter-or-type-value-4-cen = CIRCLE CD ກຳນົດເສັ້ນຜ່າກາງ ຫຼື ພິມຄ່າ <__ocs_fmt_0__> [ໃຈກາງ (__ocs_fmt_1__,__ocs_fmt_2__)]: + .circle-ttr-select-first-tangent-object = CIRCLE TTR ເລືອກວັດຖຸສຳຜັດທຳອິດ: + .circle-ttr-select-second-tangent-object = CIRCLE TTR ເລືອກວັດຖຸສຳຜັດທີສອງ: + .circle-ttr-specify-radius = CIRCLE TTR ກຳນົດລັດສະໝີ: + .circle-ttt-select-first-tangent-object = CIRCLE TTT ເລືອກວັດຖຸສຳຜັດທຳອິດ: + .circle-ttt-select-second-tangent-object = CIRCLE TTT ເລືອກວັດຖຸສຳຜັດທີສອງ: + .circle-ttt-select-third-tangent-object = CIRCLE TTT ເລືອກວັດຖຸສຳຜັດທີສາມ: + .copy-specify-base-point-count-objects = COPY ກຳນົດຈຸດຫຼັກ [__ocs_arg_count__ ວັດຖຸ]: + .center-line = ເສັ້ນແກນກາງ + .center-mark = ເຄື່ອງໝາຍໃຈກາງ + .center-axes = ໃຈກາງ, ແກນ + .center-diameter = ໃຈກາງ, ເສັ້ນຜ່າກາງ + .center-radius = ໃຈກາງ, ລັດສະໝີ + .center-start-angle = ໃຈກາງ, ເລີ່ມຕົ້ນ, ມຸມ + .center-start-end = ໃຈກາງ, ເລີ່ມຕົ້ນ, ສິ້ນສຸດ + .center-start-length = ໃຈກາງ, ເລີ່ມຕົ້ນ, ຄວາມຍາວ + .circumscribed = ແຕ້ມນອກຮູບວົງມົນ (Circumscribed) + .current-settings-edit-mode = ການຕັ້ງຄ່າປັດຈຸບັນ: ໂໝດການແກ້ໄຂ = __ocs_fmt_0__ + .ddedit-selected-entity-is-not-text = DDEDIT: ອົງປະກອບທີ່ເລືອກບໍ່ແມ່ນຂໍ້ຄວາມ. + .dimcenter-select-arc-or-circle = DIMCENTER ເລືອກສ່ວນໂຄ້ງ ຫຼື ວົງມົນ: + .donut-specify-center-of-donut-enter-to-exit = DONUT ກຳນົດຈຸດໃຈກາງຂອງ donut (ກົດ Enter ເພື່ອອອກ): + .donut-specify-inside-diameter-0 = DONUT ກຳນົດເສັ້ນຜ່າກາງພາຍໃນ <0>: + .donut-specify-outside-diameter = DONUT ກຳນົດເສັ້ນຜ່າກາງພາຍນອກ: + .donut-specify-second-point-for-inside-diameter = DONUT ກຳນົດຈຸດທີສອງສຳລັບເສັ້ນຜ່າກາງພາຍໃນ: + .donut-specify-second-point-for-outside-diameter = DONUT ກຳນົດຈຸດທີສອງສຳລັບເສັ້ນຜ່າກາງພາຍນອກ: + .draw-manually = ແຕ້ມດ້ວຍຕົນເອງ + .ellipse-specify-center = ELLIPSE ກຳນົດຈຸດໃຈກາງ: + .ellipse-specify-major-axis-endpoint = ELLIPSE ກຳນົດຈຸດປາຍແກນເອກ: + .ellipse-specify-minor-axis-point-or-type-half = ELLIPSE ກຳນົດຈຸດແກນໂທ ຫຼື ພິມເຄິ່ງຄວາມຍາວ [ແກນເອກ r=__ocs_fmt_0__]: + .ellipse-axis-specify-first-endpoint-of-major-a = ELLIPSE (Axis) ກຳນົດຈຸດປາຍທຳອິດຂອງແກນເອກ: + .ellipse-axis-specify-minor-axis-point-or-type = ELLIPSE (Axis) ກຳນົດຈຸດແກນໂທ ຫຼື ພິມເຄິ່ງຄວາມຍາວ [ແກນເອກ r=__ocs_fmt_0__]: + .ellipse-axis-specify-second-endpoint-of-major = ELLIPSE (Axis) ກຳນົດຈຸດປາຍທີສອງຂອງແກນເອກ: + .ellipse-arc-specify-center = ELLIPSE ARC ກຳນົດຈຸດໃຈກາງ: + .ellipse-arc-specify-end-angle-point-or-type-de = ELLIPSE ARC ກຳນົດຈຸດມຸມສິ້ນສຸດ ຫຼື ພິມມຸມເປັນອົງສາ [start=__ocs_arg_sa__]: + .ellipse-arc-specify-major-axis-endpoint = ELLIPSE ARC ກຳນົດຈຸດປາຍແກນເອກ: + .ellipse-arc-specify-minor-axis-point-or-type-h = ELLIPSE ARC ກຳນົດຈຸດແກນໂທ ຫຼື ພິມເຄິ່ງຄວາມຍາວ [ແກນເອກ r=__ocs_arg_r__]: + .ellipse-arc-specify-start-angle-point-or-type = ELLIPSE ARC ກຳນົດຈຸດມຸມເລີ່ມຕົ້ນ ຫຼື ພິມມຸມເປັນອົງສາ: + .ellipse-arc = ສ່ວນໂຄ້ງຮູບວົງລີ + .ext-line-color-aci-dimclre = ສີເສັ້ນຊ່ວຍບອກຂະໜາດ ACI (DIMCLRE) + .ext-line-weight-dimlwe = ຄວາມໜາເສັ້ນຊ່ວຍບອກຂະໜາດ (DIMLWE) + .gradient-boundary-point-1 = GRADIENT ຈຸດຂອບເຂດ 1: + .gradient-point-n = GRADIENT ຈຸດ __ocs_arg_n__: + .gradient-kind-invert-pick-internal-point-miss = GRADIENT (__ocs_arg_kind____ocs_arg_invert__) ເລືອກຈຸດພາຍໃນ:__ocs_arg_miss__ + .gradient = ໄລ່ລະດັບສີ (Gradient) + .hatch-boundary-point-1 = HATCH ຈຸດຂອບເຂດ 1: + .hatch-pick-internal-point-miss = HATCH ເລືອກຈຸດພາຍໃນ:__ocs_arg_miss__ + .hatch-point-n = HATCH ຈຸດ __ocs_arg_n__: + .hatchedit-pattern-name-scale-scale-angle-angle = HATCHEDIT ລວດລາຍ:__ocs_arg_name__ ມາດຕາສ່ວນ:__ocs_arg_scale__ ມຸມ:__ocs_arg_angle__ [P / S / A | Enter ເພື່ອນຳໃຊ້]: + .hatchedit-select-hatch = HATCHEDIT ເລືອກລວດລາຍ hatch: + .hatchedit-selected-entity-is-not-a-hatch = HATCHEDIT: ອົງປະກອບທີ່ເລືອກບໍ່ແມ່ນ hatch. + .helix-enter-number-of-turns-3 = HELIX ປ້ອນຈຳນວນຮອບ <3>: + .helix-specify-base-radius = HELIX ກຳນົດລັດສະໝີພື້ນ: + .helix-specify-centre-point-of-base = HELIX ກຳນົດຈຸດໃຈກາງຂອງພື້ນ: + .helix-specify-helix-height = HELIX ກຳນົດລວງສູງຂອງ helix: + .helix-specify-top-radius-same-as-base = HELIX ກຳນົດລັດສະໝີຍອດ <ເທົ່າກັບພື້ນ>: + .image-specify-insertion-point-name = IMAGE ກຳນົດຈຸດແຊກ (__ocs_arg_name__): + .image-specify-width-drag-right = IMAGE ກຳນົດລວງກວ້າງ (ລາກໄປທາງຂວາ): + .invert-off = ກັບດ້ານ: ປິດ + .invert-on = ກັບດ້ານ: ເປີດ + .line-specify-first-point = LINE ກຳນົດຈຸດທຳອິດ: + .line-specify-next-point-close-undo = LINE ກຳນົດຈຸດຖັດໄປ [Close/Undo]: + .line-specify-next-point-undo = LINE ກຳນົດຈຸດຖັດໄປ [Undo]: + .line-color-aci = ສີເສັ້ນ (ACI): + .line-type = ປະເພດເສັ້ນ: + .line-weight = ຄວາມໜາເສັ້ນ: + .linear-factor-dimlfac = ຕົວຄູນເສັ້ນຊື່ (DIMLFAC) + .literal-spaces-space-stays-in-the-line-instead = ຊ່ອງວ່າງຕົວອັກສອນ: ຍະຫວ່າງຈະຢູ່ໃນຂໍ້ຄວາມແທນທີ່ຈະເປັນການສັ່ງການຄຳສັ່ງ (ຄືກັບການພິມ '>' ໄວ້ທາງໜ້າ). ຈະເປີດຢູ່ຈົນກວ່າຈະຖືກປິດ. + .mline-enter-scale-factor = MLINE ປ້ອນຕົວຄູນມາດຕາສ່ວນ: + .mline-specify-next-point-count-pts-enter-to-fi = MLINE ກຳນົດຈຸດຖັດໄປ (__ocs_arg_count__ ຈຸດ, ກົດ Enter ເພື່ອສຳເລັດ, C ເພື່ອປິດຮອບ, S ເພື່ອກຳນົດມາດຕາສ່ວນ): + .mline-specify-start-point-scale-scale = MLINE ກຳນົດຈຸດເລີ່ມຕົ້ນ (ມາດຕາສ່ວນ=__ocs_arg_scale__): + .pline-specify-start-point = PLINE ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .pline-mode-next-pt-count-pts = PLINE [__ocs_arg_mode__] ຈຸດຖັດໄປ [__ocs_arg_count__ ຈຸດ]: + .point-specify-point = POINT ກຳນົດຈຸດ: + .polygon-enter-number-of-sides = POLYGON ປ້ອນຈຳນວນດ້ານ <__ocs_fmt_0__>: + .polygon-specify-center-sides = POLYGON ກຳນົດຈຸດໃຈກາງ [__ocs_fmt_0__ ດ້ານ]: + .polygon-specify-vertex-on-circle-sides-inscrib = POLYGON ກຳນົດຈຸດຍອດເທິງວົງມົນ [__ocs_fmt_0__ ດ້ານ ແນບໃນວົງມົນ]: + .polygon-c-enter-number-of-sides = POLYGON C ປ້ອນຈຳນວນດ້ານ <__ocs_fmt_0__>: + .polygon-c-specify-center-sides = POLYGON C ກຳນົດຈຸດໃຈກາງ [__ocs_fmt_0__ ດ້ານ]: + .polygon-c-specify-edge-midpoint-radius-sides-c = POLYGON C ກຳນົດລັດສະໝີຈຸດເຄິ່ງກາງຂອບ [__ocs_fmt_0__ ດ້ານ ແນບນອກວົງມົນ]: + .polygon-e-enter-number-of-sides-n = POLYGON E ປ້ອນຈຳນວນດ້ານ <__ocs_arg_n__>: + .polygon-e-specify-first-endpoint-of-edge-n-sid = POLYGON E ກຳນົດຈຸດປາຍທຳອິດຂອງດ້ານ [__ocs_arg_n__ ດ້ານ]: + .polygon-e-specify-second-endpoint-of-edge-n-si = POLYGON E ກຳນົດຈຸດປາຍທີສອງຂອງດ້ານ [__ocs_arg_n__ ດ້ານ]: + .polysolid-wall-height = POLYSOLID ລວງສູງຝາ: + .polysolid-wall-width = POLYSOLID ລວງກວ້າງຝາ: + .pyramid-base-radius = PYRAMID ລັດສະໝີພື້ນ: + .pyramid-height-add-sides-by-typing-a-3rd-numbe = PYRAMID ລວງສູງ (ເພີ່ມຈຳນວນດ້ານໂດຍການພິມຕົວເລກທີ 3): + .polygon-circumscribed = ຮູບຫຼາຍແຈ - ແນບນອກວົງມົນ + .polygon-edge = ຮູບຫຼາຍແຈ - ກຳນົດຕາມດ້ານ + .polygon-inscribed = ຮູບຫຼາຍແຈ - ແນບໃນວົງມົນ + .ray-specify-start-point = RAY ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .ray-specify-through-point = RAY ກຳນົດຈຸດທີ່ຜ່ານ: + .rect-specify-first-corner = RECT ກຳນົດມຸມທຳອິດ: + .rect-specify-opposite-corner = RECT ກຳນົດມຸມກົງກັນຂ້າມ: + .rect-calculate-dimensions-based-on-length-width = RECT ຄິດໄລ່ຂະໜາດອີງຕາມ [Length / Width] : + .rect-specify-elevation = RECT ກຳນົດລະດັບຄວາມສູງ <__ocs_fmt_0__>: + .rect-specify-fillet-radius = RECT ກຳນົດລັດສະໝີມຸມມົນ <__ocs_fmt_0__>: + .rect-specify-first-chamfer-distance = RECT ກຳນົດໄລຍະຕັດມຸມທຳອິດ <__ocs_fmt_0__>: + .rect-specify-orientation-from-the-first-corner = RECT ກຳນົດທິດທາງຈາກມຸມທຳອິດ: + .rect-specify-rectangle-area = RECT ກຳນົດເນື້ອທີ່ສີ່ຫຼ່ຽມ: + .rect-specify-rectangle-length = RECT ກຳນົດລວງຍາວສີ່ຫຼ່ຽມ: + .rect-specify-rectangle-width = RECT ກຳນົດລວງກວ້າງສີ່ຫຼ່ຽມ: + .rect-specify-rotation-angle = RECT ກຳນົດມຸມໝຸນ <__ocs_fmt_0__>: + .rect-specify-second-chamfer-distance = RECT ກຳນົດໄລຍະຕັດມຸມທີສອງ <__ocs_fmt_0__>: + .rect-specify-thickness = RECT ກຳນົດຄວາມໜາ <__ocs_fmt_0__>: + .rect-specify-width = RECT ກຳນົດລວງກວ້າງ <__ocs_fmt_0__>: + .rect-cen-specify-center-point = RECT CEN ກຳນົດຈຸດໃຈກາງ: + .rect-cen-specify-corner-point = RECT CEN ກຳນົດຈຸດມຸມ: + .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT ກຳນົດມຸມຕິດກັນ (ກຳນົດທິດທາງຂອບ): + .rect-rot-specify-first-corner = RECT ROT ກຳນົດມຸມທຳອິດ: + .rect-rot-specify-height-perpendicular-pick = RECT ROT ກຳນົດລວງສູງ (ເລືອກແບບຕັ້ງສາກ): + .region-created-count-region-s = REGION: ສ້າງແລ້ວ __ocs_fmt_0__ region(s). + .region-select-closed-polylines-or-circles = REGION: ເລືອກໂພລີລາຍແບບປິດ ຫຼື ວົງມົນ. + .revcloud-specify-next-point-count-pts-enter-to = REVCLOUD ກຳນົດຈຸດຖັດໄປ (__ocs_arg_count__ ຈຸດ, ກົດ Enter ເພື່ອປິດຮອບ): + .revcloud-specify-start-point-arc-length-arc-le = REVCLOUD ກຳນົດຈຸດເລີ່ມຕົ້ນ (ຄວາມຍາວສ່ວນໂຄ້ງ = __ocs_arg_arc_length__): + .revcloud-first-corner = REVCLOUD ມຸມທຳອິດ (__ocs_arg_style__, ຄວາມຍາວສ່ວນໂຄ້ງ __ocs_arg_length__): __ocs_arg_message__ + .revcloud-opposite-corner = REVCLOUD ມຸມກົງກັນຂ້າມ: __ocs_arg_message__ + .revcloud-polygonal-start-point = REVCLOUD ຈຸດເລີ່ມຕົ້ນຮູບຫຼາຍແຈ (__ocs_arg_style__, ຄວາມຍາວສ່ວນໂຄ້ງ __ocs_arg_length__): __ocs_arg_message__ + .revcloud-next-polygonal-point = REVCLOUD ຈຸດຖັດໄປຂອງຮູບຫຼາຍແຈ (__ocs_arg_count__ ຈຸດ, ກົດ Enter ເພື່ອປິດຮອບ): __ocs_arg_message__ + .revcloud-freehand-first-point = REVCLOUD ຈຸດທຳອິດແບບແຕ້ມອິດສະຫຼະ (__ocs_arg_style__, ຄວາມຍາວສ່ວນໂຄ້ງ __ocs_arg_length__): __ocs_arg_message__ + .revcloud-guide-cursor = REVCLOUD ລາກເຄີເຊີ, ແລ້ວກົດຄລິກ ຫຼື ກົດ Enter ເພື່ອປິດຮອບ: __ocs_arg_message__ + .revcloud-approximate-arc-chord-length = REVCLOUD ຄວາມຍາວສາຍກົ່ງສ່ວນໂຄ້ງໂດຍປະມານ <__ocs_arg_length__>: + .revcloud-style = REVCLOUD ຮູບແບບ [Normal/Calligraphy] <__ocs_arg_style__>: + .revcloud-select-closed-curve = REVCLOUD ເລືອກວົງມົນ, ວົງລີ, ໂພລີລາຍ, ຫຼື spline ແບບປິດ: __ocs_arg_message__ + .revcloud-reverse-arc-direction = REVCLOUD ກັບທິດທາງສ່ວນໂຄ້ງ [Yes/No] : + .revcloud-select-revision-cloud = REVCLOUD ເລືອກໂພລີລາຍຟັງຊັ່ນກ້ອນເມກແກ້ໄຂແບບປິດໃກ້ກັບຈຸດເລີ່ມປ່ຽນແທນ: __ocs_arg_message__ + .revcloud-replacement-point = REVCLOUD ຈຸດປ່ຽນແທນ __ocs_arg_count__; ຈົບລົງເທິງຈຸດຍອດເມກອື່ນ: + .revcloud-pick-side = REVCLOUD ເລືອກດ້ານຂອງເມກເດີມທີ່ຈະລຶບ: + .revcloud-path-cannot-form = ເສັ້ນທາງທີ່ເລືອກບໍ່ສາມາດສ້າງເປັນກ້ອນເມກແກ້ໄຂໄດ້. + .revcloud-select-closed-revision-cloud = ເລືອກໂພລີລາຍກ້ອນເມກແກ້ໄຂແບບປິດ. + .revcloud-polyline-not-planar = ໂພລີລາຍທີ່ເລືອກບໍ່ໄດ້ຢູ່ເທິງລະນາບດຽວກັນ. + .revcloud-corners-non-zero-area = ສອງມຸມຕ້ອງກຳນົດເນື້ອທີ່ທີ່ບໍ່ເທົ່າກັບສູນ. + .revcloud-arc-length-positive = ຄວາມຍາວສ່ວນໂຄ້ງຕ້ອງໃຫຍ່ກວ່າສູນ. + .revcloud-enter-valid-arc-length = ປ້ອນຄວາມຍາວສ່ວນໂຄ້ງທີ່ຖືກຕ້ອງ. + .revcloud-select-supported-closed-curve = ເລືອກເສັ້ນໂຄ້ງແບບປິດທີ່ຮອງຮັບ. + .revcloud-select-closed-entity = ເລືອກວົງມົນ, ວົງລີ, ໂພລີລາຍ, ຫຼື spline ແບບປິດ. + .calligraphy = ແບບເສັ້ນພູ່ກັນ (Calligraphy) + .first-point = ຈຸດທຳອິດ + .freehand = ແຕ້ມອິດສະຫຼະ (Freehand) + .rectangular = ສີ່ຫຼ່ຽມມຸມສາກ + .rectangle-center = ຮູບສີ່ຫຼ່ຽມ - ໃຈກາງ + .rectangle-rotated = ຮູບສີ່ຫຼ່ຽມ - ໝຸນ + .rectangle-two-corners = ຮູບສີ່ຫຼ່ຽມ - ສອງມຸມ + .rev-cloud = ກ້ອນເມກແກ້ໄຂ (Rev Cloud) + .section-cutting-plane-axis-x-y-z = SECTION ແກນລະນາບຕັດ [X / Y / Z]: + .section-offset-along-x = SECTION ໄລຍະເລື່ອນຂະໜານຕາມແກນ X: + .section-offset-along-y = SECTION ໄລຍະເລື່ອນຂະໜານຕາມແກນ Y: + .section-offset-along-z = SECTION ໄລຍະເລື່ອນຂະໜານຕາມແກນ Z: + .sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH ວາງປາກກາ — ຍ້າຍເພື່ອແຕ້ມ, ກົດຄລິກເພື່ອຍົກຂຶ້ນ, ກົດ Enter ເພື່ອບັນທຶກ: + .sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH ຍົກປາກກາ — ກົດຄລິກເພື່ອວາງປາກກາລົງ, ກົດ Enter ເພື່ອບັນທຶກ: + .sketch-pen = ປາກກາ + .sketch-increment = ຄ່າເພີ່ມ + .sketch-record = ບັນທຶກ + .sketch-stop-erasing = ຢຸດການລຶບ + .sketch-connect = ເຊື່ອມຕໍ່ + .sketch-exit = ອອກ + .sketch-quit = ຍົກເລີກ + .sketch-object-type = SKETCH ປະເພດວັດຖຸ [Line/Polyline/Spline]: + .sketch-record-increment = SKETCH ບັນທຶກຄ່າເພີ່ມ <__ocs_arg_value__>: + .sketch-spline-fit-tolerance = SKETCH ຄ່າຄວາມຄາດເຄື່ອນ Spline fit <__ocs_arg_value__>: + .sketch-erase-move-backward = SKETCH ລຶບ — ຍ້າຍຖອຍຫຼັງເທິງຮູບແຕ້ມຊົ່ວຄາວ: + .sketch-pen-down-move = SKETCH (__ocs_arg_type__, ຄ່າເພີ່ມ __ocs_arg_increment__) ວາງປາກກາ — ຍ້າຍເພື່ອແຕ້ມ: + .sketch-pen-up-click = SKETCH (__ocs_arg_type__, ຄ່າເພີ່ມ __ocs_arg_increment__) ຍົກປາກກາ — ກົດຄລິກເພື່ອວາງປາກກາລົງ: + .slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE ແກນລະນາບຕັດ [X / Y / Z] (ເພີ່ມ TOP/BOTTOM ໂດຍການພິມ): + .slice-offset-along-x = SLICE ໄລຍະເລື່ອນຂະໜານຕາມແກນ X: + .slice-offset-along-y = SLICE ໄລຍະເລື່ອນຂະໜານຕາມແກນ Y: + .slice-offset-along-z = SLICE ໄລຍະເລື່ອນຂະໜານຕາມແກນ Z: + .slice-select-targets = SLICE ເລືອກວັດຖຸແຂງ ຫຼື ພື້ນຜິວທີ່ຈະຕັດແບ່ງ, ແລ້ວກົດ Enter: + .slice-first-point-or-method = SLICE ກຳນົດຈຸດທຳອິດເທິງລະນາບຕັດ ຫຼື [planar Object/Surface/Zaxis/View/XY/YZ/ZX/3points] <3points>: + .slice-first-plane-point = SLICE ກຳນົດຈຸດທຳອິດເທິງລະນາບຕັດ: + .slice-second-plane-point = SLICE ກຳນົດຈຸດທີສອງເທິງລະນາບຕັດ: + .slice-third-plane-point = SLICE ກຳນົດຈຸດທີສາມເທິງລະນາບຕັດ: + .slice-point-on-plane = SLICE ກຳນົດຈຸດເທິງລະນາບຕັດ: + .slice-point-on-normal-axis = SLICE ກຳນົດຈຸດເທິງແກນຕັ້ງສາກຂອງລະນາບ: + .slice-point-on-view-plane = SLICE ກຳນົດຈຸດເທິງລະນາບມຸມມອງປັດຈຸບັນ: + .slice-point-on-ucs-plane = SLICE ກຳນົດຈຸດເທິງລະນາບ UCS ປັດຈຸບັນ: + .slice-select-planar-object = SLICE ເລືອກວັດຖຸເສັ້ນໂຄ້ງເທິງລະນາບ: + .slice-select-planar-surface = SLICE ເລືອກພື້ນຜິວລະນາບ: + .slice-select-cutting-surface = SLICE ເລືອກພື້ນຜິວຕັດ: + .slice-select-side-or-both = SLICE ກຳນົດຈຸດເທິງດ້ານທີ່ຕ້ອງການຮັກສາໄວ້ ຫຼື [Both] : + .slice-select-object-or-both = SLICE ເລືອກວັດຖຸທີ່ຖືກຕັດທີ່ຈະຮັກສາໄວ້ ຫຼື [Both] : + .slice-surface-not-planar = SLICE ພື້ນຜິວທີ່ເລືອກບໍ່ແມ່ນລະນາບ. ກະລຸນາເລືອກພື້ນຜິວລະນາບ: + .slice-surface-not-supported = SLICE ພື້ນຜິວທີ່ເລືອກບໍ່ໄດ້ກຳນົດແຜ່ນຕັດທີ່ຮອງຮັບ. ກະລຸນາເລືອກພື້ນຜິວອື່ນ: + .slice-object-not-planar = SLICE ວັດຖຸທີ່ເລືອກບໍ່ໄດ້ກຳນົດລະນາບ. ກະລຸນາເລືອກວັດຖຸເສັ້ນໂຄ້ງເທິງລະນາບ: + .slice-planar-object = ວັດຖຸລະນາບ (planar Object) + .slice-zaxis = ແກນ Z (Zaxis) + .slice-three-points = 3 ຈຸດ (3points) + .solid-specify-first-point = SOLID ກຳນົດຈຸດທຳອິດ: + .solid-specify-fourth-point = SOLID ກຳນົດຈຸດທີສີ່: + .solid-specify-second-point = SOLID ກຳນົດຈຸດທີສອງ: + .solid-specify-third-point = SOLID ກຳນົດຈຸດທີສາມ: + .spline-specify-first-control-point = SPLINE ກຳນົດຈຸດຄວບຄຸມທຳອິດ: + .spline-specify-first-point = SPLINE ກຳນົດຈຸດທຳອິດ: + .spline-specify-next-point-n-pts = SPLINE ກຳນົດຈຸດຖັດໄປ [__ocs_arg_n__ ຈຸດ]: + .sketch = ຮູບແຕ້ມຮ່າງ (Sketch) + .start-center-angle = ເລີ່ມຕົ້ນ, ໃຈກາງ, ມຸມ + .start-center-end = ເລີ່ມຕົ້ນ, ໃຈກາງ, ສິ້ນສຸດ + .start-center-length = ເລີ່ມຕົ້ນ, ໃຈກາງ, ຄວາມຍາວ + .start-end-angle = ເລີ່ມຕົ້ນ, ສິ້ນສຸດ, ມຸມ + .start-end-direction = ເລີ່ມຕົ້ນ, ສິ້ນສຸດ, ທິດທາງ + .start-end-radius = ເລີ່ມຕົ້ນ, ສິ້ນສຸດ, ລັດສະໝີ + .suppress-1st-line-dimsd1 = ລະງັບເສັ້ນທຳອິດ (DIMSD1) + .suppress-1st-line-dimse1 = ລະງັບເສັ້ນຊ່ວຍທຳອິດ (DIMSE1) + .suppress-2nd-line-dimsd2 = ລະງັບເສັ້ນທີສອງ (DIMSD2) + .suppress-2nd-line-dimse2 = ລະງັບເສັ້ນຊ່ວຍທີສອງ (DIMSE2) + .trace-specify-next-point-undo = TRACE ກຳນົດຈຸດຖັດໄປ [Undo]: + .trace-specify-start-point = TRACE ກຳນົດຈຸດເລີ່ມຕົ້ນ: + .trace-specify-trace-width-w = TRACE ກຳນົດຄວາມກວ້າງ trace <__ocs_arg_w__>: + .tan-tan-radius = ສຳຜັດ, ສຳຜັດ, ລັດສະໝີ + .tan-tan-tan = ສຳຜັດ, ສຳຜັດ, ສຳຜັດ + .trace = ເສັ້ນໜາ (Trace) + .ttt = Ttt + .usage-attdisp-on-off-normal = ວິທີໃຊ້: ATTDISP ON | OFF | NORMAL + .usage-polysolid-width-height-select-a-polyline = ວິທີໃຊ້: POLYSOLID (ເລືອກໂພລີລາຍກ່ອນ) + .usage-pyramid-radius-height-sides-default-4-si = ວິທີໃຊ້: PYRAMID [sides] (ເລີ່ມຕົ້ນ 4 ດ້ານ) + .wipeout-specify-first-point-or-polyline = WIPEOUT ກຳນົດຈຸດທຳອິດ ຫຼື [Frames/Polyline] : + .wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT ກຳນົດຈຸດຖັດໄປ ຫຼື [Undo/Close] (__ocs_arg_n__ ຈຸດ): + .wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polyline ເລືອກໂພລີລາຍລະນາບແບບປິດ: + .wipeout-rectangular-specify-first-corner = WIPEOUT Rectangular ກຳນົດມຸມທຳອິດ: + .wipeout-rectangular-specify-opposite-corner = WIPEOUT Rectangular ກຳນົດມຸມກົງກັນຂ້າມ: + .wipeout-frames-enter-frame-mode = WIPEOUT Frames ປ້ອນໂໝດກອບເສັ້ນ [Off/On/DisplayButNotPlot] <__ocs_arg_mode__>: + .wipeout-polyline-erase-source = WIPEOUT Polyline ລຶບໂພລີລາຍຕົ້ນສະບັບບໍ່? [Yes/No] : + .wipeout-polyline-invalid = WIPEOUT Polyline: ເລືອກໂພລີລາຍ 2D ແບບປິດ, ເປັນເສັ້ນຊື່, ຢູ່ເທິງລະນາບ ແລະ ມີຢ່າງໜ້ອຍ 3 ຈຸດຍອດທີ່ບໍ່ຕັດກັນເອງ. + .setvar-frame-range = SETVAR: __ocs_fmt_0__ ຕ້ອງການຄ່າ 0, 1, ຫຼື 2. + .frames = ກອບເສັ້ນ + .display-but-not-plot = ສະແດງແຕ່ບໍ່ພິມອອກ + .frames-on-not-plotted = ເປີດກອບເສັ້ນ, ບໍ່ພິມອອກ + .image-adjust = ປັບແຕ່ງຮູບພາບ + .x-ray = ເອັກສະເລ (X-Ray) + .xline-specify-a-point = XLINE ກຳນົດຈຸດ: + .xline-specify-through-point = XLINE ກຳນົດຈຸດທີ່ຜ່ານ: + .n-object-s-erased = ລຶບອອກແລ້ວ __ocs_fmt_0__ ວັດຖຸ. + + .circle-specify-diameter = CIRCLE ກຳນົດເສັ້ນຜ່າກາງ <__ocs_fmt_0__>: + .centermark-select-arc-or-circle-finish = CENTERMARK ເລືອກສ່ວນໂຄ້ງ ຫຼື ວົງມົນ : + .centerreassociate-select-new-arc-or-circle = CENTERREASSOCIATE ເລືອກສ່ວນໂຄ້ງ ຫຼື ວົງມົນໃໝ່: + .value-3dface-specify-first-point-or-invisible = 3DFACE ກຳນົດຈຸດທຳອິດ ຫຼື [Invisible]: + .value-3dface-specify-second-point-or-invisible = 3DFACE ກຳນົດຈຸດທີສອງ ຫຼື [Invisible]: + .value-3dface-specify-third-point-or-invisible-exit = 3DFACE ກຳນົດຈຸດທີສາມ ຫຼື [Invisible] : + .value-3dface-specify-fourth-point-or-invisible-create-three-sided-face = 3DFACE ກຳນົດຈຸດທີສີ່ ຫຼື [Invisible] <ສ້າງໜ້າສາມຫຼ່ຽມ>: + .edge-select-edge-of-3d-face-to-toggle-visibility-or-display = EDGE ເລືອກຂອບຂອງໜ້າ 3D ເພື່ອສະຫຼັບການສະແດງຜົນ ຫຼື [Display]: + .edge-display-invisible-edges-all-select-select = EDGE ສະແດງຂອບທີ່ເຊື່ອງໄວ້ [All/Select] : + .dimbaseline-specify-second-extension-line-origin-select-undo-select = DIMBASELINE ກຳນົດຈຸດເລີ່ມຕົ້ນຂອງເສັ້ນຊ່ວຍທີສອງ [Select/Undo] : + .dimcontinue-specify-second-extension-line-origin-select-undo-select = DIMCONTINUE ກຳນົດຈຸດເລີ່ມຕົ້ນຂອງເສັ້ນຊ່ວຍທີສອງ [Select/Undo] :").into_owned() } else { - crate::t!("DIMBASELINE Specify second extension line origin [Select/Undo] :") + .into_owned() } } @@ -486,13 +487,12 @@ impl CadCommand for DimBaselineCommand { } fn dimension_preview(&self, cursor: DVec3) -> Option> { - self.build_dimension(cursor) - .map(|dimension| { - vec![DimensionPreview { - entity: EntityType::Dimension(dimension), - preserve_base_style: self.preserve_base_style, - }] - }) + self.build_dimension(cursor).map(|dimension| { + vec![DimensionPreview { + entity: EntityType::Dimension(dimension), + preserve_base_style: self.preserve_base_style, + }] + }) } } @@ -532,7 +532,10 @@ fn build_linear( perpendicular = -perpendicular; } let distance = offset.abs(); - (fixed + perpendicular * distance, point + perpendicular * distance) + ( + fixed + perpendicular * distance, + point + perpendicular * distance, + ) } else { let target = fixed.dot(source_perpendicular) + offset; ( @@ -642,12 +645,8 @@ fn refresh_measurement(dimension: &mut Dimension, plane: Plane) { value.base.actual_measurement = (second - first).dot(axis).abs(); } Dimension::Aligned(value) => value.base.actual_measurement = value.measurement(), - Dimension::Angular2Ln(value) => { - value.base.actual_measurement = value.measurement_degrees() - } - Dimension::Angular3Pt(value) => { - value.base.actual_measurement = value.measurement_degrees() - } + Dimension::Angular2Ln(value) => value.base.actual_measurement = value.measurement_degrees(), + Dimension::Angular3Pt(value) => value.base.actual_measurement = value.measurement_degrees(), Dimension::Ordinate(value) => value.refresh_measurement(), _ => {} } @@ -755,9 +754,9 @@ fn preview_for_dimension(dimension: &Dimension) -> WireModel { plinegen: true, fill_tris: Vec::new(), fill_tris_low: Vec::new(), - + ..Default::default() -} + } } fn linear_preview( @@ -821,8 +820,7 @@ fn project_to_line(point: Vec2, perpendicular: Vec2, offset: f64) -> Vec2 { } fn plane_from_normal(origin: Vector3, normal: Vector3) -> Plane { - let (x_axis, y_axis) = - crate::scene::view::transform::ocs_axes((normal.x, normal.y, normal.z)); + let (x_axis, y_axis) = crate::scene::view::transform::ocs_axes((normal.x, normal.y, normal.z)); Plane::from_axes( point(origin), [x_axis.0, x_axis.1, x_axis.2], @@ -844,7 +842,11 @@ fn point(value: Vector3) -> [f64; 3] { } fn segment_local(points: &mut Vec<[f32; 3]>, plane: Plane, first: Vec2, second: Vec2) { - segment_world(points, plane.point_at(first.into()), plane.point_at(second.into())); + segment_world( + points, + plane.point_at(first.into()), + plane.point_at(second.into()), + ); } fn segment_world(points: &mut Vec<[f32; 3]>, first: [f64; 3], second: [f64; 3]) { @@ -861,4 +863,6 @@ fn float3(value: [f64; 3]) -> [f32; 3] { [value[0] as f32, value[1] as f32, value[2] as f32] } -inventory::submit!(crate::command::CommandRegistration { names: &["DIMBASELINE"] }); +inventory::submit!(crate::command::CommandRegistration { + names: &["DIMBASELINE"] +}); diff --git a/src/modules/annotate/dim_continue.rs b/src/modules/annotate/dim_continue.rs index 2de9dcd40..8922e019b 100644 --- a/src/modules/annotate/dim_continue.rs +++ b/src/modules/annotate/dim_continue.rs @@ -4,12 +4,12 @@ use codec::entities::{ }; use codec::types::Vector3; use codec::{EntityType, Handle}; +use glam::DVec3; use kernel::geom2d::{ arc_span, closest_point, intersect, nearest_of, Arc, Curve, Ray, Tolerance, Transform, Vec2, XLine, }; use kernel::space::Plane; -use glam::DVec3; use crate::command::{ CadCommand, CmdOption, CmdResult, DimensionAssociationInput, DimensionPreview, @@ -199,7 +199,10 @@ impl DimContinueCommand { fn build_dimension(&self, point_world: DVec3) -> Option { let state = self.base.as_ref()?; - let point = state.plane.project(point_world.to_array()).map(Vec2::from)?; + let point = state + .plane + .project(point_world.to_array()) + .map(Vec2::from)?; let mut dimension = match &state.kind { ContinueKind::Linear { current, @@ -332,7 +335,8 @@ impl CadCommand for DimContinueCommand { { crate::t!("DIMCONTINUE Specify feature location [Undo/Select] :").into_owned() + crate::t!("DIMCONTINUE Specify second extension line origin [Select/Undo]