Merge branch 'main' into de_DE-patch-1
15
.github/workflows/pages.yml
vendored
|
|
@ -51,7 +51,10 @@ jobs:
|
|||
tool: wasm-bindgen@${{ steps.wasm-bindgen-version.outputs.version }}
|
||||
|
||||
- name: Build web bundle
|
||||
run: trunk build --release --public-url /
|
||||
run: trunk build --release --public-url /app/ --dist dist/app --html-output index.html web-app.html
|
||||
|
||||
- name: Assemble landing page
|
||||
run: sh scripts/assemble-site.sh
|
||||
|
||||
# GitHub Pages runs Jekyll, which ignores files; opt out so the
|
||||
# wasm-bindgen output is served verbatim.
|
||||
|
|
@ -74,7 +77,7 @@ jobs:
|
|||
from decimal import Decimal, ROUND_HALF_UP
|
||||
from pathlib import Path
|
||||
|
||||
output = Path("dist/supporters.json")
|
||||
output = Path("dist/app/supporters.json")
|
||||
token = os.environ.get("OCS_PATREON_TOKEN", "")
|
||||
if not token:
|
||||
output.write_text("[]\n", encoding="utf-8")
|
||||
|
|
@ -196,8 +199,8 @@ jobs:
|
|||
|
||||
playlist = "https://youtube.com/playlist?list=PLZq_TEkIFh9bAnoOX1HiCAunm3anZDBOl"
|
||||
fallback = Path("web/videos.json")
|
||||
output = Path("dist/videos.json")
|
||||
thumbs = Path("dist/video_thumbs")
|
||||
output = Path("dist/app/videos.json")
|
||||
thumbs = Path("dist/app/video_thumbs")
|
||||
thumbs.mkdir(parents=True, exist_ok=True)
|
||||
request_headers = {"User-Agent": "Mozilla/5.0"}
|
||||
|
||||
|
|
@ -294,8 +297,8 @@ jobs:
|
|||
updated_at: .updatedAt,
|
||||
pinned
|
||||
})' \
|
||||
> dist/discussions.json
|
||||
echo "discussions: $(jq 'length' dist/discussions.json)"
|
||||
> dist/app/discussions.json
|
||||
echo "discussions: $(jq 'length' dist/app/discussions.json)"
|
||||
|
||||
- uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
|
|
|
|||
6
Cargo.lock
generated
|
|
@ -4,7 +4,7 @@ version = 4
|
|||
|
||||
[[package]]
|
||||
name = "OpenCADStudio"
|
||||
version = "0.9.6"
|
||||
version = "0.9.7"
|
||||
dependencies = [
|
||||
"acadrust",
|
||||
"ashpd",
|
||||
|
|
@ -72,7 +72,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
|||
[[package]]
|
||||
name = "acadrust"
|
||||
version = "0.4.1"
|
||||
source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=931c4ab#931c4ab0c590b755e280bed318a35f41c57b139f"
|
||||
source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
|
|
@ -878,7 +878,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
|||
[[package]]
|
||||
name = "cadkernel"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=ff950ce#ff950ce4146c9fb94a5478e6704ada01264dbf9f"
|
||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=76de3b3#76de3b35e55a45f3ffb813c499cf3affe176351d"
|
||||
dependencies = [
|
||||
"acadrust",
|
||||
"cavalier_contours",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "OpenCADStudio"
|
||||
version = "0.9.6"
|
||||
version = "0.9.7"
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
|
|
@ -27,8 +27,8 @@ glam = { version = "0.33", features = ["bytemuck"] }
|
|||
rfd = "0.17"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
env_logger = "0.11"
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "931c4ab", features = ["serde"] }
|
||||
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "ff950ce", features = ["acis", "offset"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "94df2c3", features = ["serde"] }
|
||||
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "76de3b3", features = ["acis", "offset"] }
|
||||
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
||||
flate2 = "1"
|
||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<p align="center">
|
||||
<a href="README.md">English</a> ·
|
||||
<a href="docs/readme/README.bg.md">Български</a> ·
|
||||
<a href="docs/readme/README.pt-BR.md">Português (Brasil)</a> ·
|
||||
<a href="docs/readme/README.cs.md">Čeština</a> ·
|
||||
<a href="docs/readme/README.nl.md">Nederlands</a> ·
|
||||
|
|
@ -46,7 +47,7 @@
|
|||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio workspace" width="100%">
|
||||
<img src="site/workspace.png" alt="Open CAD Studio workspace" width="100%">
|
||||
</p>
|
||||
|
||||
## Overview
|
||||
|
|
@ -65,7 +66,7 @@ The project is under active development. Keep backups of important production dr
|
|||
- **Extensible workflows** — native plugins, command scripts, headless conversion, and a line-based JSON automation API.
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D model in Open CAD Studio" width="100%">
|
||||
<img src="site/modeling.png" alt="3D model in Open CAD Studio" width="100%">
|
||||
</p>
|
||||
|
||||
## File workflows
|
||||
|
|
@ -121,9 +122,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 19 interface languages:
|
||||
Open CAD Studio can follow the system language or use any of these 20 interface languages:
|
||||
|
||||
> Arabic · Brazilian Portuguese · Czech · Dutch · English · Finnish · French · German · Hindi · Hungarian · Italian · Japanese · Korean · Polish · Russian · Simplified Chinese · Spanish · Traditional Chinese · Turkish
|
||||
> Arabic · Brazilian Portuguese · Bulgarian · Czech · Dutch · English · Finnish · French · German · Hindi · Hungarian · Italian · Japanese · Korean · 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@
|
|||
# cargo install trunk wasm-bindgen-cli
|
||||
|
||||
[build]
|
||||
target = "index.html"
|
||||
target = "web-app.html"
|
||||
dist = "dist/app"
|
||||
public_url = "/app/"
|
||||
html_output = "index.html"
|
||||
# The crate has no Cargo features: the geometry kernel is pure Rust and
|
||||
# cross-compiles to wasm, so the web build gets the same solid modelling the
|
||||
# native one does.
|
||||
|
|
|
|||
4
assets/icons/extrude.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="64" height="64">
|
||||
<path d="M4 16 10 13 16 16 10 19Z" fill="none" stroke="#e0e0e0" stroke-width="1.7" stroke-linejoin="round"/>
|
||||
<path d="M10 13V5M7 8l3-3 3 3" fill="none" stroke="#4cc9f0" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 332 B |
4
assets/icons/loft.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="64" height="64">
|
||||
<path d="M5 18 12 21 19 18 16 6 12 3 8 6Z" fill="none" stroke="#e0e0e0" stroke-width="1.7" stroke-linejoin="round"/>
|
||||
<path d="M8 6h8M5 18h14" stroke="#4cc9f0" stroke-width="1.4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 275 B |
4
assets/icons/polysolid.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="64" height="64">
|
||||
<path d="M3 17 8 12 13 16 21 8V14L13 22 8 18 3 23Z" fill="none" stroke="#e0e0e0" stroke-width="1.7" stroke-linejoin="round"/>
|
||||
<path d="M3 17V23M8 12V18M13 16V22M21 8V14" stroke="#4cc9f0" stroke-width="1.4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 303 B |
4
assets/icons/presspull.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="64" height="64">
|
||||
<path d="M4 17 10 14 16 17 10 20Z" fill="none" stroke="#e0e0e0" stroke-width="1.7" stroke-linejoin="round"/>
|
||||
<path d="M10 14V5M7 8l3-3 3 3M18 5v9m-2-3 2 3 2-3" fill="none" stroke="#4cc9f0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 352 B |
4
assets/icons/pyramid3d.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="64" height="64">
|
||||
<path d="M12 3 3 18 12 22 21 18Z" fill="none" stroke="#e0e0e0" stroke-width="1.7" stroke-linejoin="round"/>
|
||||
<path d="M12 3V22M3 18H21" fill="none" stroke="#4cc9f0" stroke-width="1.4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 280 B |
4
assets/icons/revolve.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="64" height="64">
|
||||
<path d="M8 18V6M8 7c7-2 11 1 11 5s-4 7-11 5" fill="none" stroke="#e0e0e0" stroke-width="1.7" stroke-linecap="round"/>
|
||||
<path d="m16 8 3 4-4 2" fill="none" stroke="#4cc9f0" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 335 B |
4
assets/icons/sweep.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="64" height="64">
|
||||
<circle cx="6" cy="17" r="3" fill="none" stroke="#e0e0e0" stroke-width="1.7"/>
|
||||
<path d="M8 15c3-6 6-8 11-8M16 5l3 2-2 3" fill="none" stroke="#4cc9f0" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 313 B |
|
|
@ -21,6 +21,7 @@ SPL, *SPLINE
|
|||
H, *HATCH
|
||||
BH, *HATCH
|
||||
HE, *HATCHEDIT
|
||||
HB, *HATCHTOBACK
|
||||
BO, *BOUNDARY
|
||||
GD, *GRADIENT
|
||||
XL, *XLINE
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ serde = { version = "1", features = ["derive"] }
|
|||
# Pulled in only by the `host` feature, which adds the `acadrust`-typed
|
||||
# `HostApi` runtime surface. The default crate stays dependency-free so engine
|
||||
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "931c4ab", optional = true, features = ["serde"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "94df2c3", optional = true, features = ["serde"] }
|
||||
|
||||
# Runtime IPC and serialization (host feature only).
|
||||
interprocess = { version = "2", optional = true }
|
||||
|
|
@ -37,7 +37,7 @@ serde_json = "1"
|
|||
serde = { version = "1", features = ["derive"] }
|
||||
cargo-lock = "11"
|
||||
# acadrust is scanned at build time to generate the embedded type registry.
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "931c4ab", features = ["serde"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "94df2c3", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ fn generate_type_registry(out_dir: &Path) {
|
|||
("Polyline2D", trace::<acadrust::entities::Polyline2D>),
|
||||
("Polyline3D", trace::<acadrust::entities::Polyline3D>),
|
||||
("LwPolyline", trace::<acadrust::LwPolyline>),
|
||||
("MText", trace::<acadrust::entities::MText>),
|
||||
("Spline", trace::<acadrust::Spline>),
|
||||
("EntityCommon", trace::<acadrust::entities::EntityCommon>),
|
||||
("Handle", trace::<acadrust::Handle>),
|
||||
|
|
@ -96,6 +97,23 @@ fn add_enum_samples(tracer: &mut Tracer, samples: &mut Samples) {
|
|||
let _ = tracer.trace_value(samples, &acadrust::entities::SmoothSurfaceType::CubicBSpline);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::SmoothSurfaceType::Bezier);
|
||||
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::AttachmentPoint::TopLeft);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::AttachmentPoint::TopCenter);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::AttachmentPoint::TopRight);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::AttachmentPoint::MiddleLeft);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::AttachmentPoint::MiddleCenter);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::AttachmentPoint::MiddleRight);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::AttachmentPoint::BottomLeft);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::AttachmentPoint::BottomCenter);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::AttachmentPoint::BottomRight);
|
||||
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::DrawingDirection::LeftToRight);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::DrawingDirection::TopToBottom);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::DrawingDirection::ByStyle);
|
||||
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::LineSpacingStyle::AtLeast);
|
||||
let _ = tracer.trace_value(samples, &acadrust::entities::LineSpacingStyle::Exactly);
|
||||
|
||||
let _ = tracer.trace_value(samples, &acadrust::xdata::XDataValue::String(String::new()));
|
||||
let _ = tracer.trace_value(samples, &acadrust::xdata::XDataValue::ControlString(String::new()));
|
||||
let _ = tracer.trace_value(samples, &acadrust::xdata::XDataValue::LayerName(String::new()));
|
||||
|
|
|
|||
|
|
@ -79,6 +79,78 @@ pub fn recv_framed<R: Read, T: serde::de::DeserializeOwned>(reader: &mut R) -> R
|
|||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Error during a polled read: either an IO error on the stream, or an
|
||||
/// interrupt reported by the poll callback (e.g. the user pressed Ctrl+C).
|
||||
#[derive(Debug)]
|
||||
enum PollError {
|
||||
Io(ProxyError),
|
||||
Interrupted(PluginRequestError),
|
||||
}
|
||||
|
||||
impl From<ProxyError> for PollError {
|
||||
fn from(e: ProxyError) -> Self {
|
||||
PollError::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PollError> for PluginRequestError {
|
||||
fn from(e: PollError) -> Self {
|
||||
match e {
|
||||
PollError::Io(err) => err.into(),
|
||||
PollError::Interrupted(err) => err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read exactly `buf.len()` bytes from `reader`, calling `poll` whenever no
|
||||
/// data is currently available. Unlike `read_exact`, this preserves partial
|
||||
/// progress across short read timeouts so the stream can never get out of sync.
|
||||
fn read_with_poll<R: Read>(
|
||||
reader: &mut R,
|
||||
buf: &mut [u8],
|
||||
poll: &mut dyn FnMut() -> Result<(), PluginRequestError>,
|
||||
) -> Result<(), PollError> {
|
||||
use std::io::ErrorKind;
|
||||
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(n) => off += n,
|
||||
Err(e) if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut => {
|
||||
if let Err(err) = poll() {
|
||||
return Err(PollError::Interrupted(err));
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(PollError::Io(ProxyError::Io(e))),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receive a length-framed bincode message with polling. The length prefix and
|
||||
/// payload are read incrementally so a timeout never splits a message.
|
||||
fn recv_framed_with_poll<R: Read>(
|
||||
reader: &mut R,
|
||||
poll: &mut dyn FnMut() -> Result<(), PluginRequestError>,
|
||||
) -> Result<PluginResponse, PollError> {
|
||||
let mut len_buf = [0u8; 8];
|
||||
read_with_poll(reader, &mut len_buf, poll)?;
|
||||
let len = u64::from_le_bytes(len_buf) as usize;
|
||||
if len == 0 {
|
||||
return Err(ProxyError::Empty.into());
|
||||
}
|
||||
if len > MAX_MESSAGE_SIZE {
|
||||
return Err(ProxyError::TooLarge(len).into());
|
||||
}
|
||||
let mut buf = vec![0u8; len];
|
||||
read_with_poll(reader, &mut buf, poll)?;
|
||||
let msg = bincode::deserialize(&buf).map_err(ProxyError::Encode)?;
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Thread-safe [`PluginRequestSender`] implementation that forwards requests
|
||||
/// over a single TCP stream to a request proxy server.
|
||||
pub struct ProxyPluginRequestSender {
|
||||
|
|
@ -113,12 +185,12 @@ impl ProxyPluginRequestSender {
|
|||
/// [`POLL_INTERVAL`] while waiting. This lets Python callers run
|
||||
/// `py.check_signals()` so that `Ctrl+C`/`KeyboardInterrupt` is raised
|
||||
/// promptly instead of waiting for the full socket timeout.
|
||||
/// Partial reads are preserved across timeouts to keep framing synchronized.
|
||||
pub fn request_with_poll(
|
||||
&self,
|
||||
req: PluginRequest,
|
||||
poll: &mut dyn FnMut() -> Result<(), PluginRequestError>,
|
||||
) -> Result<PluginResponse, PluginRequestError> {
|
||||
use std::io::ErrorKind;
|
||||
let mut stream = self
|
||||
.stream
|
||||
.lock()
|
||||
|
|
@ -130,21 +202,17 @@ impl ProxyPluginRequestSender {
|
|||
stream
|
||||
.set_read_timeout(Some(POLL_INTERVAL))
|
||||
.map_err(|e| PluginRequestError(e.to_string()))?;
|
||||
let result = loop {
|
||||
match recv_framed(&mut *stream) {
|
||||
Ok(resp) => break Ok(resp),
|
||||
Err(ProxyError::Io(e))
|
||||
if e.kind() == ErrorKind::WouldBlock
|
||||
|| e.kind() == ErrorKind::TimedOut =>
|
||||
{
|
||||
if let Err(e) = poll() {
|
||||
let _ = stream.shutdown(Shutdown::Both);
|
||||
break Err(e);
|
||||
}
|
||||
}
|
||||
Err(e) => break Err(e.into()),
|
||||
|
||||
let result = match recv_framed_with_poll(&mut *stream, poll) {
|
||||
Ok(resp) => Ok(resp),
|
||||
Err(PollError::Interrupted(e)) => {
|
||||
// A partial response cannot be reused after interruption.
|
||||
let _ = stream.shutdown(Shutdown::Both);
|
||||
Err(e)
|
||||
}
|
||||
Err(PollError::Io(e)) => Err(e.into()),
|
||||
};
|
||||
|
||||
let _ = stream.set_read_timeout(original_timeout);
|
||||
result
|
||||
}
|
||||
|
|
@ -331,4 +399,55 @@ mod tests {
|
|||
.unwrap();
|
||||
assert!(matches!(resp, PluginResponse::Ok));
|
||||
}
|
||||
|
||||
/// A reader that returns one byte per call and then `WouldBlock` on the
|
||||
/// next call, exercising both the incremental read and the poll path.
|
||||
struct OneByteThenBlock<'a> {
|
||||
data: &'a [u8],
|
||||
block_next: bool,
|
||||
}
|
||||
|
||||
impl<'a> Read for OneByteThenBlock<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
if self.block_next {
|
||||
self.block_next = false;
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::WouldBlock,
|
||||
"simulated block",
|
||||
));
|
||||
}
|
||||
if self.data.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
let n = buf.len().min(1);
|
||||
buf[..n].copy_from_slice(&self.data[..n]);
|
||||
self.data = &self.data[n..];
|
||||
self.block_next = true;
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recv_framed_with_poll_reads_one_byte_at_a_time() {
|
||||
let msg = PluginResponse::Bool(true);
|
||||
let bytes = bincode::serialize(&msg).unwrap();
|
||||
let len = bytes.len() as u64;
|
||||
let mut framed = Vec::with_capacity(8 + bytes.len());
|
||||
framed.extend_from_slice(&len.to_le_bytes());
|
||||
framed.extend_from_slice(&bytes);
|
||||
|
||||
let mut reader = OneByteThenBlock {
|
||||
data: &framed,
|
||||
block_next: false,
|
||||
};
|
||||
let mut polls = 0;
|
||||
let resp = recv_framed_with_poll(&mut reader, &mut || {
|
||||
polls += 1;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
assert!(matches!(resp, PluginResponse::Bool(true)));
|
||||
// One byte, one block for every byte except the last (no poll after EOF).
|
||||
assert_eq!(polls, framed.len() - 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ mod tests {
|
|||
let registry: TypeRegistry =
|
||||
serde_json::from_str(get_embedded_type_registry_json()).expect("valid TypeRegistry JSON");
|
||||
|
||||
for name in ["Point", "Line", "Circle", "EntityCommon"] {
|
||||
for name in ["Point", "Line", "Circle", "MText", "EntityCommon"] {
|
||||
assert!(
|
||||
registry.types.contains_key(&TypeId::new(name)),
|
||||
"registry should contain {}",
|
||||
|
|
@ -42,6 +42,33 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn embedded_type_registry_mtext_is_struct_with_expected_fields() {
|
||||
let registry: TypeRegistry =
|
||||
serde_json::from_str(get_embedded_type_registry_json()).expect("valid TypeRegistry JSON");
|
||||
|
||||
let mtext = registry
|
||||
.types
|
||||
.get(&TypeId::new("MText"))
|
||||
.expect("MText in registry");
|
||||
assert_eq!(mtext.kind, TypeKind::Struct);
|
||||
|
||||
let field_names: Vec<&str> = mtext.fields.iter().map(|f| f.name.as_str()).collect();
|
||||
for expected in [
|
||||
"common",
|
||||
"value",
|
||||
"insertion_point",
|
||||
"height",
|
||||
"attachment_point",
|
||||
] {
|
||||
assert!(
|
||||
field_names.contains(&expected),
|
||||
"MText should have field {}",
|
||||
expected
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn embedded_type_registry_point_is_struct_with_expected_fields() {
|
||||
let registry: TypeRegistry =
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ publish = false
|
|||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "931c4ab", features = ["serde"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "94df2c3", features = ["serde"] }
|
||||
bincode = "1.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ crate-type = ["cdylib"]
|
|||
|
||||
[dependencies]
|
||||
ocs_plugin_api = { path = "../ocs_plugin_api", features = ["host"] }
|
||||
acadrust = "0.3.4"
|
||||
acadrust = { git = "https://github.com/ramox81/cadcodec.git", rev = "6dcda1a", features = ["serde"] }
|
||||
|
|
|
|||
|
|
@ -98,7 +98,8 @@ folder and load again on the next desktop launch.
|
|||
## Build & deploy
|
||||
|
||||
- Native: `cargo build --release --bin OpenCADStudio`.
|
||||
- Web: `trunk build --release --public-url /OpenCADStudio/`.
|
||||
- Web app: `trunk build --release --public-url /app/ --dist dist/app --html-output index.html web-app.html`.
|
||||
Run `sh scripts/assemble-site.sh` afterward to add the landing page.
|
||||
`.github/workflows/pages.yml` builds and deploys to GitHub Pages on every
|
||||
release. No COOP/COEP headers are needed because the web build is
|
||||
single-threaded.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="شعار Open CAD Studio"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>الانضمام إلى النقاش</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="مساحة عمل Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="مساحة عمل Open CAD Studio" width="100%"></p>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Open CAD Studio تطبيق متعدد المنصات للرسم التقني و
|
|||
- **تصيير عبر GPU** — إطارات عرض ثنائية وثلاثية الأبعاد مسرّعة بواسطة `wgpu` مع كاميرات متعامدة ومنظورية.
|
||||
- **سير عمل قابل للتوسعة** — إضافات أصلية ونصوص أوامر وتحويل من دون واجهة وواجهة JSON للأتمتة تعتمد على الأسطر.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="نموذج ثلاثي الأبعاد في Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="نموذج ثلاثي الأبعاد في Open CAD Studio" width="100%"></p>
|
||||
|
||||
## سير عمل الملفات
|
||||
|
||||
|
|
@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage
|
|||
|
||||
## اللغات
|
||||
|
||||
يمكن لـ Open CAD Studio اتباع لغة النظام أو استخدام إحدى لغات الواجهة التسع عشرة التالية:
|
||||
يمكن لـ Open CAD Studio اتباع لغة النظام أو استخدام إحدى لغات الواجهة العشرين التالية:
|
||||
|
||||
> العربية · البرتغالية البرازيلية · التشيكية · الهولندية · الإنجليزية · الفنلندية · الفرنسية · الألمانية · الهندية · المجرية · الإيطالية · اليابانية · الكورية · البولندية · الروسية · الصينية المبسطة · الإسبانية · الصينية التقليدية · التركية
|
||||
> العربية · البرتغالية البرازيلية · البلغارية · التشيكية · الهولندية · الإنجليزية · الفنلندية · الفرنسية · الألمانية · الهندية · المجرية · الإيطالية · اليابانية · الكورية · البولندية · الروسية · الصينية المبسطة · الإسبانية · الصينية التقليدية · التركية
|
||||
|
||||
غيّر اللغة من إعدادات التطبيق. عند اختيار **النظام**، يستخدم إصدار المتصفح أيضاً الإعدادات المحلية المفضلة للمتصفح.
|
||||
|
||||
|
|
|
|||
197
docs/readme/README.bg.md
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Лого на Open CAD Studio"></p>
|
||||
|
||||
<h1 align="center">Open CAD Studio</h1>
|
||||
|
||||
<p align="center">Приложение с отворен код за 2D чертане и 3D моделиране за настолни системи и уеб, разработено с Rust.</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/HakanSeven12/OpenCADStudio/releases/latest"><img alt="Последна версия" src="https://img.shields.io/github/v/release/HakanSeven12/OpenCADStudio"></a>
|
||||
<a href="https://github.com/HakanSeven12/OpenCADStudio/releases"><img alt="Изтегляния на версии" src="https://img.shields.io/github/downloads/HakanSeven12/OpenCADStudio/total"></a>
|
||||
<a href="https://github.com/HakanSeven12/OpenCADStudio/stargazers"><img alt="Звезди в GitHub" src="https://img.shields.io/github/stars/HakanSeven12/OpenCADStudio"></a>
|
||||
<a href="../../LICENSE"><img alt="Лиценз GPL-3.0" src="https://img.shields.io/github/license/HakanSeven12/OpenCADStudio"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.opencadstudio.com"><strong>Отворете уеб приложението</strong></a> ·
|
||||
<a href="https://github.com/HakanSeven12/OpenCADStudio/releases/latest"><strong>Изтеглете настолното приложение</strong></a> ·
|
||||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Присъединете се към дискусията</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Работно пространство на Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Общ преглед
|
||||
|
||||
Open CAD Studio е междуплатформено приложение за техническо чертане, работа с оформления и моделиране на твърди тела. То чете и записва DWG и DXF чертежи директно, като настолната и браузърната версия използват общо ядро за редактиране.
|
||||
|
||||
Проектът се разработва активно. Пазете резервни копия на важните производствени чертежи и съобщавайте възпроизводими проблеми чрез [GitHub Issues](https://github.com/HakanSeven12/OpenCADStudio/issues).
|
||||
|
||||
## Основни възможности
|
||||
|
||||
- **Директен работен процес с чертежи** — отваряйте, редактирайте, възстановявайте и записвайте DWG и DXF файлове без услуга за преобразуване.
|
||||
- **Прецизно 2D чертане** — линии, полилинии, криви, сплайни, щриховки, обектно прихващане, проследяване, слоеве, блокове и външни препратки.
|
||||
- **Инструменти за документация** — текст, размери, указателни линии, допуски, таблици, моделно пространство, листово пространство, изгледи и стилове за плотиране.
|
||||
- **3D моделиране с геометрично ядро** — твърдотелни примитиви, екструдиране, завъртане, изтегляне по траектория, loft, булеви операции и теселация на ACIS обекти.
|
||||
- **GPU визуализация** — ускорени 2D и 3D изгледи чрез `wgpu`, с ортографски и перспективни камери.
|
||||
- **Разширяеми работни процеси** — локални приставки, командни скриптове, преобразуване без графичен интерфейс и редово ориентиран JSON API за автоматизация.
|
||||
|
||||
<p align="center"><img src="../../site/modeling.png" alt="3D модел в Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Работа с файлове
|
||||
|
||||
| Формат или работен процес | Поддръжка |
|
||||
| --- | --- |
|
||||
| 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
|
||||
|
||||
Изтеглете x86-64 AppImage файла, направете го изпълним и го стартирайте:
|
||||
|
||||
```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**.
|
||||
|
||||
Приложението е подписано ad hoc, но към момента не е нотариално заверено от Apple.
|
||||
|
||||
## Езици
|
||||
|
||||
Open CAD Studio може да следва системния език или да използва един от следните 20 езика на интерфейса:
|
||||
|
||||
> Арабски · Бразилски португалски · Български · Чешки · Нидерландски · Английски · Фински · Френски · Немски · Хинди · Унгарски · Италиански · Японски · Корейски · Полски · Руски · Опростен китайски · Испански · Традиционен китайски · Турски
|
||||
|
||||
Променете езика от настройките на приложението. Когато е избрано **Системен език**, браузърната версия също използва предпочитания локал на браузъра.
|
||||
|
||||
## Компилиране от изходния код
|
||||
|
||||
### Настолна версия
|
||||
|
||||
Изисквания:
|
||||
|
||||
- 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` (`OpenCADStudio.exe` в Windows).
|
||||
|
||||
### Уеб версия
|
||||
|
||||
Инсталирайте еднократно 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
|
||||
```
|
||||
|
||||
Сървърът обменя по един JSON обект на ред чрез стандартния вход/изход или локален TCP сокет. Вижте [ръководството за автоматизация](../automation/README.md) и включения [Python клиент](../automation/ocs.py).
|
||||
|
||||
## Приставки
|
||||
|
||||
Настолните приставки работят в отделни процеси и комуникират с основното приложение чрез версионирания API за приставки. Браузърната версия не зарежда локални приставки.
|
||||
|
||||
- [Архитектура на приставките](../plugin-architecture.md)
|
||||
- [Шаблон за приставка](../plugin-template/README.md)
|
||||
- [Регистър на приставките](../../plugins/README.md)
|
||||
|
||||
## Документация на проекта
|
||||
|
||||
- [API за автоматизация](../automation/README.md)
|
||||
- [Архитектура на приставките](../plugin-architecture.md)
|
||||
- [Процес на теселация](../tessellation.md)
|
||||
- [Политика за сигурност](../../SECURITY.md)
|
||||
|
||||
## Принос
|
||||
|
||||
Приветстват се доклади за грешки, целенасочени pull request-и, преводи, подобрения на документацията и приноси към приставките.
|
||||
|
||||
- Потърсете в съществуващите [issues](https://github.com/HakanSeven12/OpenCADStudio/issues), преди да отворите нов доклад.
|
||||
- Използвайте [Discussions](https://github.com/HakanSeven12/OpenCADStudio/discussions) за въпроси и идеи.
|
||||
- Докладвайте уязвимости поверително, като следвате [политиката за сигурност](../../SECURITY.md).
|
||||
|
||||
## Развитие на проекта
|
||||
|
||||
<a href="https://github.com/HakanSeven12/OpenCADStudio/stargazers">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://www.opencadstudio.com/star-history-dark.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://www.opencadstudio.com/star-history-light.svg">
|
||||
<img alt="Звезди и изтегляния на Open CAD Studio" src="https://www.opencadstudio.com/star-history-light.svg">
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Подкрепете проекта
|
||||
|
||||
Ако 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).
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Logo Open CAD Studio"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Zapojit se do diskuse</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Pracovní plocha Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Pracovní plocha Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Přehled
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Projekt je aktivně vyvíjen. Důležité produkční výkresy zálohujte a repr
|
|||
- **Vykreslování přes GPU** — akcelerované 2D a 3D pohledy pomocí `wgpu`, s ortografickou a perspektivní kamerou.
|
||||
- **Rozšiřitelné postupy** — nativní pluginy, příkazové skripty, bezobslužná konverze a řádkové JSON automatizační API.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D model v Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="3D model v Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Práce se soubory
|
||||
|
||||
|
|
@ -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 19 jazyků rozhraní:
|
||||
Open CAD Studio může používat jazyk systému nebo jeden z těchto 20 jazyků rozhraní:
|
||||
|
||||
> Arabština · Brazilská portugalština · Čeština · Nizozemština · Angličtina · Finština · Francouzština · Němčina · 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 · 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
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Open-CAD-Studio-Logo"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>An Diskussionen teilnehmen</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Arbeitsbereich von Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Arbeitsbereich von Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Überblick
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Das Projekt wird aktiv entwickelt. Bewahre Sicherungskopien wichtiger Produktion
|
|||
- **GPU-Darstellung** — beschleunigte 2D- und 3D-Ansichten über `wgpu` mit orthografischen und perspektivischen Kameras.
|
||||
- **Erweiterbare Abläufe** — native Plugins, Befehlsskripte, Konvertierung ohne Oberfläche und zeilenbasierte JSON-Automatisierungs-API.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D-Modell in Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="3D-Modell in Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Dateiabläufe
|
||||
|
||||
|
|
@ -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 19 Oberflächensprachen verwenden:
|
||||
Open CAD Studio kann der Systemsprache folgen oder eine dieser 20 Oberflächensprachen verwenden:
|
||||
|
||||
> Arabisch · Brasilianisches Portugiesisch · Tschechisch · Niederländisch · Englisch · Finnisch · Französisch · Deutsch · 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 · Hindi · Ungarisch · Italienisch · Japanisch · Koreanisch · 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Logotipo de Open CAD Studio"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Participar en la conversación</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Espacio de trabajo de Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Espacio de trabajo de Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Descripción general
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ El proyecto está en desarrollo activo. Conserva copias de seguridad de los dibu
|
|||
- **Renderizado por GPU** — vistas 2D y 3D aceleradas mediante `wgpu`, con cámaras ortográfica y en perspectiva.
|
||||
- **Flujos ampliables** — complementos nativos, scripts de comandos, conversión sin interfaz y una API de automatización JSON basada en líneas.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Modelo 3D en Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Modelo 3D en Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Flujos de archivos
|
||||
|
||||
|
|
@ -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 19 idiomas de interfaz:
|
||||
Open CAD Studio puede seguir el idioma del sistema o usar cualquiera de estos 20 idiomas de interfaz:
|
||||
|
||||
> Árabe · Portugués de Brasil · Checo · Neerlandés · Inglés · Finés · Francés · Alemán · 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 · Hindi · Húngaro · Italiano · Japonés · Coreano · 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**.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Open CAD Studion logo"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Osallistu keskusteluun</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studion työtila" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Open CAD Studion työtila" width="100%"></p>
|
||||
|
||||
## Yleiskatsaus
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Projektia kehitetään aktiivisesti. Säilytä tärkeistä tuotantopiirustuksist
|
|||
- **GPU-renderöinti** — `wgpu`:n kiihdyttämät 2D- ja 3D-näkymät sekä ortografiset ja perspektiivikamerat.
|
||||
- **Laajennettavat työnkulut** — natiiviliitännäiset, komentoskriptit, käyttöliittymätön muunnos ja rivipohjainen JSON-automaatiorajapinta.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D-malli Open CAD Studiossa" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="3D-malli Open CAD Studiossa" width="100%"></p>
|
||||
|
||||
## Tiedostotyönkulut
|
||||
|
||||
|
|
@ -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ä 19 käyttöliittymäkielestä:
|
||||
Open CAD Studio voi seurata järjestelmän kieltä tai käyttää jotakin näistä 20 käyttöliittymäkielestä:
|
||||
|
||||
> Arabia · Brasilianportugali · Tšekki · Hollanti · Englanti · Suomi · Ranska · Saksa · Hindi · Unkari · Italia · Japani · Korea · Puola · Venäjä · Yksinkertaistettu kiina · Espanja · Perinteinen kiina · Turkki
|
||||
> Arabia · Brasilianportugali · Bulgaria · Tšekki · Hollanti · Englanti · Suomi · Ranska · Saksa · Hindi · Unkari · Italia · Japani · Korea · 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Logo Open CAD Studio"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Participer aux discussions</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Espace de travail Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Espace de travail Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Présentation
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Le projet est en développement actif. Conservez des sauvegardes des dessins de
|
|||
- **Rendu GPU** — vues 2D et 3D accélérées par `wgpu`, avec caméras orthographique et perspective.
|
||||
- **Flux extensibles** — plugins natifs, scripts de commandes, conversion sans interface et API d’automatisation JSON ligne par ligne.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Modèle 3D dans Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Modèle 3D dans Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Flux de fichiers
|
||||
|
||||
|
|
@ -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 19 langues d’interface :
|
||||
Open CAD Studio peut suivre la langue du système ou utiliser l’une de ces 20 langues d’interface :
|
||||
|
||||
> Arabe · Portugais du Brésil · Tchèque · Néerlandais · Anglais · Finnois · Français · Allemand · 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 · Hindi · Hongrois · Italien · Japonais · Coréen · 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é.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Open CAD Studio लोगो"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>चर्चा में शामिल हों</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio कार्यक्षेत्र" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio कार्यक्षेत्र" width="100%"></p>
|
||||
|
||||
## परिचय
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Open CAD Studio तकनीकी ड्राइंग, लेआउट का
|
|||
- **GPU रेंडरिंग** — `wgpu` से तेज़ किए गए 2D और 3D व्यूपोर्ट, ऑर्थोग्राफ़िक और पर्सपेक्टिव कैमरा सहित।
|
||||
- **विस्तार योग्य कार्यप्रवाह** — मूल प्लगइन, कमांड स्क्रिप्ट, हेडलेस रूपांतरण और पंक्ति-आधारित JSON स्वचालन API।
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio में 3D मॉडल" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio में 3D मॉडल" width="100%"></p>
|
||||
|
||||
## फ़ाइल कार्यप्रवाह
|
||||
|
||||
|
|
@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage
|
|||
|
||||
## भाषाएँ
|
||||
|
||||
Open CAD Studio सिस्टम भाषा का अनुसरण कर सकता है या इन 19 इंटरफ़ेस भाषाओं में से किसी एक का उपयोग कर सकता है:
|
||||
Open CAD Studio सिस्टम भाषा का अनुसरण कर सकता है या इन 20 इंटरफ़ेस भाषाओं में से किसी एक का उपयोग कर सकता है:
|
||||
|
||||
> अरबी · ब्राज़ीलियाई पुर्तगाली · चेक · डच · अंग्रेज़ी · फ़िनिश · फ़्रेंच · जर्मन · हिन्दी · हंगेरियन · इतालवी · जापानी · कोरियाई · पोलिश · रूसी · सरलीकृत चीनी · स्पैनिश · पारंपरिक चीनी · तुर्की
|
||||
> अरबी · ब्राज़ीलियाई पुर्तगाली · बुल्गारियाई · चेक · डच · अंग्रेज़ी · फ़िनिश · फ़्रेंच · जर्मन · हिन्दी · हंगेरियन · इतालवी · जापानी · कोरियाई · पोलिश · रूसी · सरलीकृत चीनी · स्पैनिश · पारंपरिक चीनी · तुर्की
|
||||
|
||||
अनुप्रयोग सेटिंग में भाषा बदलें। **सिस्टम** चुने जाने पर ब्राउज़र संस्करण भी ब्राउज़र की पसंदीदा लोकेल का उपयोग करता है।
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Open CAD Studio logó"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Csatlakozás a beszélgetéshez</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio munkaterület" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio munkaterület" width="100%"></p>
|
||||
|
||||
## Áttekintés
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ A projekt aktív fejlesztés alatt áll. A fontos gyártási rajzokról tarts bi
|
|||
- **GPU-megjelenítés** — `wgpu` által gyorsított 2D és 3D nézetek, ortografikus és perspektivikus kamerákkal.
|
||||
- **Bővíthető munkafolyamatok** — natív bővítmények, parancsfájlok, felület nélküli konverzió és soralapú JSON automatizálási API.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D modell az Open CAD Studióban" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="3D modell az Open CAD Studióban" width="100%"></p>
|
||||
|
||||
## Fájlmunkafolyamatok
|
||||
|
||||
|
|
@ -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 19 felületi nyelv egyikét:
|
||||
Az Open CAD Studio követheti a rendszer nyelvét, vagy használhatja az alábbi 20 felületi nyelv egyikét:
|
||||
|
||||
> Arab · Brazil portugál · Cseh · Holland · Angol · Finn · Francia · Német · 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 · Hindi · Magyar · Olasz · Japán · Koreai · 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Logo di Open CAD Studio"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Partecipa alla discussione</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Area di lavoro di Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Area di lavoro di Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Panoramica
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Il progetto è in sviluppo attivo. Conserva copie di sicurezza dei disegni di pr
|
|||
- **Rendering GPU** — viste 2D e 3D accelerate tramite `wgpu`, con telecamere ortografiche e prospettiche.
|
||||
- **Flussi estensibili** — plugin nativi, script di comandi, conversione headless e API di automazione JSON basata su righe.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Modello 3D in Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Modello 3D in Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Flussi dei file
|
||||
|
||||
|
|
@ -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 19 lingue dell’interfaccia:
|
||||
Open CAD Studio può seguire la lingua di sistema o usare una di queste 20 lingue dell’interfaccia:
|
||||
|
||||
> Arabo · Portoghese brasiliano · Ceco · Olandese · Inglese · Finlandese · Francese · Tedesco · Hindi · Ungherese · Italiano · Giapponese · Coreano · Polacco · Russo · Cinese semplificato · Spagnolo · Cinese tradizionale · Turco
|
||||
> Arabo · Portoghese brasiliano · Bulgaro · Ceco · Olandese · Inglese · Finlandese · Francese · Tedesco · Hindi · Ungherese · Italiano · Giapponese · Coreano · 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**.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Open CAD Studio ロゴ"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>ディスカッションに参加</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio ワークスペース" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio ワークスペース" width="100%"></p>
|
||||
|
||||
## 概要
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Open CAD Studio は、技術製図、レイアウト作業、ソリッドモデ
|
|||
- **GPU レンダリング** — `wgpu` による高速な 2D/3D ビューポートと、正投影・透視投影カメラ。
|
||||
- **拡張可能なワークフロー** — ネイティブプラグイン、コマンドスクリプト、ヘッドレス変換、行単位の JSON 自動化 API。
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio の 3D モデル" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio の 3D モデル" width="100%"></p>
|
||||
|
||||
## ファイルワークフロー
|
||||
|
||||
|
|
@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage
|
|||
|
||||
## 言語
|
||||
|
||||
Open CAD Studio はシステム言語に従うか、次の 19 のインターフェース言語を使用できます。
|
||||
Open CAD Studio はシステム言語に従うか、次の 20 のインターフェース言語を使用できます。
|
||||
|
||||
> アラビア語 · ブラジルポルトガル語 · チェコ語 · オランダ語 · 英語 · フィンランド語 · フランス語 · ドイツ語 · ヒンディー語 · ハンガリー語 · イタリア語 · 日本語 · 韓国語 · ポーランド語 · ロシア語 · 簡体字中国語 · スペイン語 · 繁体字中国語 · トルコ語
|
||||
> アラビア語 · ブラジルポルトガル語 · ブルガリア語 · チェコ語 · オランダ語 · 英語 · フィンランド語 · フランス語 · ドイツ語 · ヒンディー語 · ハンガリー語 · イタリア語 · 日本語 · 韓国語 · ポーランド語 · ロシア語 · 簡体字中国語 · スペイン語 · 繁体字中国語 · トルコ語
|
||||
|
||||
言語はアプリケーション設定から変更できます。**システム** を選ぶと、ブラウザ版もブラウザの優先ロケールを使用します。
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Open CAD Studio 로고"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>토론 참여</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio 작업 공간" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio 작업 공간" width="100%"></p>
|
||||
|
||||
## 개요
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Open CAD Studio는 기술 도면 작성, 배치 작업, 솔리드 모델링을
|
|||
- **GPU 렌더링** — `wgpu`로 가속된 2D 및 3D 뷰포트와 직교·원근 카메라를 제공합니다.
|
||||
- **확장 가능한 작업 흐름** — 네이티브 플러그인, 명령 스크립트, 헤드리스 변환, 줄 단위 JSON 자동화 API를 지원합니다.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio의 3D 모델" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio의 3D 모델" width="100%"></p>
|
||||
|
||||
## 파일 작업 흐름
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage
|
|||
|
||||
Open CAD Studio는 시스템 언어를 따르거나 다음 19개 인터페이스 언어 중 하나를 사용할 수 있습니다.
|
||||
|
||||
> 아랍어 · 브라질 포르투갈어 · 체코어 · 네덜란드어 · 영어 · 핀란드어 · 프랑스어 · 독일어 · 힌디어 · 헝가리어 · 이탈리아어 · 일본어 · 한국어 · 폴란드어 · 러시아어 · 중국어 간체 · 스페인어 · 중국어 번체 · 터키어
|
||||
> 아랍어 · 브라질 포르투갈어 · 불가리아어 · 체코어 · 네덜란드어 · 영어 · 핀란드어 · 프랑스어 · 독일어 · 힌디어 · 헝가리어 · 이탈리아어 · 일본어 · 한국어 · 폴란드어 · 러시아어 · 중국어 간체 · 스페인어 · 중국어 번체 · 터키어
|
||||
|
||||
애플리케이션 설정에서 언어를 변경할 수 있습니다. **시스템**을 선택하면 브라우저 버전도 브라우저의 기본 로캘을 사용합니다.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Logo van Open CAD Studio"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Deelnemen aan discussies</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Werkruimte van Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Werkruimte van Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Overzicht
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Het project wordt actief ontwikkeld. Bewaar reservekopieën van belangrijke prod
|
|||
- **GPU-rendering** — versnelde 2D- en 3D-viewports via `wgpu`, met orthografische en perspectiefcamera's.
|
||||
- **Uitbreidbare workflows** — native plug-ins, opdrachtscripts, headless conversie en een regelgebaseerde JSON-automatiserings-API.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D-model in Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="3D-model in Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Bestandsworkflows
|
||||
|
||||
|
|
@ -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 19 interfacetalen gebruiken:
|
||||
Open CAD Studio kan de systeemtaal volgen of een van deze 20 interfacetalen gebruiken:
|
||||
|
||||
> Arabisch · Braziliaans-Portugees · Tsjechisch · Nederlands · Engels · Fins · Frans · Duits · Hindi · Hongaars · Italiaans · Japans · Koreaans · Pools · Russisch · Vereenvoudigd Chinees · Spaans · Traditioneel Chinees · Turks
|
||||
> Arabisch · Braziliaans-Portugees · Bulgaars · Tsjechisch · Nederlands · Engels · Fins · Frans · Duits · Hindi · Hongaars · Italiaans · Japans · Koreaans · 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Logo Open CAD Studio"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Dołącz do dyskusji</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Obszar roboczy Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Obszar roboczy Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Przegląd
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Projekt jest aktywnie rozwijany. Zachowuj kopie zapasowe ważnych rysunków prod
|
|||
- **Renderowanie GPU** — przyspieszone widoki 2D i 3D przez `wgpu`, z kamerą ortograficzną i perspektywiczną.
|
||||
- **Rozszerzalne przepływy pracy** — natywne wtyczki, skrypty poleceń, konwersja bez interfejsu i wierszowe API automatyzacji JSON.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Model 3D w Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Model 3D w Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Przepływy plików
|
||||
|
||||
|
|
@ -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 19 języków interfejsu:
|
||||
Open CAD Studio może korzystać z języka systemu lub jednego z 20 języków interfejsu:
|
||||
|
||||
> Arabski · Portugalski brazylijski · Czeski · Niderlandzki · Angielski · Fiński · Francuski · Niemiecki · 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 · Hindi · Węgierski · Włoski · Japoński · Koreań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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Logotipo do Open CAD Studio"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Participar da discussão</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Área de trabalho do Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Área de trabalho do Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Visão geral
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ O projeto está em desenvolvimento ativo. Mantenha cópias de segurança de dese
|
|||
- **Renderização por GPU** — viewports 2D e 3D aceleradas por `wgpu`, com câmeras ortográfica e em perspectiva.
|
||||
- **Fluxos extensíveis** — plugins nativos, scripts de comandos, conversão sem interface e API de automação JSON baseada em linhas.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Modelo 3D no Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Modelo 3D no Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Fluxos de arquivos
|
||||
|
||||
|
|
@ -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 19 idiomas de interface:
|
||||
Open CAD Studio pode seguir o idioma do sistema ou usar um destes 20 idiomas de interface:
|
||||
|
||||
> Árabe · Português do Brasil · Tcheco · Holandês · Inglês · Finlandês · Francês · Alemão · 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 · Hindi · Húngaro · Italiano · Japonês · Coreano · 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Логотип Open CAD Studio"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Присоединиться к обсуждению</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Рабочее пространство Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Рабочее пространство Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Обзор
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Open CAD Studio — кроссплатформенное приложение д
|
|||
- **Отрисовка на GPU** — ускоренные 2D- и 3D-виды через `wgpu`, ортографическая и перспективная камеры.
|
||||
- **Расширяемые процессы** — нативные плагины, сценарии команд, преобразование без интерфейса и построчный JSON API автоматизации.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D-модель в Open CAD Studio" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="3D-модель в Open CAD Studio" width="100%"></p>
|
||||
|
||||
## Работа с файлами
|
||||
|
||||
|
|
@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage
|
|||
|
||||
## Языки
|
||||
|
||||
Open CAD Studio может следовать языку системы или использовать один из 19 языков интерфейса:
|
||||
Open CAD Studio может следовать языку системы или использовать один из 20 языков интерфейса:
|
||||
|
||||
> Арабский · Бразильский португальский · Чешский · Нидерландский · Английский · Финский · Французский · Немецкий · Хинди · Венгерский · Итальянский · Японский · Корейский · Польский · Русский · Упрощённый китайский · Испанский · Традиционный китайский · Турецкий
|
||||
> Арабский · Бразильский португальский · Болгарский · Чешский · Нидерландский · Английский · Финский · Французский · Немецкий · Хинди · Венгерский · Итальянский · Японский · Корейский · Польский · Русский · Упрощённый китайский · Испанский · Традиционный китайский · Турецкий
|
||||
|
||||
Язык меняется в настройках приложения. При выборе **Система** версия для браузера также использует предпочтительную локаль браузера.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Open CAD Studio logosu"></p>
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Tartışmalara katıl</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio çalışma alanı" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio çalışma alanı" width="100%"></p>
|
||||
|
||||
## Genel bakış
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ Proje aktif olarak geliştirilmektedir. Önemli üretim çizimlerinin yedeklerin
|
|||
- **GPU ile görüntüleme** — `wgpu` üzerinden hızlandırılmış 2B ve 3B görünüm pencereleri; ortografik ve perspektif kameralar.
|
||||
- **Genişletilebilir iş akışları** — yerel eklentiler, komut betikleri, arayüzsüz dönüştürme ve satır tabanlı JSON otomasyon API'si.
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio içinde 3B model" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio içinde 3B model" width="100%"></p>
|
||||
|
||||
## Dosya iş akışları
|
||||
|
||||
|
|
@ -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 19 arayüz dilinden birini kullanabilir:
|
||||
Open CAD Studio sistem dilini izleyebilir veya şu 20 arayüz dilinden birini kullanabilir:
|
||||
|
||||
> Arapça · Brezilya Portekizcesi · Çekçe · Felemenkçe · İngilizce · Fince · Fransızca · Almanca · 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 · Hintçe · Macarca · İtalyanca · Japonca · Korece · 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Open CAD Studio 标志"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>参与讨论</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio 工作区" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio 工作区" width="100%"></p>
|
||||
|
||||
## 概览
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Open CAD Studio 是一款用于技术绘图、布局设计和实体建模的跨
|
|||
- **GPU 渲染** — 通过 `wgpu` 加速二维和三维视口,并支持正交与透视相机。
|
||||
- **可扩展工作流** — 支持原生插件、命令脚本、无界面转换和逐行 JSON 自动化 API。
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio 中的三维模型" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio 中的三维模型" width="100%"></p>
|
||||
|
||||
## 文件工作流
|
||||
|
||||
|
|
@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage
|
|||
|
||||
## 语言
|
||||
|
||||
Open CAD Studio 可以跟随系统语言,也可以使用以下 19 种界面语言:
|
||||
Open CAD Studio 可以跟随系统语言,也可以使用以下 20 种界面语言:
|
||||
|
||||
> 阿拉伯语 · 巴西葡萄牙语 · 捷克语 · 荷兰语 · 英语 · 芬兰语 · 法语 · 德语 · 印地语 · 匈牙利语 · 意大利语 · 日语 · 韩语 · 波兰语 · 俄语 · 简体中文 · 西班牙语 · 繁体中文 · 土耳其语
|
||||
> 阿拉伯语 · 巴西葡萄牙语 · 保加利亚语 · 捷克语 · 荷兰语 · 英语 · 芬兰语 · 法语 · 德语 · 印地语 · 匈牙利语 · 意大利语 · 日语 · 韩语 · 波兰语 · 俄语 · 简体中文 · 西班牙语 · 繁体中文 · 土耳其语
|
||||
|
||||
可在应用设置中更改语言。选择**系统**时,浏览器版也会使用浏览器的首选区域设置。
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<a href="../../README.md">English</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
<a href="../../README.md">English</a> · <a href="README.bg.md">Български</a> · <a href="README.pt-BR.md">Português (Brasil)</a> · <a href="README.cs.md">Čeština</a> · <a href="README.nl.md">Nederlands</a> · <a href="README.fr.md">Français</a> · <a href="README.fi.md">Suomi</a> · <a href="README.de.md">Deutsch</a> · <a href="README.hu.md">Magyar</a> · <a href="README.it.md">Italiano</a> · <a href="README.ja.md">日本語</a> · <a href="README.ko.md">한국어</a> · <a href="README.pl.md">Polski</a> · <a href="README.ru.md">Русский</a> · <a href="README.zh-CN.md">简体中文</a> · <a href="README.es.md">Español</a> · <a href="README.zh-TW.md">繁體中文</a> · <a href="README.tr.md">Türkçe</a> · <a href="README.hi.md">हिन्दी</a> · <a href="README.ar.md">العربية</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="../../assets/logo.svg" width="112" alt="Open CAD Studio 標誌"></p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>參與討論</strong></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio 工作區" width="100%"></p>
|
||||
<p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio 工作區" width="100%"></p>
|
||||
|
||||
## 概覽
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ Open CAD Studio 是用於技術繪圖、配置作業和實體建模的跨平台
|
|||
- **GPU 轉譯** — 透過 `wgpu` 加速二維和三維視埠,並支援正投影與透視相機。
|
||||
- **可擴充工作流程** — 支援原生外掛程式、指令碼、無介面轉換和逐行 JSON 自動化 API。
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio 中的三維模型" width="100%"></p>
|
||||
<p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio 中的三維模型" width="100%"></p>
|
||||
|
||||
## 檔案工作流程
|
||||
|
||||
|
|
@ -91,9 +91,9 @@ chmod +x OpenCADStudio-*-linux-x86_64.AppImage
|
|||
|
||||
## 語言
|
||||
|
||||
Open CAD Studio 可以跟隨系統語言,也可以使用以下 19 種介面語言:
|
||||
Open CAD Studio 可以跟隨系統語言,也可以使用以下 20 種介面語言:
|
||||
|
||||
> 阿拉伯文 · 巴西葡萄牙文 · 捷克文 · 荷蘭文 · 英文 · 芬蘭文 · 法文 · 德文 · 北印度文 · 匈牙利文 · 義大利文 · 日文 · 韓文 · 波蘭文 · 俄文 · 簡體中文 · 西班牙文 · 繁體中文 · 土耳其文
|
||||
> 阿拉伯文 · 巴西葡萄牙文 · 保加利亞文 · 捷克文 · 荷蘭文 · 英文 · 芬蘭文 · 法文 · 德文 · 北印度文 · 匈牙利文 · 義大利文 · 日文 · 韓文 · 波蘭文 · 俄文 · 簡體中文 · 西班牙文 · 繁體中文 · 土耳其文
|
||||
|
||||
可在應用程式設定中變更語言。選擇**系統**時,瀏覽器版也會使用瀏覽器的偏好地區設定。
|
||||
|
||||
|
|
|
|||
392
index.html
|
|
@ -1,80 +1,330 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Open CAD Studio</title>
|
||||
<link data-trunk rel="icon" href="assets/logo.svg" />
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: #1e1e1e; }
|
||||
canvas { display: block; }
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Open CAD Studio — Open-Source DWG & DXF CAD</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Open-source CAD for native DWG and DXF editing, precise 2D drafting, layouts and kernel-backed 3D modeling. Use it on the web or download it for desktop."
|
||||
/>
|
||||
<meta name="robots" content="index, follow, max-image-preview:large" />
|
||||
<meta name="theme-color" content="#0b1016" />
|
||||
<meta name="color-scheme" content="dark" />
|
||||
<link rel="canonical" href="https://www.opencadstudio.com/" />
|
||||
<link rel="icon" href="/assets/logo.svg" type="image/svg+xml" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="stylesheet" href="/site.css" />
|
||||
<link rel="preload" as="image" href="/assets/workspace.png" fetchpriority="high" />
|
||||
|
||||
/* Splash shown while the wasm bundle downloads and initializes. Removed
|
||||
once the app's canvas appears (see the observer script below). */
|
||||
#loading {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 22px;
|
||||
background: #1e1e1e;
|
||||
color: #cfcfcf;
|
||||
font-family: system-ui, sans-serif;
|
||||
z-index: 9999;
|
||||
transition: opacity 0.4s ease;
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="Open CAD Studio" />
|
||||
<meta property="og:title" content="Open CAD Studio — Open-Source DWG & DXF CAD" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Technical drawing and solid modeling for desktop and web, built in Rust and open to everyone."
|
||||
/>
|
||||
<meta property="og:url" content="https://www.opencadstudio.com/" />
|
||||
<meta property="og:image" content="https://www.opencadstudio.com/og.png" />
|
||||
<meta property="og:image:alt" content="Open CAD Studio technical drawing and 3D modeling" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="Open CAD Studio — Open-Source DWG & DXF CAD" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="Native DWG and DXF editing, precise 2D drafting and kernel-backed 3D modeling."
|
||||
/>
|
||||
<meta name="twitter:image" content="https://www.opencadstudio.com/og.png" />
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@graph": [
|
||||
{
|
||||
"@type": "WebSite",
|
||||
"@id": "https://www.opencadstudio.com/#website",
|
||||
"url": "https://www.opencadstudio.com/",
|
||||
"name": "Open CAD Studio",
|
||||
"alternateName": "OCS",
|
||||
"description": "Open-source 2D drafting and 3D modeling for desktop and web."
|
||||
},
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"@id": "https://www.opencadstudio.com/#software",
|
||||
"name": "Open CAD Studio",
|
||||
"url": "https://www.opencadstudio.com/",
|
||||
"downloadUrl": "https://github.com/HakanSeven12/OpenCADStudio/releases/latest",
|
||||
"installUrl": "https://www.opencadstudio.com/app/",
|
||||
"applicationCategory": "DesignApplication",
|
||||
"operatingSystem": "Windows, Linux, macOS, Web",
|
||||
"isAccessibleForFree": true,
|
||||
"license": "https://www.gnu.org/licenses/gpl-3.0.html",
|
||||
"codeRepository": "https://github.com/HakanSeven12/OpenCADStudio",
|
||||
"description": "Open-source CAD for DWG and DXF editing, 2D drafting, layouts and 3D solid modeling.",
|
||||
"featureList": [
|
||||
"Native DWG and DXF read and write",
|
||||
"Precise 2D drafting and documentation",
|
||||
"Model space, paper space and viewports",
|
||||
"Kernel-backed 3D solid modeling",
|
||||
"Browser and desktop editions"
|
||||
],
|
||||
"offers": {
|
||||
"@type": "Offer",
|
||||
"price": "0",
|
||||
"priceCurrency": "USD"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
#loading.hide { opacity: 0; pointer-events: none; }
|
||||
#loading .title { font-size: 22px; font-weight: 600; letter-spacing: 0.5px; }
|
||||
#loading .title span { color: #3399e6; }
|
||||
#loading .sub { font-size: 12px; color: #777; }
|
||||
#loading .spinner {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
border: 4px solid #2e2e2e;
|
||||
border-top-color: #3399e6;
|
||||
border-radius: 50%;
|
||||
animation: ocs-spin 0.9s linear infinite;
|
||||
}
|
||||
@keyframes ocs-spin { to { transform: rotate(360deg); } }
|
||||
</style>
|
||||
<!-- Trunk builds the wasm32 binary. The crate has no Cargo features:
|
||||
the geometry kernel is pure Rust, so the web build gets the same
|
||||
solid modelling the native one does. See issue #45. -->
|
||||
<link data-trunk rel="rust" data-bin="OpenCADStudio" />
|
||||
<!-- Per-script Noto subsets, fetched lazily at runtime (one alphabet per
|
||||
file) so CAD text renders non-Latin scripts on the web. (#141) -->
|
||||
<link data-trunk rel="copy-dir" href="web/fonts" />
|
||||
<link data-trunk rel="copy-file" href="web/ocs-parse-worker.js" />
|
||||
<link data-trunk rel="copy-file" href="web/discussions.json" />
|
||||
<link data-trunk rel="copy-file" href="web/videos.json" />
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="loading">
|
||||
<div class="spinner"></div>
|
||||
<div class="title">Open <span>CAD</span> Studio</div>
|
||||
<div class="sub">Loading…</div>
|
||||
</div>
|
||||
<script>
|
||||
// The app mounts a <canvas> into <body> when it's ready. Watch for it and
|
||||
// fade the splash out.
|
||||
(function () {
|
||||
const splash = document.getElementById("loading");
|
||||
const done = () => {
|
||||
if (!splash) return;
|
||||
splash.classList.add("hide");
|
||||
setTimeout(() => splash.remove(), 450);
|
||||
};
|
||||
if (document.querySelector("canvas")) return done();
|
||||
const obs = new MutationObserver(() => {
|
||||
if (document.querySelector("canvas")) {
|
||||
obs.disconnect();
|
||||
done();
|
||||
}
|
||||
});
|
||||
obs.observe(document.body, { childList: true, subtree: true });
|
||||
})();
|
||||
</script>
|
||||
<a class="skip-link" href="#main">Skip to content</a>
|
||||
|
||||
<header class="site-header">
|
||||
<div class="shell nav-shell">
|
||||
<a class="brand" href="/" aria-label="Open CAD Studio home">
|
||||
<img src="/assets/logo.svg" width="38" height="38" alt="" />
|
||||
<span>Open <strong>CAD</strong> Studio</span>
|
||||
</a>
|
||||
<nav aria-label="Primary navigation">
|
||||
<a href="#capabilities">Capabilities</a>
|
||||
<a href="#desktop">Desktop</a>
|
||||
<a href="#formats">Formats</a>
|
||||
<a href="https://github.com/HakanSeven12/OpenCADStudio">GitHub</a>
|
||||
</nav>
|
||||
<a class="button button-small" href="/app/">Launch web app <span aria-hidden="true">↗</span></a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main">
|
||||
<section class="hero shell">
|
||||
<div class="hero-copy">
|
||||
<p class="eyebrow"><span></span> Open-source CAD · Desktop + Web</p>
|
||||
<h1>Native DWG & DXF.<br /><em>One open CAD studio.</em></h1>
|
||||
<p class="hero-lead">
|
||||
Create technical drawings, layouts and 3D models with a shared Rust core.
|
||||
Start in your browser or install the desktop app for your full production workflow.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="button" href="/app/">Launch web app <span aria-hidden="true">↗</span></a>
|
||||
<a class="button button-quiet" href="https://github.com/HakanSeven12/OpenCADStudio/releases/latest">
|
||||
Download desktop
|
||||
</a>
|
||||
</div>
|
||||
<ul class="hero-notes" aria-label="Project highlights">
|
||||
<li>GPL-3.0 licensed</li>
|
||||
<li>Windows · Linux · macOS</li>
|
||||
<li>20 interface languages</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="hero-visual" aria-label="Open CAD Studio workspace preview">
|
||||
<div class="visual-toolbar" aria-hidden="true">
|
||||
<span class="window-mark"><i></i><i></i><i></i></span>
|
||||
<span>technical-plan.dwg</span>
|
||||
<b>2D</b>
|
||||
</div>
|
||||
<img
|
||||
src="/assets/workspace.png"
|
||||
width="1920"
|
||||
height="940"
|
||||
alt="A detailed DWG technical plan open in the Open CAD Studio workspace"
|
||||
fetchpriority="high"
|
||||
/>
|
||||
<div class="visual-badge"><strong>DWG</strong><span>Native editing</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="signal-strip" aria-label="Key capabilities">
|
||||
<div class="shell signal-grid">
|
||||
<div><strong>DWG + DXF</strong><span>Read and write</span></div>
|
||||
<div><strong>2D + 3D</strong><span>One workspace</span></div>
|
||||
<div><strong>GPU</strong><span>Accelerated viewport</span></div>
|
||||
<div><strong>Rust</strong><span>Shared editing core</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section shell" id="capabilities">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow"><span></span> Built for real drawing work</p>
|
||||
<h2>From the first line to the final layout.</h2>
|
||||
<p>One focused environment for drafting, documentation and solid modeling.</p>
|
||||
</div>
|
||||
|
||||
<div class="capability-grid">
|
||||
<article class="capability-card capability-wide">
|
||||
<div class="card-index">01</div>
|
||||
<div>
|
||||
<p class="card-kicker">Precise drafting</p>
|
||||
<h3>Draw with intent.</h3>
|
||||
<p>
|
||||
Lines, polylines, curves, splines, hatches, object snaps, tracking,
|
||||
layers, blocks and external references stay close at hand.
|
||||
</p>
|
||||
</div>
|
||||
<div class="drafting-mark" aria-hidden="true">
|
||||
<span class="axis axis-x"></span><span class="axis axis-y"></span>
|
||||
<span class="arc"></span><i></i><b></b>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="capability-card">
|
||||
<div class="card-index">02</div>
|
||||
<p class="card-kicker">Documentation</p>
|
||||
<h3>Layouts that communicate.</h3>
|
||||
<p>
|
||||
Text, dimensions, leaders, tolerances, tables, paper space, viewports
|
||||
and plot styles turn geometry into deliverables.
|
||||
</p>
|
||||
</article>
|
||||
|
||||
<article class="capability-card card-accent">
|
||||
<div class="card-index">03</div>
|
||||
<p class="card-kicker">Open workflow</p>
|
||||
<h3>Your drawings stay yours.</h3>
|
||||
<p>
|
||||
Work with native files, inspect the source and choose the browser or
|
||||
desktop workflow that fits the job.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section model-section">
|
||||
<div class="shell model-grid">
|
||||
<div class="model-copy">
|
||||
<p class="eyebrow"><span></span> Kernel-backed 3D</p>
|
||||
<h2>Solid modeling, not a visual shortcut.</h2>
|
||||
<p>
|
||||
Build primitives and shape them with extrusion, revolution, sweep,
|
||||
loft and Boolean operations. The geometry kernel resolves the model;
|
||||
the GPU viewport keeps it responsive.
|
||||
</p>
|
||||
<ul class="check-list">
|
||||
<li>Orthographic and perspective cameras</li>
|
||||
<li>ACIS entity tessellation</li>
|
||||
<li>OBJ import and mesh export workflows</li>
|
||||
</ul>
|
||||
</div>
|
||||
<figure class="model-frame">
|
||||
<img
|
||||
src="/assets/modeling.png"
|
||||
width="1920"
|
||||
height="940"
|
||||
alt="A 3D mechanical model shown in two Open CAD Studio viewports"
|
||||
loading="lazy"
|
||||
/>
|
||||
<figcaption><span>Model space</span><strong>Kernel → Tessellation → GPU</strong></figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section shell" id="desktop">
|
||||
<div class="section-heading compact">
|
||||
<p class="eyebrow"><span></span> One core, two ways to work</p>
|
||||
<h2>Start instantly. Scale up when you need to.</h2>
|
||||
</div>
|
||||
|
||||
<div class="edition-grid">
|
||||
<article class="edition-card edition-primary">
|
||||
<div class="edition-label">Web</div>
|
||||
<h3>Open a drawing in your browser.</h3>
|
||||
<p>
|
||||
No installation. Select a drawing from your device, edit with the
|
||||
shared core and save the result as a local download.
|
||||
</p>
|
||||
<a class="text-link" href="/app/">Launch the web app <span aria-hidden="true">↗</span></a>
|
||||
</article>
|
||||
<article class="edition-card">
|
||||
<div class="edition-label">Desktop</div>
|
||||
<h3>Connect the full production workflow.</h3>
|
||||
<p>
|
||||
Add native file associations, thumbnails, system printing, PDF output,
|
||||
external plugins, scripts and headless automation.
|
||||
</p>
|
||||
<a class="text-link" href="https://github.com/HakanSeven12/OpenCADStudio/releases/latest">
|
||||
Download the latest release <span aria-hidden="true">↗</span>
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section formats-section" id="formats">
|
||||
<div class="shell formats-grid">
|
||||
<div>
|
||||
<p class="eyebrow"><span></span> File workflows</p>
|
||||
<h2>Meet drawings where they are.</h2>
|
||||
<p>
|
||||
Native drawing formats, recovery files, survey data, meshes, plots
|
||||
and property exports fit into a practical project workflow.
|
||||
</p>
|
||||
</div>
|
||||
<div class="format-list" aria-label="Supported file workflows">
|
||||
<div><strong>DWG</strong><span>Read · Write · R14–2018 targets</span></div>
|
||||
<div><strong>DXF</strong><span>Read · Write · R14–2018 targets</span></div>
|
||||
<div><strong>BAK / SV$</strong><span>Drawing recovery</span></div>
|
||||
<div><strong>OBJ / STL</strong><span>Mesh import and export</span></div>
|
||||
<div><strong>PDF</strong><span>Desktop plotting</span></div>
|
||||
<div><strong>CSV</strong><span>Property extraction</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section shell faq-section">
|
||||
<div class="section-heading compact">
|
||||
<p class="eyebrow"><span></span> Common questions</p>
|
||||
<h2>Before you open your first drawing.</h2>
|
||||
</div>
|
||||
<div class="faq-grid">
|
||||
<details>
|
||||
<summary>Is Open CAD Studio free?</summary>
|
||||
<p>Yes. The project is open source and distributed under the GPL-3.0 license.</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Can I use it without installing anything?</summary>
|
||||
<p>Yes. The web app runs in a modern browser and saves edited drawings as local downloads.</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Which desktop platforms are supported?</summary>
|
||||
<p>Release packages are available for Windows, Linux and Apple Silicon macOS.</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Is the project ready for critical production files?</summary>
|
||||
<p>The project is under active development. Keep backups and report reproducible problems on GitHub.</p>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section shell community-cta">
|
||||
<div>
|
||||
<p class="eyebrow"><span></span> Built in the open</p>
|
||||
<h2>Use it. Inspect it. Help shape it.</h2>
|
||||
<p>
|
||||
Follow development, report reproducible issues, contribute code or
|
||||
support the work that keeps an independent CAD project moving.
|
||||
</p>
|
||||
</div>
|
||||
<div class="cta-links">
|
||||
<a class="button" href="https://github.com/HakanSeven12/OpenCADStudio">View on GitHub</a>
|
||||
<a class="button button-quiet" href="https://github.com/sponsors/HakanSeven12">Sponsor development</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="shell footer-grid">
|
||||
<div class="brand footer-brand">
|
||||
<img src="/assets/logo.svg" width="34" height="34" alt="" />
|
||||
<span>Open <strong>CAD</strong> Studio</span>
|
||||
</div>
|
||||
<p>Open-source technical drawing and solid modeling.</p>
|
||||
<nav aria-label="Footer navigation">
|
||||
<a href="/app/">Web app</a>
|
||||
<a href="https://github.com/HakanSeven12/OpenCADStudio/releases/latest">Downloads</a>
|
||||
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions">Discussions</a>
|
||||
<a href="https://github.com/HakanSeven12/OpenCADStudio/blob/main/LICENSE">GPL-3.0</a>
|
||||
</nav>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = رسم
|
||||
.annotate = تعليقات توضيحية
|
||||
.insert = إدراج
|
||||
.model = نموذج
|
||||
.model = النمذجة
|
||||
.layout = تخطيط
|
||||
.manage = إدارة
|
||||
.view = عرض
|
||||
|
|
@ -326,6 +326,7 @@ common =
|
|||
.block-rotation = دوران الكتلة
|
||||
.blocks = الكتل
|
||||
.blue = أزرق
|
||||
.bulge = التحدب
|
||||
.boolean-failed-the-solids-may-not-overlap = فشلت العملية المنطقية — قد لا تتداخل المجسمات.
|
||||
.boolean-select-exactly-two-solids-created-this = العملية المنطقية: حدد مجسمين بالضبط أُنشئا في هذه الجلسة.
|
||||
.bottom = أسفل
|
||||
|
|
@ -1341,6 +1342,18 @@ draw =
|
|||
.ray-specify-through-point = RAY حدد نقطة العبور:
|
||||
.rect-specify-first-corner = RECT حدد الزاوية الأولى:
|
||||
.rect-specify-opposite-corner = RECT حدد الزاوية المقابلة:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT احسب الأبعاد استنادًا إلى [الطول / العرض] <الطول>:
|
||||
.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 حدد الزاوية المجاورة (تحدد اتجاه الحافة):
|
||||
|
|
@ -1350,6 +1363,31 @@ draw =
|
|||
.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 النمط [عادي/خطّي] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD حدد دائرة أو قطعًا ناقصًا أو خطًا متعددًا أو منحنى مغلقًا: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD عكس اتجاه الأقواس [نعم/لا] <لا>:
|
||||
.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 = حدد دائرة أو قطعًا ناقصًا أو خطًا متعددًا أو منحنى مغلقًا.
|
||||
.calligraphy = خطّي
|
||||
.first-point = النقطة الأولى
|
||||
.freehand = رسم حر
|
||||
.rectangular = مستطيل
|
||||
.rectangle-center = مستطيل - مركز
|
||||
.rectangle-rotated = مستطيل - مدوّر
|
||||
.rectangle-two-corners = مستطيل - زاويتان
|
||||
|
|
@ -1360,6 +1398,19 @@ draw =
|
|||
.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 نوع الكائن [خط/متعدد الخطوط/Spline]:
|
||||
.sketch-record-increment = SKETCH زيادة التسجيل <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH تفاوت ملاءمة منحنى Spline <__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:
|
||||
|
|
@ -1391,11 +1442,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = الاستخدام: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = الاستخدام: POLYSOLID <width> <height> (حدد خطًا متعددًا أولًا)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = الاستخدام: PYRAMID <radius> <height> [sides] (الافتراضي 4 أضلاع)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT حدد النقطة الأولى أو [خط متعدد]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT حدد النقطة الأولى أو [الإطارات/خط متعدد] <خط متعدد>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT حدد النقطة التالية أو [تراجع/إغلاق] (__ocs_arg_n__ نقاط):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT خط متعدد حدد خطًا متعددًا مستويًا مغلقًا:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT مستطيل حدد الزاوية الأولى:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT مستطيل حدد الزاوية المقابلة:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT الإطارات أدخل وضع الإطار [إيقاف/تشغيل/عرضبدونطباعة] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT خط متعدد حذف الخط المتعدد المصدر؟ [نعم/لا] <لا>:
|
||||
.wipeout-polyline-invalid = WIPEOUT خط متعدد: حدد خطًا متعددًا ثنائي الأبعاد مستقيمًا ومغلقًا ومستويًا بـ 3 رؤوس غير متقاطعة على الأقل.
|
||||
.setvar-frame-range = SETVAR: يتطلب __ocs_fmt_0__ القيمة 0 أو 1 أو 2.
|
||||
.frames = الإطارات
|
||||
.display-but-not-plot = عرض بدون طباعة
|
||||
.frames-on-not-plotted = الإطارات ظاهرة، غير مطبوعة
|
||||
.image-adjust = ضبط الصورة
|
||||
.x-ray = أشعة سينية
|
||||
.xline-specify-a-point = XLINE حدد نقطة:
|
||||
.xline-specify-through-point = XLINE حدد نقطة العبور:
|
||||
|
|
@ -1988,6 +2047,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = قُص __ocs_fmt_0__ كائن إلى الحافظة.
|
||||
|
||||
model =
|
||||
.create = إنشاء
|
||||
.boolean = عمليات منطقية
|
||||
.pyramid = هرم
|
||||
.polysolid = مجسّم متعدد
|
||||
.extrude = بثق
|
||||
.revolve = تدوير
|
||||
.loft = تشكيل انتقالي
|
||||
.sweep = سحب على مسار
|
||||
.presspull = دفع/سحب
|
||||
.n-specify-center-point = __ocs_arg_n__ حدد نقطة المركز:
|
||||
.n-specify-first-corner = __ocs_arg_n__ حدد الزاوية الأولى:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ حدد الارتفاع <Enter للافتراضي>:
|
||||
|
|
@ -2000,6 +2068,11 @@ model =
|
|||
.cv-hull = غلاف نقاط التحكم
|
||||
.class-entity = الفئة / الكيان
|
||||
.curved = منحني
|
||||
.cylindrical = أسطواني
|
||||
.inverted-curved = منحني معكوس
|
||||
.inverted-cylindrical = أسطواني معكوس
|
||||
.inverted-hemispherical = نصف كروي معكوس
|
||||
.inverted-spherical = كروي معكوس
|
||||
.data-size = حجم البيانات
|
||||
.database-curves = منحنيات قاعدة البيانات
|
||||
.design = التصميم
|
||||
|
|
@ -2283,6 +2356,7 @@ view =
|
|||
.zoom-extents =
|
||||
تكبير إلى
|
||||
النطاقات
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: ┘äϺ Ϭ┘êϼϻ ┘âϺϪ┘åϺϬ Ϭϩ┘ä┘è┘ä.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: لا توجد كائنات نص أو أبعاد.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: لم يُنفّذ بعد.
|
||||
.viewport-s-in-layout = __ocs_fmt_0__ منفذ عرض في التخطيط "__ocs_fmt_1__":
|
||||
|
|
@ -3022,7 +3096,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE القيمة الجديدة [0=نقطة 1=لا شيء 2=+ 3=x 4=شرطة؛ +32 دائرة +64 مربع]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = عُيّن PDMODE إلى __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE حجم النقطة الجديد (0 = 5% من منفذ العرض، <0 = مطلق):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE حجم النقطة الجديد (0 = 5% من منفذ العرض، >0 = مطلق، <0 = نسبة مئوية من منفذ العرض):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = عُيّن PDSIZE إلى __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Чертане
|
||||
.annotate = Анотиране
|
||||
.insert = Вмъкване
|
||||
.model = Модел
|
||||
.model = Моделиране
|
||||
.layout = Оформление
|
||||
.manage = Управление
|
||||
.view = Изглед
|
||||
|
|
@ -313,6 +313,7 @@ common =
|
|||
.block-rotation = Завъртане на блок
|
||||
.blocks = Блокове
|
||||
.blue = Синьо
|
||||
.bulge = Изпъкналост
|
||||
.boolean-failed-the-solids-may-not-overlap = Булева операция неуспешна — телата може да не се припокриват.
|
||||
.boolean-select-exactly-two-solids-created-this = Булева операция: избери точно две тела, създадени в тази сесия.
|
||||
.bottom = Долу
|
||||
|
|
@ -1328,6 +1329,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Посочи точка на преминаване:
|
||||
.rect-specify-first-corner = RECT Посочи първи ъгъл:
|
||||
.rect-specify-opposite-corner = RECT Посочи противоположен ъгъл:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Изчисли размерите по [Дължина / Ширина] <Дължина>:
|
||||
.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 Посочи съседен ъгъл (определя посоката на страната):
|
||||
|
|
@ -1337,6 +1350,31 @@ draw =
|
|||
.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 Стил [Нормален/Калиграфски] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Изберете затворена окръжност, елипса, полилиния или сплайн: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Обръщане на посоката на дъгите [Да/Не] <Не>:
|
||||
.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 = Изберете затворена окръжност, елипса, полилиния или сплайн.
|
||||
.calligraphy = Калиграфия
|
||||
.first-point = Първа точка
|
||||
.freehand = Свободно чертане
|
||||
.rectangular = Правоъгълен
|
||||
.rectangle-center = Правоъгълник - Център
|
||||
.rectangle-rotated = Правоъгълник - Завъртян
|
||||
.rectangle-two-corners = Правоъгълник - Два ъгъла
|
||||
|
|
@ -1347,6 +1385,19 @@ draw =
|
|||
.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 Тип обект [Линия/Полилиния/Spline]:
|
||||
.sketch-record-increment = SKETCH Стъпка на запис <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Толеранс на напасване на Spline <__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:
|
||||
|
|
@ -1378,11 +1429,19 @@ draw =
|
|||
.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 <радиус> <височина> [страни] (4 страни по подразбиране)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Посочи първа точка или [Полилиния]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Посочи първа точка или [Рамки/Полилиния] <Полилиния>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Посочи следваща точка или [Отмени/Затвори] (__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 Рамки Въведи режим на рамката [Изкл./Вкл./ПоказвайБезПечат] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Полилиния Да се изтрие ли изходната полилиния? [Да/Не] <Не>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Полилиния: избери права, затворена, равнинна 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 = Рентген
|
||||
.xline-specify-a-point = XLINE Посочи точка:
|
||||
.xline-specify-through-point = XLINE Посочи точка на преминаване:
|
||||
|
|
@ -1975,6 +2034,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ обект(а) изрязани в клипборда.
|
||||
|
||||
model =
|
||||
.create = Създаване
|
||||
.boolean = Булеви операции
|
||||
.pyramid = Пирамида
|
||||
.polysolid = Полисолид
|
||||
.extrude = Екструдиране
|
||||
.revolve = Завъртане
|
||||
.loft = Лофт
|
||||
.sweep = Изтегляне по траектория
|
||||
.presspull = Натискане/издърпване
|
||||
.n-specify-center-point = __ocs_arg_n__ Посочи централна точка:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Посочи първи ъгъл:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Посочи височина <Enter за подразбиране>:
|
||||
|
|
@ -1987,6 +2055,11 @@ model =
|
|||
.cv-hull = CV обвивка
|
||||
.class-entity = Клас / Обект
|
||||
.curved = Извит
|
||||
.cylindrical = Цилиндричен
|
||||
.inverted-curved = Обърнат извит
|
||||
.inverted-cylindrical = Обърнат цилиндричен
|
||||
.inverted-hemispherical = Обърнат полусферичен
|
||||
.inverted-spherical = Обърнат сферичен
|
||||
.data-size = Размер на данни
|
||||
.database-curves = Криви в базата данни
|
||||
.design = Дизайн
|
||||
|
|
@ -2270,6 +2343,7 @@ view =
|
|||
.zoom-extents =
|
||||
Мащаб
|
||||
Обхват
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: ð¢ÐÅð╝ð░ ÐëÐÇð©Ðàð¥ð▓ÐèÐçð¢ð© ð¥ð▒ðÁð║Ðéð©.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: няма текстови обекти или оразмерявания.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: все още не е реализирано.
|
||||
.viewport-s-in-layout = __ocs_fmt_0__ изглед(а) в оформление "__ocs_fmt_1__":
|
||||
|
|
@ -3010,7 +3084,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE нова стойност [0=точка 1=нищо 2=+ 3=x 4=чертичка; +32 окръжност +64 квадрат]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE зададен на __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE нов размер на точка (0 = 5% от изгледа, <0 = абсолютно):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE нов размер на точка (0 = 5% от изгледа, >0 = абсолютен, <0 = процент от изгледа):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE зададен на __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Kreslení
|
||||
.annotate = Poznámky
|
||||
.insert = Vložit
|
||||
.model = Model
|
||||
.model = Modelování
|
||||
.layout = Rozvržení
|
||||
.manage = Správa
|
||||
.view = Zobrazení
|
||||
|
|
@ -313,6 +313,7 @@ common =
|
|||
.block-rotation = Rotace bloku
|
||||
.blocks = Bloky
|
||||
.blue = Modrá
|
||||
.bulge = Vyklenutí
|
||||
.boolean-failed-the-solids-may-not-overlap = Logická hodnota selhala – tělesa se nemusí překrývat.
|
||||
.boolean-select-exactly-two-solids-created-this = Boolean: vyberte přesně dvě tělesa vytvořená v této relaci.
|
||||
.bottom = Spodní
|
||||
|
|
@ -1328,6 +1329,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Určete průchozí bod:
|
||||
.rect-specify-first-corner = RECT Určete první roh:
|
||||
.rect-specify-opposite-corner = RECT Určete opačný roh:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Vypočítat rozměry podle [Délky / Šířky] <Délka>:
|
||||
.rect-specify-elevation = RECT Určete výšku <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Určete poloměr zaoblení <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT Určete první vzdálenost zkosení <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Určete orientaci od prvního rohu:
|
||||
.rect-specify-rectangle-area = RECT Určete plochu obdélníku:
|
||||
.rect-specify-rectangle-length = RECT Určete délku obdélníku:
|
||||
.rect-specify-rectangle-width = RECT Určete šířku obdélníku:
|
||||
.rect-specify-rotation-angle = RECT Určete úhel otočení <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT Určete druhou vzdálenost zkosení <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Určete tloušťku <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Určete šířku <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Zadejte střed:
|
||||
.rect-cen-specify-corner-point =RECT CEN Zadejte rohový bod:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Určete sousední roh (definuje směr hrany):
|
||||
|
|
@ -1337,6 +1350,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: vyberte uzavřené křivky nebo kružnice.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Zadejte další bod (__ocs_arg_count__ bodů, Enter pro uzavření):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Zadejte počáteční bod (délka oblouku = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD První roh (__ocs_arg_style__, délka oblouku __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Protilehlý roh: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Počáteční bod mnohoúhelníku (__ocs_arg_style__, délka oblouku __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Další bod mnohoúhelníku (__ocs_arg_count__ bodů, Enter pro uzavření): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD První bod od ruky (__ocs_arg_style__, délka oblouku __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Veďte kurzor, poté klikněte nebo stiskněte Enter pro uzavření: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Přibližná délka tětivy oblouku <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Styl [Normální/Kaligrafie] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Vyberte uzavřenou kružnici, elipsu, křivku nebo spline: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Obrátit směr oblouků [Ano/Ne] <Ne>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Vyberte uzavřenou křivku revizního oblaku poblíž začátku náhrady: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Bod náhrady __ocs_arg_count__; ukončete na jiném vrcholu oblaku:
|
||||
.revcloud-pick-side = REVCLOUD Vyberte stranu původního oblaku, která se má vymazat:
|
||||
.revcloud-path-cannot-form = Vybraná cesta nemůže vytvořit revizní oblak.
|
||||
.revcloud-select-closed-revision-cloud = Vyberte uzavřenou křivku revizního oblaku.
|
||||
.revcloud-polyline-not-planar = Vybraná křivka není rovinná.
|
||||
.revcloud-corners-non-zero-area = Dva rohy musí vymezovat nenulovou plochu.
|
||||
.revcloud-arc-length-positive = Délka oblouku musí být větší než nula.
|
||||
.revcloud-enter-valid-arc-length = Zadejte platnou délku oblouku.
|
||||
.revcloud-select-supported-closed-curve = Vyberte podporovanou uzavřenou křivku.
|
||||
.revcloud-select-closed-entity = Vyberte uzavřenou kružnici, elipsu, křivku nebo spline.
|
||||
.calligraphy = Kaligrafie
|
||||
.first-point = První bod
|
||||
.freehand = Od ruky
|
||||
.rectangular = Obdélníkový
|
||||
.rectangle-center = Obdélník - střed
|
||||
.rectangle-rotated = Obdélník - otočený
|
||||
.rectangle-two-corners = Obdélník - dva rohy
|
||||
|
|
@ -1347,6 +1385,19 @@ draw =
|
|||
.section-offset-along-z = Odsazení SECTION podél Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Perem dolů – pohybem na skicu, kliknutím zvedněte, Enter zaznamenáte:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pero nahoru – kliknutím spustíte pero, Enter zaznamenáte:
|
||||
.sketch-pen = Pero
|
||||
.sketch-increment = Přírůstek
|
||||
.sketch-record = Zaznamenat
|
||||
.sketch-stop-erasing = Zastavit mazání
|
||||
.sketch-connect = Připojit
|
||||
.sketch-exit = Ukončit
|
||||
.sketch-quit = Zahodit
|
||||
.sketch-object-type = SKETCH Typ objektu [Úsečka/Polylinie/Spline]:
|
||||
.sketch-record-increment = SKETCH Krok záznamu <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolerance proložení spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Mazání — pohybujte se zpět po dočasné skice:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, krok __ocs_arg_increment__) Pero dole — kreslete pohybem:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, krok __ocs_arg_increment__) Pero nahoře — kliknutím jej spusťte:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE osa řezné roviny [X / Y / Z] (přidejte TOP/BOTTOM zadáním):
|
||||
.slice-offset-along-x = Odsazení SLICE podél X:
|
||||
.slice-offset-along-y = Odsazení SLICE podél Y:
|
||||
|
|
@ -1378,11 +1429,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Použití: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Použití: POLYSOLID <width> <height> (nejprve vyberte křivku)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Použití: PYRAMID <radius> <height> [sides] (výchozí 4 strany)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Zadejte první bod nebo [Polyline]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Zadejte první bod nebo [Rámečky/Křivka] <Křivka>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Zadejte další bod nebo [Undo/Close] (body __ocs_arg_n__):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polyline Vyberte uzavřenou rovinnou křivku:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Obdélníkový Zadejte první roh:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Obdélníkový Zadejte protilehlý roh:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Rámečky Zadejte režim rámečku [Vypnuto/Zapnuto/ZobrazitNetisknout] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Křivka Vymazat zdrojovou křivku? [Ano/Ne] <Ne>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Křivka: vyberte přímou, uzavřenou, rovinnou 2D křivku s alespoň 3 neprotínajícími se vrcholy.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ vyžaduje 0, 1 nebo 2.
|
||||
.frames = Rámečky
|
||||
.display-but-not-plot = Zobrazit, ale netisknout
|
||||
.frames-on-not-plotted = Rámečky zapnuté, netisknuté
|
||||
.image-adjust = Úprava obrazu
|
||||
.x-ray = X-Ray
|
||||
.xline-specify-a-point = XLINE Určete bod:
|
||||
.xline-specify-through-point = XLINE Určete průchozí bod:
|
||||
|
|
@ -1975,6 +2034,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = Objekt(y) __ocs_fmt_0__ vyříznutý do schránky.
|
||||
|
||||
model =
|
||||
.create = Vytvořit
|
||||
.boolean = Booleovské operace
|
||||
.pyramid = Jehlan
|
||||
.polysolid = Polysolid
|
||||
.extrude = Vysunutí
|
||||
.revolve = Rotace
|
||||
.loft = Přechod
|
||||
.sweep = Tažení
|
||||
.presspull = Tlačit/táhnout
|
||||
.n-specify-center-point = __ocs_arg_n__ Zadejte středový bod:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Určete první roh:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Zadejte výšku <Enter for default>:
|
||||
|
|
@ -1987,6 +2055,11 @@ model =
|
|||
.cv-hull = CV Trup
|
||||
.class-entity =Třída / Entita
|
||||
.curved = Zakřivené
|
||||
.cylindrical = Válcové
|
||||
.inverted-curved = Obrácené zakřivené
|
||||
.inverted-cylindrical = Obrácené válcové
|
||||
.inverted-hemispherical = Obrácené polokulovité
|
||||
.inverted-spherical = Obrácené sférické
|
||||
.data-size = Velikost dat
|
||||
.database-curves = Databázové křivky
|
||||
.design = Design
|
||||
|
|
@ -2270,6 +2343,7 @@ view =
|
|||
.zoom-extents =
|
||||
Přiblížení
|
||||
Rozsahy
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: ┼¥├ídn├® ┼írafovac├¡ objekty.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: žádný text ani kótovací objekty.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: dosud neimplementováno.
|
||||
.viewport-s-in-layout =Výřezy __ocs_fmt_0__ v rozložení "__ocs_fmt_1__":
|
||||
|
|
@ -3009,7 +3083,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nová hodnota [0=dot 1=none 2=+ 3=x 4=tick; +32 kruh +64 čtverec]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE nastaveno na __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = Nová velikost bodu PDSIZE (0 = 5 % výřezu, <0 = absolutní):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = Nová velikost bodu PDSIZE (0 = 5 % výřezu, >0 = absolutní, <0 = procento výřezu):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE nastaveno na __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -1329,6 +1329,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Geben Sie durch Punkt an:
|
||||
.rect-specify-first-corner = RECT Geben Sie die erste Ecke an:
|
||||
.rect-specify-opposite-corner = RECT Geben Sie die gegenüberliegende Ecke an:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Abmessungen anhand von [Länge / Breite] berechnen <Länge>:
|
||||
.rect-specify-elevation = RECT Erhebung angeben <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Abrundungsradius angeben <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT Ersten Fasenabstand angeben <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Ausrichtung von der ersten Ecke angeben:
|
||||
.rect-specify-rectangle-area = RECT Rechteckfläche angeben:
|
||||
.rect-specify-rectangle-length = RECT Rechtecklänge angeben:
|
||||
.rect-specify-rectangle-width = RECT Rechteckbreite angeben:
|
||||
.rect-specify-rotation-angle = RECT Drehwinkel angeben <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT Zweiten Fasenabstand angeben <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Stärke angeben <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Breite angeben <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Mittelpunkt angeben:
|
||||
.rect-cen-specify-corner-point = RECT CEN Eckpunkt angeben:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Angrenzende Ecke angeben (Kantenrichtung definieren):
|
||||
|
|
@ -1338,6 +1350,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: Wählen Sie geschlossene Polylinien oder Kreise.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Nächsten Punkt angeben (__ocs_arg_count__ Punkte, Eingabe zum Schließen):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Startpunkt angeben (Bogenlänge = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD Erste Ecke (__ocs_arg_style__, Bogenlänge __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Gegenüberliegende Ecke: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Polygonaler Startpunkt (__ocs_arg_style__, Bogenlänge __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Nächster Polygonpunkt (__ocs_arg_count__ Punkte, Enter zum Schließen): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Erster Freihandpunkt (__ocs_arg_style__, Bogenlänge __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Cursor führen, dann klicken oder Enter zum Schließen drücken: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Ungefähre Bogensehnenlänge <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Stil [Normal/Kalligrafie] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Geschlossenen Kreis, Ellipse, Polylinie oder Spline auswählen: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Bogenrichtung umkehren [Ja/Nein] <Nein>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Geschlossene Revisionswolken-Polylinie nahe dem Ersetzungsanfang auswählen: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Ersetzungspunkt __ocs_arg_count__; an einem anderen Wolkenscheitel beenden:
|
||||
.revcloud-pick-side = REVCLOUD Zu löschende Seite der ursprünglichen Wolke wählen:
|
||||
.revcloud-path-cannot-form = Der ausgewählte Pfad kann keine Revisionswolke bilden.
|
||||
.revcloud-select-closed-revision-cloud = Eine geschlossene Revisionswolken-Polylinie auswählen.
|
||||
.revcloud-polyline-not-planar = Die ausgewählte Polylinie ist nicht planar.
|
||||
.revcloud-corners-non-zero-area = Die beiden Ecken müssen eine Fläche ungleich null definieren.
|
||||
.revcloud-arc-length-positive = Die Bogenlänge muss größer als null sein.
|
||||
.revcloud-enter-valid-arc-length = Eine gültige Bogenlänge eingeben.
|
||||
.revcloud-select-supported-closed-curve = Eine unterstützte geschlossene Kurve auswählen.
|
||||
.revcloud-select-closed-entity = Einen geschlossenen Kreis, eine Ellipse, Polylinie oder einen Spline auswählen.
|
||||
.calligraphy = Kalligrafie
|
||||
.first-point = Erster Punkt
|
||||
.freehand = Freihand
|
||||
.rectangular = Rechteckig
|
||||
.rectangle-center = Rechteck - Mitte
|
||||
.rectangle-rotated = Rechteck - rotiert
|
||||
.rectangle-two-corners = Rechteck - Zwei Ecken
|
||||
|
|
@ -1348,6 +1385,19 @@ draw =
|
|||
.section-offset-along-z = SECTION Versetzt entlang Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Pen down — zum Skizzieren bewegen, zum Liften klicken, zum Aufzeichnen eingeben:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pen up — klicken Sie auf den Stift zu senken, geben Sie ein, um aufzuzeichnen:
|
||||
.sketch-pen = Stift
|
||||
.sketch-increment = Schrittweite
|
||||
.sketch-record = Aufzeichnen
|
||||
.sketch-stop-erasing = Radieren beenden
|
||||
.sketch-connect = Verbinden
|
||||
.sketch-exit = Beenden
|
||||
.sketch-quit = Verwerfen
|
||||
.sketch-object-type = SKETCH Objekttyp [Linie/Polylinie/Spline]:
|
||||
.sketch-record-increment = SKETCH Aufzeichnungsschritt <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline-Anpassungstoleranz <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Radieren — rückwärts über die temporäre Skizze bewegen:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, Schrittweite __ocs_arg_increment__) Stift unten — zum Skizzieren bewegen:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, Schrittweite __ocs_arg_increment__) Stift oben — zum Absenken klicken:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE Schnittebenenachse [X / Y / Z] (fügen Sie TOP/BOTTOM durch Eingabe hinzu):
|
||||
.slice-offset-along-x = SLICE versetzt entlang X:
|
||||
.slice-offset-along-y = SLICE längs Y versetzt:
|
||||
|
|
@ -1379,11 +1429,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Verwendung: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Verwendung: POLYSOLID <width> <height> (select a polyline first)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Verwendung: PYRAMID <radius> <height> [sides] (default 4 sides)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Geben Sie den ersten Punkt oder [Polylinie] an:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Geben Sie den ersten Punkt oder [Rahmen/Polylinie] an <Polylinie>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Nächsten Punkt angeben oder [Zurück/Schließen] (__ocs_arg_n__ Punkte):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polylinie Wählen Sie eine geschlossene planare Polylinie:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Rechteckig Geben Sie die erste Ecke an:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Rechteckig Angeben der gegenüberliegenden Ecke:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Rahmen Rahmenmodus eingeben [Aus/Ein/AnzeigenAberNichtPlotten] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Polylinie Quellpolylinie löschen? [Ja/Nein] <Nein>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Polylinie: Wählen Sie eine gerade, geschlossene, planare 2D-Polylinie mit mindestens 3 sich nicht schneidenden Scheitelpunkten.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ erfordert 0, 1 oder 2.
|
||||
.frames = Rahmen
|
||||
.display-but-not-plot = Anzeigen, aber nicht plotten
|
||||
.frames-on-not-plotted = Rahmen ein, nicht geplottet
|
||||
.image-adjust = Bildanpassung
|
||||
.x-ray = Röntgenstrahlen
|
||||
.xline-specify-a-point = XLINE Geben Sie einen Punkt an:
|
||||
.xline-specify-through-point = XLINE Geben Sie durch Punkt an:
|
||||
|
|
@ -1974,6 +2032,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ Objekt(e) in die Zwischenablage geschnitten.
|
||||
|
||||
model =
|
||||
.create = Erstellen
|
||||
.boolean = Boolesche Operationen
|
||||
.pyramid = Pyramide
|
||||
.polysolid = Polykörper
|
||||
.extrude = Extrudieren
|
||||
.revolve = Drehen
|
||||
.loft = Loft
|
||||
.sweep = Austragen
|
||||
.presspull = Drücken/Ziehen
|
||||
.n-specify-center-point = __ocs_arg_n__ Mittelpunkt angeben:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Erste Ecke angeben:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Höhe angeben <Eingabe für Standardwert>:
|
||||
|
|
@ -1986,6 +2053,11 @@ model =
|
|||
.cv-hull = Kontrollpolygon
|
||||
.class-entity = Klasse / Objekt
|
||||
.curved = gebogen
|
||||
.cylindrical = Zylindrisch
|
||||
.inverted-curved = Invertiert gebogen
|
||||
.inverted-cylindrical = Invertiert zylindrisch
|
||||
.inverted-hemispherical = Invertiert hemisphärisch
|
||||
.inverted-spherical = Invertiert kugelförmig
|
||||
.data-size = Datengröße
|
||||
.database-curves = Datenbankkurven
|
||||
.design = Design
|
||||
|
|
@ -2269,6 +2341,7 @@ view =
|
|||
.zoom-extents =
|
||||
Zoom
|
||||
Umfang
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: keine Schraffurobjekte.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: keine Text- oder Dimensionsobjekte.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: noch nicht umgesetzt.
|
||||
.viewport-s-in-layout = __ocs_fmt_0__ Ansichtsfenster(s) im Layout "__ocs_fmt_1__":
|
||||
|
|
@ -3008,7 +3081,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE Neuer Wert [0=Punkt, 1=Kein Symbol, 2=+, 3=×, 4=Strich; +32=Kreis, +64=Quadrat]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE auf __ocs_fmt_0__ gesetzt
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Neue Punktgröße (0 = 5 % des Ansichtsfensters, < 0 = absolut):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Neue Punktgröße (0 = 5 % des Ansichtsfensters, > 0 = absolut, < 0 = Ansichtsfenster-Prozentwert):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE auf __ocs_fmt_0__ gesetzt
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Draw
|
||||
.annotate = Annotate
|
||||
.insert = Insert
|
||||
.model = Model
|
||||
.model = Modelling
|
||||
.layout = Layout
|
||||
.manage = Manage
|
||||
.view = View
|
||||
|
|
@ -314,6 +314,7 @@ common =
|
|||
.block-rotation = Block rotation
|
||||
.blocks = Blocks
|
||||
.blue = Blue
|
||||
.bulge = Bulge
|
||||
.boolean-failed-the-solids-may-not-overlap = Boolean failed — the solids may not overlap.
|
||||
.boolean-select-exactly-two-solids-created-this = Boolean: select exactly two solids created this session.
|
||||
.bottom = Bottom
|
||||
|
|
@ -1169,6 +1170,15 @@ draw =
|
|||
.tag-val = [__ocs_fmt_0__] = __ocs_fmt_1__
|
||||
.no-closed-boundary-found = ⚠ No closed boundary found.
|
||||
.inverted = , inverted
|
||||
.loaded-multiline-styles = Loaded multiline styles: __ocs_fmt_0__
|
||||
.mline-current-settings = MLINE Current settings: Justification = __ocs_fmt_0__, Scale = __ocs_fmt_1__, Style = __ocs_fmt_2__
|
||||
Specify start point or [Justification/Scale/STyle]:
|
||||
.mline-enter-justification = MLINE Enter justification type [Top/Zero/Bottom] <__ocs_fmt_0__>:
|
||||
.mline-enter-scale-factor-default = MLINE Enter scale factor <__ocs_fmt_0__>:
|
||||
.mline-enter-style-name = MLINE Enter style name or [?] <__ocs_fmt_0__>:
|
||||
.mline-specify-next-point-close-undo = MLINE Specify next point or [Close/Undo]:
|
||||
.mline-specify-next-point-undo = MLINE Specify next point or [Undo]:
|
||||
.multiline-style-was-not-found = Multiline style "__ocs_arg_style__" was not found.
|
||||
.value-2-point = 2-Point
|
||||
.value-2d-solid = 2D Solid
|
||||
.value-3-point = 3-Point
|
||||
|
|
@ -1329,6 +1339,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Specify through point:
|
||||
.rect-specify-first-corner = RECT Specify first corner:
|
||||
.rect-specify-opposite-corner = RECT Specify opposite corner:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Calculate dimensions based on [Length / Width] <Length>:
|
||||
.rect-specify-elevation = RECT Specify elevation <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Specify fillet radius <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT Specify first chamfer distance <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Specify orientation from the first corner:
|
||||
.rect-specify-rectangle-area = RECT Specify rectangle area:
|
||||
.rect-specify-rectangle-length = RECT Specify rectangle length:
|
||||
.rect-specify-rectangle-width = RECT Specify rectangle width:
|
||||
.rect-specify-rotation-angle = RECT Specify rotation angle <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT Specify second chamfer distance <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Specify thickness <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Specify width <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Specify center point:
|
||||
.rect-cen-specify-corner-point = RECT CEN Specify corner point:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Specify adjacent corner (defines edge direction):
|
||||
|
|
@ -1338,6 +1360,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: select closed polylines or circles.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Specify next point (__ocs_arg_count__ pts, Enter to close):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Specify start point (arc length = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD First corner (__ocs_arg_style__, arc length __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Opposite corner: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Polygonal start point (__ocs_arg_style__, arc length __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Next polygonal point (__ocs_arg_count__ points, Enter to close): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Freehand first point (__ocs_arg_style__, arc length __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Guide the cursor, then click or press Enter to close: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Approximate arc chord length <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Style [Normal/Calligraphy] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Select a closed circle, ellipse, polyline, or spline: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Reverse arc direction [Yes/No] <No>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Select a closed revision-cloud polyline near the replacement start: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Replacement point __ocs_arg_count__; finish on another cloud vertex:
|
||||
.revcloud-pick-side = REVCLOUD Pick the side of the original cloud to erase:
|
||||
.revcloud-path-cannot-form = The selected path cannot form a revision cloud.
|
||||
.revcloud-select-closed-revision-cloud = Select a closed revision-cloud polyline.
|
||||
.revcloud-polyline-not-planar = The selected polyline is not planar.
|
||||
.revcloud-corners-non-zero-area = The two corners must define a non-zero area.
|
||||
.revcloud-arc-length-positive = Arc length must be greater than zero.
|
||||
.revcloud-enter-valid-arc-length = Enter a valid arc length.
|
||||
.revcloud-select-supported-closed-curve = Select a supported closed curve.
|
||||
.revcloud-select-closed-entity = Select a closed circle, ellipse, polyline, or spline.
|
||||
.calligraphy = Calligraphy
|
||||
.first-point = First point
|
||||
.freehand = Freehand
|
||||
.rectangular = Rectangular
|
||||
.rectangle-center = Rectangle - Center
|
||||
.rectangle-rotated = Rectangle - Rotated
|
||||
.rectangle-two-corners = Rectangle - Two Corners
|
||||
|
|
@ -1348,6 +1395,19 @@ draw =
|
|||
.section-offset-along-z = SECTION offset along Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Pen down — move to sketch, click to lift, Enter to record:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pen up — click to lower the pen, Enter to record:
|
||||
.sketch-pen = Pen
|
||||
.sketch-increment = Increment
|
||||
.sketch-record = Record
|
||||
.sketch-stop-erasing = Stop erasing
|
||||
.sketch-connect = Connect
|
||||
.sketch-exit = Exit
|
||||
.sketch-quit = Quit
|
||||
.sketch-object-type = SKETCH Object type [Line/Polyline/Spline]:
|
||||
.sketch-record-increment = SKETCH Record increment <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline fit tolerance <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Erase — move backward over the temporary sketch:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, increment __ocs_arg_increment__) Pen down — move to sketch:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, increment __ocs_arg_increment__) Pen up — click to lower the pen:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE cutting-plane axis [X / Y / Z] (add TOP/BOTTOM by typing):
|
||||
.slice-offset-along-x = SLICE offset along X:
|
||||
.slice-offset-along-y = SLICE offset along Y:
|
||||
|
|
@ -1357,6 +1417,7 @@ draw =
|
|||
.solid-specify-second-point = SOLID Specify second point:
|
||||
.solid-specify-third-point = SOLID Specify third point:
|
||||
.spline-specify-first-control-point = SPLINE Specify first control point:
|
||||
.spline-specify-first-point = SPLINE Specify first point:
|
||||
.spline-specify-next-point-n-pts = SPLINE Specify next point [__ocs_arg_n__ pts]:
|
||||
.sketch = Sketch
|
||||
.start-center-angle = Start, Center, Angle
|
||||
|
|
@ -1379,11 +1440,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Usage: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Usage: POLYSOLID <width> <height> (select a polyline first)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Usage: PYRAMID <radius> <height> [sides] (default 4 sides)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Specify first point or [Polyline]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Specify first point or [Frames/Polyline] <Polyline>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Specify next point or [Undo/Close] (__ocs_arg_n__ points):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polyline Select a closed planar polyline:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Rectangular Specify first corner:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Rectangular Specify opposite corner:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Frames Enter frame mode [Off/On/DisplayButNotPlot] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Polyline Erase source polyline? [Yes/No] <No>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Polyline: select a straight, closed, planar 2D polyline with at least 3 non-intersecting vertices.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ requires 0, 1, or 2.
|
||||
.frames = Frames
|
||||
.display-but-not-plot = Display but not plot
|
||||
.frames-on-not-plotted = Frames On, Not Plotted
|
||||
.image-adjust = Image Adjust
|
||||
.x-ray = X-Ray
|
||||
.xline-specify-a-point = XLINE Specify a point:
|
||||
.xline-specify-through-point = XLINE Specify through point:
|
||||
|
|
@ -1392,6 +1461,24 @@ draw =
|
|||
modify =
|
||||
.count-objects = (__ocs_arg_count__ objects)
|
||||
.edge-extend-indented-title-case = [Edge: Extend]
|
||||
.add-vertex = Add Vertex
|
||||
.closed-cross = Closed Cross
|
||||
.closed-tee = Closed Tee
|
||||
.corner-joint = Corner Joint
|
||||
.cut-all = Cut All
|
||||
.cut-single = Cut Single
|
||||
.delete-vertex = Delete Vertex
|
||||
.merged-cross = Merged Cross
|
||||
.merged-tee = Merged Tee
|
||||
.mledit-choose-an-edit-tool = MLEDIT Choose an edit tool:
|
||||
.mledit-select-first-multiline = MLEDIT Select first multiline:
|
||||
.mledit-select-second-multiline = MLEDIT Select second multiline:
|
||||
.mledit-specify-end-of-weld-range = MLEDIT Specify the end of the weld range:
|
||||
.mledit-specify-second-cut-point = MLEDIT Specify the second cut point:
|
||||
.open-cross = Open Cross
|
||||
.open-tee = Open Tee
|
||||
.remove-vertex = Remove Vertex
|
||||
.weld-all = Weld All
|
||||
.align-scale-objects-based-on-alignment-points = ALIGN Scale objects based on alignment points? [Y/N]:
|
||||
.align-select-objects-count-selected-enter-when = ALIGN Select objects (__ocs_arg_count__ selected, Enter when done):
|
||||
.align-specify-1st-destination-point = ALIGN Specify 1st destination point:
|
||||
|
|
@ -1976,6 +2063,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ object(s) cut to clipboard.
|
||||
|
||||
model =
|
||||
.create = Create
|
||||
.boolean = Boolean
|
||||
.pyramid = Pyramid
|
||||
.polysolid = Polysolid
|
||||
.extrude = Extrude
|
||||
.revolve = Revolve
|
||||
.loft = Loft
|
||||
.sweep = Sweep
|
||||
.presspull = Presspull
|
||||
.n-specify-center-point = __ocs_arg_n__ Specify center point:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Specify first corner:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Specify height <Enter for default>:
|
||||
|
|
@ -1988,6 +2084,11 @@ model =
|
|||
.cv-hull = CV Hull
|
||||
.class-entity = Class / Entity
|
||||
.curved = Curved
|
||||
.cylindrical = Cylindrical
|
||||
.inverted-curved = Inverted curved
|
||||
.inverted-cylindrical = Inverted cylindrical
|
||||
.inverted-hemispherical = Inverted hemispherical
|
||||
.inverted-spherical = Inverted spherical
|
||||
.data-size = Data Size
|
||||
.database-curves = Database Curves
|
||||
.design = Design
|
||||
|
|
@ -2271,6 +2372,7 @@ view =
|
|||
.zoom-extents =
|
||||
Zoom
|
||||
Extents
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: no hatch objects.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: no text or dimension objects.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: not yet implemented.
|
||||
.viewport-s-in-layout = __ocs_fmt_0__ viewport(s) in layout "__ocs_fmt_1__":
|
||||
|
|
@ -2352,6 +2454,7 @@ properties =
|
|||
.chord-x = Chord X
|
||||
.chord-y = Chord Y
|
||||
.chord-z = Chord Z
|
||||
.chord = Chord
|
||||
.circumference = Circumference
|
||||
.class = Class
|
||||
.class-id = Class ID
|
||||
|
|
@ -2378,6 +2481,10 @@ properties =
|
|||
.control-point-x = Control Point X
|
||||
.control-point-y = Control Point Y
|
||||
.control-point-z = Control Point Z
|
||||
.control-point-x-sentence-case = Control point X
|
||||
.control-point-y-sentence-case = Control point Y
|
||||
.control-point-z-sentence-case = Control point Z
|
||||
.control-vertices = Control Vertices
|
||||
.coordination-model = Coordination Model
|
||||
.creased-edges = Creased Edges
|
||||
.croppings = Croppings
|
||||
|
|
@ -2385,6 +2492,9 @@ properties =
|
|||
.current-color-scheme = Current Color Scheme
|
||||
.current-vertex-title-case = Current Vertex
|
||||
.current-vertex-sentence-case = Current vertex
|
||||
.current-control-point = Current Control point
|
||||
.current-fit-point = Current Fit point
|
||||
.cv-frame = CV frame
|
||||
.cutting-line = Cutting Line
|
||||
.dwg-payload = DWG Payload
|
||||
.dwg-version = DWG Version
|
||||
|
|
@ -2444,6 +2554,9 @@ properties =
|
|||
.end-tangent-x = End Tangent X
|
||||
.end-tangent-y = End Tangent Y
|
||||
.end-tangent-z = End Tangent Z
|
||||
.end-tangent-vector-x = End tangent vector X
|
||||
.end-tangent-vector-y = End tangent vector Y
|
||||
.end-tangent-vector-z = End tangent vector Z
|
||||
.end-x = End X
|
||||
.end-y = End Y
|
||||
.end-z = End Z
|
||||
|
|
@ -2481,6 +2594,10 @@ properties =
|
|||
.fit-point-y = Fit Point Y
|
||||
.fit-point-z = Fit Point Z
|
||||
.fit-tolerance = Fit Tolerance
|
||||
.fit-point-x-sentence-case = Fit point X
|
||||
.fit-point-y-sentence-case = Fit point Y
|
||||
.fit-point-z-sentence-case = Fit point Z
|
||||
.fit-tolerance-sentence-case = Fit tolerance
|
||||
.fit-smooth-title-case = Fit/Smooth
|
||||
.fit-smooth-sentence-case = Fit/smooth
|
||||
.fixed-elevation-range = Fixed Elevation Range
|
||||
|
|
@ -2526,6 +2643,7 @@ properties =
|
|||
.jog-z = Jog Z
|
||||
.justify = Justify
|
||||
.knot-parameterization = Knot Parameterization
|
||||
.knot-parameterization-sentence-case = Knot parameterization
|
||||
.lamp-preset = Lamp Preset
|
||||
.landing-gap = Landing Gap
|
||||
.layout-print-configuration = Layout Print Configuration
|
||||
|
|
@ -2618,6 +2736,8 @@ properties =
|
|||
.number-of-grips = Number of Grips
|
||||
.number-of-vertices = Number of Vertices
|
||||
.number-of-turns = Number of turns
|
||||
.number-of-control-points = Number of control points
|
||||
.number-of-fit-points = Number of fit points
|
||||
.ole-frame = OLE Frame
|
||||
.object-references = Object References
|
||||
.obliquing = Obliquing
|
||||
|
|
@ -2737,6 +2857,13 @@ properties =
|
|||
.start-tangent-x = Start Tangent X
|
||||
.start-tangent-y = Start Tangent Y
|
||||
.start-tangent-z = Start Tangent Z
|
||||
.start-tangent-vector-x = Start tangent vector X
|
||||
.start-tangent-vector-y = Start tangent vector Y
|
||||
.start-tangent-vector-z = Start tangent vector Z
|
||||
.hide = Hide
|
||||
.periodic = Periodic
|
||||
.square-root = Square Root
|
||||
.uniform = Uniform
|
||||
.start-x = Start X
|
||||
.start-y = Start Y
|
||||
.start-z = Start Z
|
||||
|
|
@ -3010,7 +3137,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE new value [0=dot 1=none 2=+ 3=x 4=tick; +32 circle +64 square]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE set to __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE new point size (0 = 5% of viewport, <0 = absolute):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE new point size (0 = 5% of viewport, >0 = absolute, <0 = viewport percentage):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE set to __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Dibujo
|
||||
.annotate = Anotar
|
||||
.insert = Insertar
|
||||
.model = Modelo
|
||||
.model = Modelado
|
||||
.layout = Presentación
|
||||
.manage = Administrar
|
||||
.view = Vista
|
||||
|
|
@ -314,6 +314,7 @@ common =
|
|||
.block-rotation = Rotación del bloque
|
||||
.blocks = Bloques
|
||||
.blue = Azul
|
||||
.bulge = Curvatura
|
||||
.boolean-failed-the-solids-may-not-overlap = Falló la operación booleana; es posible que los sólidos no se solapen.
|
||||
.boolean-select-exactly-two-solids-created-this = Operación booleana: seleccione exactamente dos sólidos creados en esta sesión.
|
||||
.bottom = Inferior
|
||||
|
|
@ -1329,6 +1330,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Precise el punto de paso:
|
||||
.rect-specify-first-corner = RECT Precise la primera esquina:
|
||||
.rect-specify-opposite-corner = RECT Precise la esquina opuesta:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Calcule las dimensiones según [Longitud / Anchura] <Longitud>:
|
||||
.rect-specify-elevation = RECT Precise la elevación <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Precise el radio de empalme <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT Precise la primera distancia de chaflán <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Precise la orientación desde la primera esquina:
|
||||
.rect-specify-rectangle-area = RECT Precise el área del rectángulo:
|
||||
.rect-specify-rectangle-length = RECT Precise la longitud del rectángulo:
|
||||
.rect-specify-rectangle-width = RECT Precise la anchura del rectángulo:
|
||||
.rect-specify-rotation-angle = RECT Precise el ángulo de rotación <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT Precise la segunda distancia de chaflán <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Precise el espesor <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Precise la anchura <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Precise el punto central:
|
||||
.rect-cen-specify-corner-point = RECT CEN Precise el punto de esquina:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Precise la esquina adyacente (define la dirección de la arista):
|
||||
|
|
@ -1338,6 +1351,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: seleccione polilíneas cerradas o círculos.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Precise el siguiente punto (__ocs_arg_count__ puntos, Intro para cerrar):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Precise el punto inicial (longitud de arco = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD Primera esquina (__ocs_arg_style__, longitud de arco __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Esquina opuesta: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Punto inicial poligonal (__ocs_arg_style__, longitud de arco __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Siguiente punto poligonal (__ocs_arg_count__ puntos, Intro para cerrar): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Primer punto a mano alzada (__ocs_arg_style__, longitud de arco __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Guíe el cursor y luego haga clic o pulse Intro para cerrar: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Longitud aproximada de cuerda de arco <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Estilo [Normal/Caligrafía] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Seleccione un círculo, una elipse, una polilínea o una spline cerrados: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Invertir dirección de arco [Sí/No] <No>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Seleccione una polilínea de nube de revisión cerrada cerca del inicio del reemplazo: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Punto de reemplazo __ocs_arg_count__; termine en otro vértice de la nube:
|
||||
.revcloud-pick-side = REVCLOUD Elija el lado de la nube original que se borrará:
|
||||
.revcloud-path-cannot-form = La trayectoria seleccionada no puede formar una nube de revisión.
|
||||
.revcloud-select-closed-revision-cloud = Seleccione una polilínea de nube de revisión cerrada.
|
||||
.revcloud-polyline-not-planar = La polilínea seleccionada no es plana.
|
||||
.revcloud-corners-non-zero-area = Las dos esquinas deben definir un área distinta de cero.
|
||||
.revcloud-arc-length-positive = La longitud de arco debe ser mayor que cero.
|
||||
.revcloud-enter-valid-arc-length = Introduzca una longitud de arco válida.
|
||||
.revcloud-select-supported-closed-curve = Seleccione una curva cerrada compatible.
|
||||
.revcloud-select-closed-entity = Seleccione un círculo, una elipse, una polilínea o una spline cerrados.
|
||||
.calligraphy = Caligrafía
|
||||
.first-point = Primer punto
|
||||
.freehand = Mano alzada
|
||||
.rectangular = Rectangular
|
||||
.rectangle-center = Rectángulo - Centro
|
||||
.rectangle-rotated = Rectángulo - Girado
|
||||
.rectangle-two-corners = Rectángulo - Dos esquinas
|
||||
|
|
@ -1348,6 +1386,19 @@ draw =
|
|||
.section-offset-along-z = SECTION desfase a lo largo de Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Lápiz abajo: mueva para dibujar, pulse para levantarlo, Intro para registrar:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Lápiz arriba: pulse para bajarlo, Intro para registrar:
|
||||
.sketch-pen = Lápiz
|
||||
.sketch-increment = Incremento
|
||||
.sketch-record = Registrar
|
||||
.sketch-stop-erasing = Detener borrado
|
||||
.sketch-connect = Conectar
|
||||
.sketch-exit = Salir
|
||||
.sketch-quit = Descartar
|
||||
.sketch-object-type = SKETCH Tipo de objeto [Línea/Polilínea/Spline]:
|
||||
.sketch-record-increment = SKETCH Incremento de registro <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolerancia de ajuste de spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Borrar: retroceda sobre el boceto temporal:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Lápiz abajo: mueva para dibujar:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Lápiz arriba: pulse para bajarlo:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE eje del plano de corte [X / Y / Z] (escriba TOP/BOTTOM para añadirlo):
|
||||
.slice-offset-along-x = SLICE desfase a lo largo de X:
|
||||
.slice-offset-along-y = SLICE desfase a lo largo de Y:
|
||||
|
|
@ -1379,11 +1430,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Uso: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Uso: POLYSOLID <anchura> <altura> (seleccione primero una polilínea)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Uso: PYRAMID <radio> <altura> [lados] (4 lados de forma predeterminada)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Precise el primer punto o [Polilínea]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Precise el primer punto o [Marcos/Polilínea] <Polilínea>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Precise el punto siguiente o [Deshacer/Cerrar] (__ocs_arg_n__ puntos):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polilínea Seleccione una polilínea plana cerrada:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Rectangular Precise la primera esquina:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Rectangular Precise la esquina opuesta:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Marcos Indique el modo de marco [Desactivado/Activado/MostrarSinTrazar] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Polilínea ¿Borrar la polilínea de origen? [Sí/No] <No>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Polilínea: seleccione una polilínea 2D recta, cerrada y plana con al menos 3 vértices que no se intersequen.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ requiere 0, 1 o 2.
|
||||
.frames = Marcos
|
||||
.display-but-not-plot = Mostrar pero no trazar
|
||||
.frames-on-not-plotted = Marcos activados, no trazados
|
||||
.image-adjust = Ajuste de imagen
|
||||
.x-ray = Rayos X
|
||||
.xline-specify-a-point = XLINE Precise un punto:
|
||||
.xline-specify-through-point = XLINE Precise el punto de paso:
|
||||
|
|
@ -1976,6 +2035,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = Se cortaron __ocs_fmt_0__ objetos al portapapeles.
|
||||
|
||||
model =
|
||||
.create = Crear
|
||||
.boolean = Booleanas
|
||||
.pyramid = Pirámide
|
||||
.polysolid = Polisólido
|
||||
.extrude = Extruir
|
||||
.revolve = Revolucionar
|
||||
.loft = Transición
|
||||
.sweep = Barrido
|
||||
.presspull = Presionar/tirar
|
||||
.n-specify-center-point = __ocs_arg_n__ Especifique el centro:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Especifique la primera esquina:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Especifique la altura <Enter para predeterminada>:
|
||||
|
|
@ -1988,6 +2056,11 @@ model =
|
|||
.cv-hull = Envolvente de vértices de control
|
||||
.class-entity = Clase/Entidad
|
||||
.curved = Curvado
|
||||
.cylindrical = Cilíndrico
|
||||
.inverted-curved = Curvado invertido
|
||||
.inverted-cylindrical = Cilíndrico invertido
|
||||
.inverted-hemispherical = Hemisférico invertido
|
||||
.inverted-spherical = Esférico invertido
|
||||
.data-size = Tamaño de los datos
|
||||
.database-curves = Curvas de base de datos
|
||||
.design = Diseño
|
||||
|
|
@ -2271,6 +2344,7 @@ view =
|
|||
.zoom-extents =
|
||||
Zoom
|
||||
Extensión
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: no hay objetos de sombreado.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: no hay objetos de texto ni de cota.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: aún no implementado.
|
||||
.viewport-s-in-layout = __ocs_fmt_0__ ventanas gráficas en la presentación «__ocs_fmt_1__»:
|
||||
|
|
@ -3010,7 +3084,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nuevo valor [0=punto 1=ninguno 2=+ 3=x 4=marca; +32 círculo +64 cuadrado]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE establecido en __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nuevo tamaño de punto (0 = 5 % de la ventana gráfica, <0 = absoluto):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nuevo tamaño de punto (0 = 5 % de la ventana gráfica, >0 = absoluto, <0 = porcentaje de la ventana gráfica):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE establecido en __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Piirrä
|
||||
.annotate = Merkinnät
|
||||
.insert = Lisää
|
||||
.model = Malli
|
||||
.model = Mallinnus
|
||||
.layout = Asettelu
|
||||
.manage = Hallinta
|
||||
.view = Näkymä
|
||||
|
|
@ -313,6 +313,7 @@ common =
|
|||
.block-rotation = Lohkon kierto
|
||||
.blocks = Lohkot
|
||||
.blue = Sininen
|
||||
.bulge = Kaarevuus
|
||||
.boolean-failed-the-solids-may-not-overlap = Boolen arvo epäonnistui – kiinteät aineet eivät ehkä mene päällekkäin.
|
||||
.boolean-select-exactly-two-solids-created-this = Boolen arvo: valitse täsmälleen kaksi tässä istunnossa luotua solidia.
|
||||
.bottom = Pohja
|
||||
|
|
@ -1328,6 +1329,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Määritä pisteen kautta:
|
||||
.rect-specify-first-corner = RECT Määritä ensimmäinen kulma:
|
||||
.rect-specify-opposite-corner = RECT Määritä vastakkainen kulma:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Laske mitat [Pituuden / Leveyden] perusteella <Pituus>:
|
||||
.rect-specify-elevation = RECT Määritä korkeusasema <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Määritä pyöristyssäde <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT Määritä ensimmäinen viiste-etäisyys <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Määritä suunta ensimmäisestä kulmasta:
|
||||
.rect-specify-rectangle-area = RECT Määritä suorakulmion pinta-ala:
|
||||
.rect-specify-rectangle-length = RECT Määritä suorakulmion pituus:
|
||||
.rect-specify-rectangle-width = RECT Määritä suorakulmion leveys:
|
||||
.rect-specify-rotation-angle = RECT Määritä kiertokulma <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT Määritä toinen viiste-etäisyys <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Määritä paksuus <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Määritä leveys <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Määritä keskipiste:
|
||||
.rect-cen-specify-corner-point =RECT CEN Määritä kulmapiste:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Määritä viereinen kulma (määrittää reunasuunnan):
|
||||
|
|
@ -1337,6 +1350,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: valitse suljetut polylinjat tai ympyrät.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Määritä seuraava piste (__ocs_arg_count__ pts, Enter sulkemiseen):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Määritä aloituspiste (kaaren pituus = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD Ensimmäinen kulma (__ocs_arg_style__, kaaren pituus __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Vastakkainen kulma: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Monikulmion aloituspiste (__ocs_arg_style__, kaaren pituus __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Seuraava monikulmiopiste (__ocs_arg_count__ pistettä, sulje Enterillä): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Vapaapiirron ensimmäinen piste (__ocs_arg_style__, kaaren pituus __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Ohjaa kohdistinta ja sulje napsauttamalla tai painamalla Enter: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Kaaren jänteen likimääräinen pituus <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Tyyli [Normaali/Kalligrafia] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Valitse suljettu ympyrä, ellipsi, moniviiva tai splini: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Käännä kaaren suunta [Kyllä/Ei] <Ei>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Valitse suljettu tarkistuspilven moniviiva korvauksen alun läheltä: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Korvauspiste __ocs_arg_count__; lopeta toiseen pilven kärkipisteeseen:
|
||||
.revcloud-pick-side = REVCLOUD Valitse alkuperäisen pilven poistettava puoli:
|
||||
.revcloud-path-cannot-form = Valittu polku ei voi muodostaa tarkistuspilveä.
|
||||
.revcloud-select-closed-revision-cloud = Valitse suljettu tarkistuspilven moniviiva.
|
||||
.revcloud-polyline-not-planar = Valittu moniviiva ei ole tasomainen.
|
||||
.revcloud-corners-non-zero-area = Kahden kulman on määritettävä nollasta poikkeava pinta-ala.
|
||||
.revcloud-arc-length-positive = Kaaren pituuden on oltava suurempi kuin nolla.
|
||||
.revcloud-enter-valid-arc-length = Anna kelvollinen kaaren pituus.
|
||||
.revcloud-select-supported-closed-curve = Valitse tuettu suljettu käyrä.
|
||||
.revcloud-select-closed-entity = Valitse suljettu ympyrä, ellipsi, moniviiva tai splini.
|
||||
.calligraphy = Kalligrafia
|
||||
.first-point = Ensimmäinen piste
|
||||
.freehand = Vapaapiirto
|
||||
.rectangular = Suorakulmainen
|
||||
.rectangle-center = Suorakaide - keskellä
|
||||
.rectangle-rotated = Suorakaide - Kierretty
|
||||
.rectangle-two-corners = Suorakulmio - kaksi kulmaa
|
||||
|
|
@ -1347,6 +1385,19 @@ draw =
|
|||
.section-offset-along-z = SECTION offset pitkin Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Kynä alas — siirrä luonnos, napsauta nostaaksesi, Enter tallentaa:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Kynä ylös — napsauta laskeaksesi kynää, Enter tallentaaksesi:
|
||||
.sketch-pen = Kynä
|
||||
.sketch-increment = Askel
|
||||
.sketch-record = Tallenna
|
||||
.sketch-stop-erasing = Lopeta pyyhkiminen
|
||||
.sketch-connect = Yhdistä
|
||||
.sketch-exit = Lopeta
|
||||
.sketch-quit = Hylkää
|
||||
.sketch-object-type = SKETCH Kohdetyyppi [Viiva/Moniviiva/Spline]:
|
||||
.sketch-record-increment = SKETCH Tallennusaskel <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Splinen sovitustoleranssi <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Pyyhi — liiku taaksepäin väliaikaisen luonnoksen päällä:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, askel __ocs_arg_increment__) Kynä alhaalla — piirrä liikuttamalla:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, askel __ocs_arg_increment__) Kynä ylhäällä — laske napsauttamalla:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE leikkaustason akseli [X / Y / Z] (lisää TOP/BOTTOM kirjoittamalla):
|
||||
.slice-offset-along-x = SLICE offset pitkin X:
|
||||
.slice-offset-along-y = SLICE offset pitkin Y:
|
||||
|
|
@ -1378,11 +1429,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Käyttö: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Käyttö: POLYSOLID <width> <height> (valitse ensin polyline)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Käyttö: PYRAMID <radius> <height> [sides] (oletus 4 sivua)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Määritä ensimmäinen piste tai [Polyline]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Määritä ensimmäinen piste tai [Kehykset/Moniviiva] <Moniviiva>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Määritä seuraava piste tai [Kumoa/Sulje] (__ocs_arg_n__-pisteet):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polyline Valitse suljettu tasoviiva:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Suorakulmainen Määritä ensimmäinen kulma:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Suorakulmainen Määritä vastakkainen kulma:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Kehykset Anna kehystila [Pois/Päällä/NäytäEiTulostusta] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Moniviiva Poistetaanko lähdemoniviiva? [Kyllä/Ei] <Ei>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Moniviiva: valitse suora, suljettu ja tasomainen 2D-moniviiva, jossa on vähintään 3 toisiaan leikkaamatonta kärkeä.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ vaatii arvon 0, 1 tai 2.
|
||||
.frames = Kehykset
|
||||
.display-but-not-plot = Näytä, mutta älä tulosta
|
||||
.frames-on-not-plotted = Kehykset päällä, ei tulosteta
|
||||
.image-adjust = Kuvan säätö
|
||||
.x-ray = X-Ray
|
||||
.xline-specify-a-point = XLINE Määritä piste:
|
||||
.xline-specify-through-point = XLINE Määritä pisteen kautta:
|
||||
|
|
@ -1975,6 +2034,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ objekti(t) leikattu leikepöydälle.
|
||||
|
||||
model =
|
||||
.create = Luo
|
||||
.boolean = Boolen operaatiot
|
||||
.pyramid = Pyramidi
|
||||
.polysolid = Monikappale
|
||||
.extrude = Pursota
|
||||
.revolve = Pyöräytä
|
||||
.loft = Loftaus
|
||||
.sweep = Pyyhkäisy
|
||||
.presspull = Työnnä/vedä
|
||||
.n-specify-center-point = __ocs_arg_n__ Määritä keskipiste:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Määritä ensimmäinen kulma:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Määritä korkeus <Enter for default>:
|
||||
|
|
@ -1987,6 +2055,11 @@ model =
|
|||
.cv-hull = CV Runko
|
||||
.class-entity =Luokka / Entiteetti
|
||||
.curved = Kaareva
|
||||
.cylindrical = Sylinterimäinen
|
||||
.inverted-curved = Käänteinen kaareva
|
||||
.inverted-cylindrical = Käänteinen sylinterimäinen
|
||||
.inverted-hemispherical = Käänteinen puolipallomainen
|
||||
.inverted-spherical = Käänteinen pallomainen
|
||||
.data-size = Tietojen koko
|
||||
.database-curves = Tietokantakäyrät
|
||||
.design = Suunnittelu
|
||||
|
|
@ -2270,6 +2343,7 @@ view =
|
|||
.zoom-extents =
|
||||
Zoomaa
|
||||
Laajuudet
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: ei t├ñytt├Âobjekteja.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: ei tekstiä tai mittaobjekteja.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: ei vielä otettu käyttöön.
|
||||
.viewport-s-in-layout =__ocs_fmt_0__-näkymä(t) asettelussa "__ocs_fmt_1__":
|
||||
|
|
@ -3009,7 +3083,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE uusi arvo [0=dot 1=none 2=+ 3=x 4=tick; +32 ympyrä +64 neliö]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE asetettu arvoon __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE uusi pistekoko (0 = 5 % kuvaportista, <0 = absoluuttinen):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE uusi pistekoko (0 = 5 % kuvaportista, >0 = absoluuttinen, <0 = kuvaportin prosenttiosuus):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE asetettu arvoon __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Dessin
|
||||
.annotate = Annoter
|
||||
.insert = Insérer
|
||||
.model = Modèle
|
||||
.model = Modélisation
|
||||
.layout = Mise en page
|
||||
.manage = Gérer
|
||||
.view = Affichage
|
||||
|
|
@ -314,6 +314,7 @@ common =
|
|||
.block-rotation = Rotation par blocs
|
||||
.blocks = Blocs
|
||||
.blue = Bleu
|
||||
.bulge = Courbure
|
||||
.boolean-failed-the-solids-may-not-overlap = Booléenne échoué — les solides ne peuvent pas se chevaucher.
|
||||
.boolean-select-exactly-two-solids-created-this = Booléen : sélectionnez exactement deux solides créés cette session.
|
||||
.bottom = En bas
|
||||
|
|
@ -1329,6 +1330,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Spécifiez par le point:
|
||||
.rect-specify-first-corner = RECT Spécifiez le premier coin :
|
||||
.rect-specify-opposite-corner = RECT Spécifiez le coin opposé :
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Calculer les dimensions selon [Longueur / Largeur] <Longueur> :
|
||||
.rect-specify-elevation = RECT Spécifiez l’élévation <__ocs_fmt_0__> :
|
||||
.rect-specify-fillet-radius = RECT Spécifiez le rayon de raccord <__ocs_fmt_0__> :
|
||||
.rect-specify-first-chamfer-distance = RECT Spécifiez la première distance de chanfrein <__ocs_fmt_0__> :
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Spécifiez l’orientation depuis le premier coin :
|
||||
.rect-specify-rectangle-area = RECT Spécifiez l’aire du rectangle :
|
||||
.rect-specify-rectangle-length = RECT Spécifiez la longueur du rectangle :
|
||||
.rect-specify-rectangle-width = RECT Spécifiez la largeur du rectangle :
|
||||
.rect-specify-rotation-angle = RECT Spécifiez l’angle de rotation <__ocs_fmt_0__> :
|
||||
.rect-specify-second-chamfer-distance = RECT Spécifiez la deuxième distance de chanfrein <__ocs_fmt_0__> :
|
||||
.rect-specify-thickness = RECT Spécifiez l’épaisseur <__ocs_fmt_0__> :
|
||||
.rect-specify-width = RECT Spécifiez la largeur <__ocs_fmt_0__> :
|
||||
.rect-cen-specify-center-point = RECT CEN Spécifier le point central:
|
||||
.rect-cen-specify-corner-point = RECT CEN Spécifiez le point d'angle:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Spécifiez le coin adjacent (définit la direction du bord):
|
||||
|
|
@ -1338,6 +1351,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION : sélectionnez les polylignes ou les cercles fermés.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Spécifiez le point suivant (__ocs_arg_count__ pts, entrez pour fermer):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Spécifier le point de départ (longueur de l'arc = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD Premier coin (__ocs_arg_style__, longueur d'arc __ocs_arg_length__) : __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Coin opposé : __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Point de départ polygonal (__ocs_arg_style__, longueur d'arc __ocs_arg_length__) : __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Point polygonal suivant (__ocs_arg_count__ points, Entrée pour fermer) : __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Premier point à main levée (__ocs_arg_style__, longueur d'arc __ocs_arg_length__) : __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Guidez le curseur, puis cliquez ou appuyez sur Entrée pour fermer : __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Longueur approximative de la corde d'arc <__ocs_arg_length__> :
|
||||
.revcloud-style = REVCLOUD Style [Normal/Calligraphie] <__ocs_arg_style__> :
|
||||
.revcloud-select-closed-curve = REVCLOUD Sélectionnez un cercle, une ellipse, une polyligne ou une spline fermés : __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Inverser la direction des arcs [Oui/Non] <Non> :
|
||||
.revcloud-select-revision-cloud = REVCLOUD Sélectionnez une polyligne de nuage de révision fermée près du début du remplacement : __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Point de remplacement __ocs_arg_count__ ; terminez sur un autre sommet du nuage :
|
||||
.revcloud-pick-side = REVCLOUD Choisissez le côté du nuage d'origine à effacer :
|
||||
.revcloud-path-cannot-form = Le chemin sélectionné ne peut pas former un nuage de révision.
|
||||
.revcloud-select-closed-revision-cloud = Sélectionnez une polyligne de nuage de révision fermée.
|
||||
.revcloud-polyline-not-planar = La polyligne sélectionnée n'est pas plane.
|
||||
.revcloud-corners-non-zero-area = Les deux coins doivent définir une aire non nulle.
|
||||
.revcloud-arc-length-positive = La longueur d'arc doit être supérieure à zéro.
|
||||
.revcloud-enter-valid-arc-length = Saisissez une longueur d'arc valide.
|
||||
.revcloud-select-supported-closed-curve = Sélectionnez une courbe fermée prise en charge.
|
||||
.revcloud-select-closed-entity = Sélectionnez un cercle, une ellipse, une polyligne ou une spline fermés.
|
||||
.calligraphy = Calligraphie
|
||||
.first-point = Premier point
|
||||
.freehand = Main levée
|
||||
.rectangular = Rectangulaire
|
||||
.rectangle-center = Rectangle - Centre
|
||||
.rectangle-rotated = Rectangle - Rotation
|
||||
.rectangle-two-corners = Rectangle - Deux coins
|
||||
|
|
@ -1348,6 +1386,19 @@ draw =
|
|||
.section-offset-along-z = SECTION décalage le long de Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Pen vers le bas — déplacer vers le croquis, cliquer pour soulever, Entrer pour enregistrer:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pen up — cliquez pour baisser le stylo, entrez pour enregistrer:
|
||||
.sketch-pen = Stylo
|
||||
.sketch-increment = Incrément
|
||||
.sketch-record = Enregistrer
|
||||
.sketch-stop-erasing = Arrêter d’effacer
|
||||
.sketch-connect = Relier
|
||||
.sketch-exit = Terminer
|
||||
.sketch-quit = Abandonner
|
||||
.sketch-object-type = SKETCH Type d’objet [Ligne/Polyligne/Spline] :
|
||||
.sketch-record-increment = SKETCH Pas d’enregistrement <__ocs_arg_value__> :
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolérance d’ajustement de la spline <__ocs_arg_value__> :
|
||||
.sketch-erase-move-backward = SKETCH Effacer — revenez en arrière sur l’esquisse temporaire :
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, incrément __ocs_arg_increment__) Stylo baissé — déplacez pour dessiner :
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, incrément __ocs_arg_increment__) Stylo levé — cliquez pour l’abaisser :
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE axe du plan de coupe [X / Y / Z] (ajouter TOP/BOTTOM par typage):
|
||||
.slice-offset-along-x = SLICE décalage le long de X:
|
||||
.slice-offset-along-y = SLICE décalage le long de Y:
|
||||
|
|
@ -1379,11 +1430,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Utilisation : ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Utilisation : POLYSOLID <width> <height> (select a polyline first)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Utilisation : PYRAMID <radius> <height> [sides] (default 4 sides)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Spécifier le premier point ou [Polyligne]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Spécifier le premier point ou [Cadres/Polyligne] <Polyligne> :
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Spécifiez le point suivant ou [Désactiver/Fermer] (__ocs_arg_n__ points):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polyligne Sélectionnez une polyligne plane fermée :
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Rectangulaire Précisez le premier coin :
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Rectangulaire Spécifiez le coin opposé:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Cadres Entrez le mode de cadre [Désactivé/Activé/AfficherSansTracer] <__ocs_arg_mode__> :
|
||||
.wipeout-polyline-erase-source = WIPEOUT Polyligne Effacer la polyligne source ? [Oui/Non] <Non> :
|
||||
.wipeout-polyline-invalid = WIPEOUT Polyligne : sélectionnez une polyligne 2D droite, fermée et plane comportant au moins 3 sommets sans intersection.
|
||||
.setvar-frame-range = SETVAR : __ocs_fmt_0__ exige 0, 1 ou 2.
|
||||
.frames = Cadres
|
||||
.display-but-not-plot = Afficher sans tracer
|
||||
.frames-on-not-plotted = Cadres activés, non tracés
|
||||
.image-adjust = Réglage de l’image
|
||||
.x-ray = Rayon X
|
||||
.xline-specify-a-point = XLINE Spécifiez un point:
|
||||
.xline-specify-through-point = XLINE Spécifiez par le point:
|
||||
|
|
@ -1976,6 +2035,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = Objets coupés vers le presse-papiers : __ocs_fmt_0__.
|
||||
|
||||
model =
|
||||
.create = Créer
|
||||
.boolean = Opérations booléennes
|
||||
.pyramid = Pyramide
|
||||
.polysolid = Polysolide
|
||||
.extrude = Extruder
|
||||
.revolve = Révolution
|
||||
.loft = Lissage
|
||||
.sweep = Balayage
|
||||
.presspull = Tirer-pousser
|
||||
.n-specify-center-point = __ocs_arg_n__ Spécifier le point central:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Spécifiez le premier coin :
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Spécifiez la hauteur <Entrer pour la valeur par défaut> :
|
||||
|
|
@ -1988,6 +2056,11 @@ model =
|
|||
.cv-hull = CV Houille
|
||||
.class-entity = Catégorie/entité
|
||||
.curved = Courbé
|
||||
.cylindrical = Cylindrique
|
||||
.inverted-curved = Courbé inversé
|
||||
.inverted-cylindrical = Cylindrique inversé
|
||||
.inverted-hemispherical = Hémisphérique inversé
|
||||
.inverted-spherical = Sphérique inversé
|
||||
.data-size = Taille des données
|
||||
.database-curves = Courbes de base de données
|
||||
.design = Conception
|
||||
|
|
@ -2271,6 +2344,7 @@ view =
|
|||
.zoom-extents =
|
||||
Zoom
|
||||
Portée
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: aucun objet de hachure.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: aucun objet texte ou dimensionnel.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: pas encore mis en œuvre.
|
||||
.viewport-s-in-layout = __ocs_fmt_0__ fenêtre(s) dans la présentation « __ocs_fmt_1__ » :
|
||||
|
|
@ -3010,7 +3084,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nouvelle valeur [0=dot 1=none 2=+ 3=x 4=tick; +32 cercle +64 carré]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE réglé sur __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Nouvelle taille de point (0 = 5 % de la fenêtre, < 0 = absolue) :
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Nouvelle taille de point (0 = 5 % de la fenêtre, > 0 = absolue, < 0 = pourcentage de la fenêtre) :
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE réglé sur __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = ड्रॉ
|
||||
.annotate = एनोटेट
|
||||
.insert = सम्मिलित करें
|
||||
.model = मॉडल
|
||||
.model = मॉडलिंग
|
||||
.layout = लेआउट
|
||||
.manage = प्रबंधित करें
|
||||
.view = देखें
|
||||
|
|
@ -302,6 +302,7 @@ common =
|
|||
.block-rotation = ब्लॉक रोटेशन
|
||||
.blocks = ब्लॉक
|
||||
.blue = नीला
|
||||
.bulge = उभार
|
||||
.boolean-failed-the-solids-may-not-overlap = बोओलेन विफल रहा - ठोस ओवरलैप नहीं हो सकता।
|
||||
.boolean-select-exactly-two-solids-created-this = बोओलान: इस सत्र को बनाने वाले दो ठोस चुनें।
|
||||
.bottom = नीचे
|
||||
|
|
@ -1317,6 +1318,18 @@ draw =
|
|||
.ray-specify-through-point = RAY पॉइंट के माध्यम से निर्दिष्ट करें:
|
||||
.rect-specify-first-corner = RECT पहले कोने को निर्दिष्ट करें:
|
||||
.rect-specify-opposite-corner = RECT विपरीत कोने निर्दिष्ट करें:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT [लंबाई / चौड़ाई] के आधार पर आयाम निकालें <लंबाई>:
|
||||
.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 निकटवर्ती कोने (डिफ़ाइन किनारे दिशा) निर्दिष्ट करें:
|
||||
|
|
@ -1326,6 +1339,31 @@ draw =
|
|||
.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 शैली [सामान्य/सुलेख] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD बंद वृत्त, दीर्घवृत्त, पॉलीलाइन या स्प्लाइन चुनें: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD चाप दिशा उलटें [हाँ/नहीं] <नहीं>:
|
||||
.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 = बंद वृत्त, दीर्घवृत्त, पॉलीलाइन या स्प्लाइन चुनें।
|
||||
.calligraphy = सुलेख
|
||||
.first-point = पहला बिंदु
|
||||
.freehand = मुक्तहस्त
|
||||
.rectangular = आयताकार
|
||||
.rectangle-center = आयत - केंद्र
|
||||
.rectangle-rotated = आयत - घुमाया
|
||||
.rectangle-two-corners = आयत - दो कोनों
|
||||
|
|
@ -1336,6 +1374,19 @@ draw =
|
|||
.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 ऑब्जेक्ट प्रकार [रेखा/पॉलीलाइन/Spline]:
|
||||
.sketch-record-increment = SKETCH रिकॉर्ड वृद्धि <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline फ़िट सहनशीलता <__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] (Typing द्वारा शीर्ष/BOTTOM को शामिल किया गया):
|
||||
.slice-offset-along-x = SLICE X के साथ ऑफसेट:
|
||||
.slice-offset-along-y = SLICE वाई के साथ ऑफसेट:
|
||||
|
|
@ -1367,11 +1418,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = उपयोग: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = उपयोग: POLYSOLID <width> <height> (select a polyline first)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = उपयोग: PYRAMID <radius> <height> [sides] (default 4 sides)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT प्रथम बिंदु या [पॉलिलाइन] निर्दिष्ट करें:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT पहला बिंदु या [फ्रेम/पॉलिलाइन] <पॉलिलाइन> निर्दिष्ट करें:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT अगला बिंदु निर्दिष्ट करें या [पूर्ववत करें / बंद करें] (__ocs_arg_n__ बिंदु):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT पॉलीलाइन एक बंद प्लानर पॉलीलाइन चुनें:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT आयताकार पहले कोने निर्दिष्ट करें:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT आयताकार विपरीत कोने निर्दिष्ट करें:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT फ्रेम फ्रेम मोड दर्ज करें [बंद/चालू/दिखाएंपरप्लॉटनकरें] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT पॉलिलाइन स्रोत पॉलिलाइन मिटाएं? [हां/नहीं] <नहीं>:
|
||||
.wipeout-polyline-invalid = WIPEOUT पॉलिलाइन: कम से कम 3 न कटने वाले शीर्षों वाली सीधी, बंद, समतल 2D पॉलिलाइन चुनें।
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ के लिए 0, 1 या 2 आवश्यक है।
|
||||
.frames = फ्रेम
|
||||
.display-but-not-plot = दिखाएं, पर प्लॉट न करें
|
||||
.frames-on-not-plotted = फ्रेम चालू, प्लॉट नहीं होंगे
|
||||
.image-adjust = छवि समायोजन
|
||||
.x-ray = एक्स-रे
|
||||
.xline-specify-a-point = XLINE एक बिंदु निर्दिष्ट करें:
|
||||
.xline-specify-through-point = XLINE बिंदु के माध्यम से निर्दिष्ट करें:
|
||||
|
|
@ -1964,6 +2023,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ ऑब्जेक्ट क्लिपबोर्ड पर काटे गए।
|
||||
|
||||
model =
|
||||
.create = बनाएँ
|
||||
.boolean = बूलियन संक्रियाएँ
|
||||
.pyramid = पिरामिड
|
||||
.polysolid = बहुखंड ठोस
|
||||
.extrude = एक्सट्रूड
|
||||
.revolve = घुमाएँ
|
||||
.loft = लॉफ्ट
|
||||
.sweep = स्वीप
|
||||
.presspull = दबाएँ/खींचें
|
||||
.n-specify-center-point = __ocs_arg_n__ केंद्र बिंदु निर्दिष्ट करें:
|
||||
.n-specify-first-corner = __ocs_arg_n__ पहले कोने को निर्दिष्ट करें:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ ऊंचाई निर्दिष्ट करें <Diefal> के लिए दर्ज करें>:
|
||||
|
|
@ -1976,6 +2044,11 @@ model =
|
|||
.cv-hull = CV हल
|
||||
.class-entity = क्लास / ऑब्जेक्ट
|
||||
.curved = घुमावदार
|
||||
.cylindrical = बेलनाकार
|
||||
.inverted-curved = उलटा घुमावदार
|
||||
.inverted-cylindrical = उलटा बेलनाकार
|
||||
.inverted-hemispherical = उलटा अर्धगोलाकार
|
||||
.inverted-spherical = उलटा गोलाकार
|
||||
.data-size = डेटा आकार
|
||||
.database-curves = डेटाबेस वक्र
|
||||
.design = डिजाइन
|
||||
|
|
@ -2259,6 +2332,7 @@ view =
|
|||
.zoom-extents =
|
||||
ज़ूम
|
||||
बहार
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: ÓñòÓÑïÓñê Óñ╣ÓÑêÓñÜ ÓñæÓñ¼ÓÑìÓñ£ÓÑçÓñòÓÑìÓñƒ Óñ¿Óñ╣ÓÑÇÓñéÓÑñ
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: कोई टेक्स्ट या डाइमेंशन ऑब्जेक्ट नहीं।
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: अभी तक लागू नहीं है।
|
||||
.viewport-s-in-layout = लेआउट "__ocs_fmt_1__" में __ocs_fmt_0__ व्यूपोर्ट:
|
||||
|
|
@ -2998,7 +3072,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE नया मान [0=बिंदु 1=कोई नहीं 2=+ 3=× 4=टिक; +32 वृत्त +64 वर्ग]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE सेट करने के लिए __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE नए बिंदु आकार (0 = 5% व्यूपोर्ट, <0 = निरपेक्ष):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE नया बिंदु आकार (0 = व्यूपोर्ट का 5%, >0 = निरपेक्ष, <0 = व्यूपोर्ट प्रतिशत):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE सेट करने के लिए __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Rajz
|
||||
.annotate = Feliratozás
|
||||
.insert = Beszúrás
|
||||
.model = Modell
|
||||
.model = Modellezés
|
||||
.layout = Elrendezés
|
||||
.manage = Kezelés
|
||||
.view = Nézet
|
||||
|
|
@ -313,6 +313,7 @@ common =
|
|||
.block-rotation = Blokkforgatás
|
||||
.blocks = Blokkok
|
||||
.blue = kék
|
||||
.bulge = Kidudorodás
|
||||
.boolean-failed-the-solids-may-not-overlap = A logikai elemzés sikertelen – előfordulhat, hogy a szilárdtestek nem fedik át egymást.
|
||||
.boolean-select-exactly-two-solids-created-this = Logikai: jelöljön ki pontosan két szilárdtestet, amelyek ebben a munkamenetben jöttek létre.
|
||||
.bottom = Alul
|
||||
|
|
@ -1328,6 +1329,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Pont megadása:
|
||||
.rect-specify-first-corner = RECT Adja meg az első sarkot:
|
||||
.rect-specify-opposite-corner = RECT Adja meg a szemközti sarkot:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Méretek számítása [Hossz / Szélesség] alapján <Hossz>:
|
||||
.rect-specify-elevation = RECT Adja meg a magasságot <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Adja meg a lekerekítés sugarát <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT Adja meg az első letörési távolságot <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Adja meg az irányt az első saroktól:
|
||||
.rect-specify-rectangle-area = RECT Adja meg a téglalap területét:
|
||||
.rect-specify-rectangle-length = RECT Adja meg a téglalap hosszát:
|
||||
.rect-specify-rectangle-width = RECT Adja meg a téglalap szélességét:
|
||||
.rect-specify-rotation-angle = RECT Adja meg az elforgatási szöget <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT Adja meg a második letörési távolságot <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Adja meg a vastagságot <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Adja meg a szélességet <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Középpont megadása:
|
||||
.rect-cen-specify-corner-point =RECT CEN Sarokpont megadása:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Adja meg a szomszédos sarkot (meghatározza az él irányát):
|
||||
|
|
@ -1337,6 +1350,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: zárt vonalláncok vagy körök kiválasztása.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Adja meg a következő pontot (__ocs_arg_count__ pont, Enter a bezáráshoz):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Kezdőpont megadása (ívhossz = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD Első sarok (__ocs_arg_style__, ívhossz __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Szemközti sarok: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Sokszög kezdőpontja (__ocs_arg_style__, ívhossz __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Következő sokszögpont (__ocs_arg_count__ pont, Enter a bezáráshoz): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Szabadkézi első pont (__ocs_arg_style__, ívhossz __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Vezesse a kurzort, majd kattintson vagy nyomja meg az Entert a bezáráshoz: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Közelítő ívhúrhossz <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Stílus [Normál/Kalligráfia] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Válasszon zárt kört, ellipszist, vonalláncot vagy spline-t: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Ívirány megfordítása [Igen/Nem] <Nem>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Válasszon zárt revíziósfelhő-vonalláncot a csere kezdőpontja közelében: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Cserepont __ocs_arg_count__; fejezze be a felhő egy másik csúcsán:
|
||||
.revcloud-pick-side = REVCLOUD Válassza ki az eredeti felhő törlendő oldalát:
|
||||
.revcloud-path-cannot-form = A kijelölt útvonalból nem készíthető revíziós felhő.
|
||||
.revcloud-select-closed-revision-cloud = Válasszon zárt revíziósfelhő-vonalláncot.
|
||||
.revcloud-polyline-not-planar = A kijelölt vonallánc nem síkbeli.
|
||||
.revcloud-corners-non-zero-area = A két saroknak nullától eltérő területet kell meghatároznia.
|
||||
.revcloud-arc-length-positive = Az ívhossznak nullánál nagyobbnak kell lennie.
|
||||
.revcloud-enter-valid-arc-length = Adjon meg érvényes ívhosszt.
|
||||
.revcloud-select-supported-closed-curve = Válasszon támogatott zárt görbét.
|
||||
.revcloud-select-closed-entity = Válasszon zárt kört, ellipszist, vonalláncot vagy spline-t.
|
||||
.calligraphy = Kalligráfia
|
||||
.first-point = Első pont
|
||||
.freehand = Szabadkézi
|
||||
.rectangular = Téglalap alakú
|
||||
.rectangle-center = Téglalap – Középen
|
||||
.rectangle-rotated = Téglalap – elforgatva
|
||||
.rectangle-two-corners = Téglalap – két sarok
|
||||
|
|
@ -1347,6 +1385,19 @@ draw =
|
|||
.section-offset-along-z = SECTION eltolás a Z mentén:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Toll lefelé – mozgassa a vázlatot, kattintson az emeléshez, Enter a rögzítéshez:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Toll felfelé – kattintson a toll leengedéséhez, Enter a rögzítéshez:
|
||||
.sketch-pen = Toll
|
||||
.sketch-increment = Lépésköz
|
||||
.sketch-record = Rögzítés
|
||||
.sketch-stop-erasing = Törlés leállítása
|
||||
.sketch-connect = Csatlakoztatás
|
||||
.sketch-exit = Kilépés
|
||||
.sketch-quit = Elvetés
|
||||
.sketch-object-type = SKETCH Objektumtípus [Vonal/Vonallánc/Spline]:
|
||||
.sketch-record-increment = SKETCH Rögzítési lépés <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline illesztési tűrése <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Törlés — haladjon visszafelé az ideiglenes vázlaton:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, lépés __ocs_arg_increment__) Toll lent — mozgassa a rajzoláshoz:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, lépés __ocs_arg_increment__) Toll fent — kattintson a leengedéshez:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE vágósík tengelye [X / Y / Z] (adja hozzá a TOP/BOTTOM-t beírással):
|
||||
.slice-offset-along-x = SLICE eltolás a X mentén:
|
||||
.slice-offset-along-y = SLICE eltolás a Y mentén:
|
||||
|
|
@ -1378,11 +1429,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Használat: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Használat: POLYSOLID <width> <height> (először válasszon ki egy vonalláncot)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Használat: PYRAMID <radius> <height> [sides] (alapértelmezett 4 oldal)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Adja meg az első pontot vagy a [Polyline]-t:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Adja meg az első pontot vagy [Keretek/Vonallánc] <Vonallánc>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Következő pont megadása vagy [Visszavonás/Bezárás] (__ocs_arg_n__ pont):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Vonallánc Válasszon ki egy zárt sík vonalláncot:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT téglalap Adja meg az első sarkot:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Rectangular Adja meg a szemközti sarkot:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Keretek Adja meg a keretmódot [Ki/Be/MegjelenítNemNyomtat] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Vonallánc Törli a forrás vonalláncot? [Igen/Nem] <Nem>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Vonallánc: válasszon egyenes, zárt, síkbeli 2D vonalláncot legalább 3, egymást nem metsző csúcsponttal.
|
||||
.setvar-frame-range = SETVAR: a __ocs_fmt_0__ értéke 0, 1 vagy 2 lehet.
|
||||
.frames = Keretek
|
||||
.display-but-not-plot = Megjelenítés nyomtatás nélkül
|
||||
.frames-on-not-plotted = Keretek bekapcsolva, nem nyomtatva
|
||||
.image-adjust = Képbeállítás
|
||||
.x-ray = X-Ray
|
||||
.xline-specify-a-point = XLINE Adjon meg egy pontot:
|
||||
.xline-specify-through-point = XLINE Pont megadása:
|
||||
|
|
@ -1975,6 +2034,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ objektum(ok) vágólapra vágva.
|
||||
|
||||
model =
|
||||
.create = Létrehozás
|
||||
.boolean = Logikai műveletek
|
||||
.pyramid = Gúla
|
||||
.polysolid = Poliszilárdtest
|
||||
.extrude = Kihúzás
|
||||
.revolve = Forgatás
|
||||
.loft = Átmenet
|
||||
.sweep = Seprés
|
||||
.presspull = Nyomás/húzás
|
||||
.n-specify-center-point = __ocs_arg_n__ Középpont megadása:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Adja meg az első sarkot:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Adja meg a <Enter for default> magasságot:
|
||||
|
|
@ -1987,6 +2055,11 @@ model =
|
|||
.cv-hull = CV Ház
|
||||
.class-entity =Osztály / Entitás
|
||||
.curved = Ívelt
|
||||
.cylindrical = Hengeres
|
||||
.inverted-curved = Fordított ívelt
|
||||
.inverted-cylindrical = Fordított hengeres
|
||||
.inverted-hemispherical = Fordított félgömb alakú
|
||||
.inverted-spherical = Fordított gömb alakú
|
||||
.data-size = Adatméret
|
||||
.database-curves = Adatbázis görbék
|
||||
.design = Tervezés
|
||||
|
|
@ -2270,6 +2343,7 @@ view =
|
|||
.zoom-extents =
|
||||
Zoom
|
||||
Terjedelem
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: nincs sraffozási objektum.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: nincs szöveg vagy méretobjektum.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: még nincs implementálva.
|
||||
.viewport-s-in-layout =__ocs_fmt_0__ nézetablak(ok) a „__ocs_fmt_1__” elrendezésben:
|
||||
|
|
@ -3009,7 +3083,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE új érték [0=dot 1=none 2=+ 3=x 4=tick; +32 kör +64 négyzet]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = A PDMODE __ocs_fmt_0__ értékre van állítva
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE új pontméret (0 = a nézetablak 5%-a, <0 = abszolút):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE új pontméret (0 = a nézetablak 5%-a, >0 = abszolút, <0 = a nézetablak százaléka):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = A PDSIZE __ocs_fmt_0__ értékre van állítva
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Disegno
|
||||
.annotate = Annotazione
|
||||
.insert = Inserisci
|
||||
.model = Modello
|
||||
.model = Modellazione
|
||||
.layout = Disposizione
|
||||
.manage = Gestisci
|
||||
.view = Vista
|
||||
|
|
@ -313,6 +313,7 @@ common =
|
|||
.block-rotation = Bloccare la rotazione
|
||||
.blocks = Blocchi
|
||||
.blue = Blu
|
||||
.bulge = Curvatura
|
||||
.boolean-failed-the-solids-may-not-overlap = Booleano non riuscito: i solidi potrebbero non sovrapporsi.
|
||||
.boolean-select-exactly-two-solids-created-this = Booleano: seleziona esattamente due solidi creati in questa sessione.
|
||||
.bottom = In basso
|
||||
|
|
@ -1328,6 +1329,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Specificare il punto di passaggio:
|
||||
.rect-specify-first-corner = RECT Specificare il primo angolo:
|
||||
.rect-specify-opposite-corner = RECT Specificare l'angolo opposto:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Calcolare le dimensioni in base a [Lunghezza / Larghezza] <Lunghezza>:
|
||||
.rect-specify-elevation = RECT Specificare l’elevazione <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Specificare il raggio di raccordo <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT Specificare la prima distanza di smusso <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Specificare l’orientamento dal primo angolo:
|
||||
.rect-specify-rectangle-area = RECT Specificare l’area del rettangolo:
|
||||
.rect-specify-rectangle-length = RECT Specificare la lunghezza del rettangolo:
|
||||
.rect-specify-rectangle-width = RECT Specificare la larghezza del rettangolo:
|
||||
.rect-specify-rotation-angle = RECT Specificare l’angolo di rotazione <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT Specificare la seconda distanza di smusso <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Specificare lo spessore <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Specificare la larghezza <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Specificare il punto centrale:
|
||||
.rect-cen-specify-corner-point =RECT CEN Specificare il punto d'angolo:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Specificare l'angolo adiacente (definisce la direzione del bordo):
|
||||
|
|
@ -1337,6 +1350,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: seleziona polilinee o cerchi chiusi.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Specificare il punto successivo (__ocs_arg_count__ punti, Enter per chiudere):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Specificare il punto iniziale (lunghezza arco = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD Primo angolo (__ocs_arg_style__, lunghezza arco __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Angolo opposto: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Punto iniziale poligonale (__ocs_arg_style__, lunghezza arco __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Punto poligonale successivo (__ocs_arg_count__ punti, Invio per chiudere): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Primo punto a mano libera (__ocs_arg_style__, lunghezza arco __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Guidare il cursore, quindi fare clic o premere Invio per chiudere: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Lunghezza approssimativa della corda dell'arco <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Stile [Normale/Calligrafia] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Selezionare un cerchio, un'ellisse, una polilinea o una spline chiusi: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Invertire la direzione dell'arco [Sì/No] <No>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Selezionare una polilinea chiusa di nuvola di revisione vicino all'inizio della sostituzione: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Punto di sostituzione __ocs_arg_count__; terminare su un altro vertice della nuvola:
|
||||
.revcloud-pick-side = REVCLOUD Scegliere il lato della nuvola originale da cancellare:
|
||||
.revcloud-path-cannot-form = Il percorso selezionato non può formare una nuvola di revisione.
|
||||
.revcloud-select-closed-revision-cloud = Selezionare una polilinea chiusa di nuvola di revisione.
|
||||
.revcloud-polyline-not-planar = La polilinea selezionata non è planare.
|
||||
.revcloud-corners-non-zero-area = I due angoli devono definire un'area diversa da zero.
|
||||
.revcloud-arc-length-positive = La lunghezza dell'arco deve essere maggiore di zero.
|
||||
.revcloud-enter-valid-arc-length = Immettere una lunghezza dell'arco valida.
|
||||
.revcloud-select-supported-closed-curve = Selezionare una curva chiusa supportata.
|
||||
.revcloud-select-closed-entity = Selezionare un cerchio, un'ellisse, una polilinea o una spline chiusi.
|
||||
.calligraphy = Calligrafia
|
||||
.first-point = Primo punto
|
||||
.freehand = Mano libera
|
||||
.rectangular = Rettangolare
|
||||
.rectangle-center = Rettangolo - Centro
|
||||
.rectangle-rotated = Rettangolo - Ruotato
|
||||
.rectangle-two-corners = Rettangolo: due angoli
|
||||
|
|
@ -1347,6 +1385,19 @@ draw =
|
|||
.section-offset-along-z = Scostamento SECTION lungo Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Penna giù: sposta sullo schizzo, fai clic per sollevare, Enter per registrare:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Penna su: fare clic per abbassare la penna, Enter per registrare:
|
||||
.sketch-pen = Penna
|
||||
.sketch-increment = Incremento
|
||||
.sketch-record = Registra
|
||||
.sketch-stop-erasing = Interrompi cancellazione
|
||||
.sketch-connect = Connetti
|
||||
.sketch-exit = Esci
|
||||
.sketch-quit = Abbandona
|
||||
.sketch-object-type = SKETCH Tipo di oggetto [Linea/Polilinea/Spline]:
|
||||
.sketch-record-increment = SKETCH Incremento di registrazione <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolleranza di adattamento spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Cancella — spostati indietro sullo schizzo temporaneo:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Penna abbassata — spostati per disegnare:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Penna sollevata — fai clic per abbassarla:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = Asse del piano di taglio SLICE [X / Y / Z] (aggiungere TOP/BOTTOM digitando):
|
||||
.slice-offset-along-x = Scostamento SLICE lungo X:
|
||||
.slice-offset-along-y = Scostamento SLICE lungo Y:
|
||||
|
|
@ -1378,11 +1429,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Utilizzo: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Utilizzo: POLYSOLID <width> <height> (selezionare prima una polilinea)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Utilizzo: PYRAMID <radius> <height> [sides] (predefinito 4 lati)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Specificare il primo punto o [Polyline]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Specificare il primo punto o [Cornici/Polilinea] <Polilinea>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Specificare il punto successivo o [Annulla/Chiudi] (punti __ocs_arg_n__):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polilinea Selezionare una polilinea planare chiusa:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Rettangolare Specificare il primo angolo:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Rettangolare Specificare l'angolo opposto:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Cornici Immettere la modalità cornice [Disattivata/Attivata/VisualizzaSenzaStampare] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Polilinea Cancellare la polilinea di origine? [Sì/No] <No>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Polilinea: selezionare una polilinea 2D diritta, chiusa e planare con almeno 3 vertici non intersecanti.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ richiede 0, 1 o 2.
|
||||
.frames = Cornici
|
||||
.display-but-not-plot = Visualizza ma non stampa
|
||||
.frames-on-not-plotted = Cornici attive, non stampate
|
||||
.image-adjust = Regolazione immagine
|
||||
.x-ray = X-Ray
|
||||
.xline-specify-a-point = XLINE Specificare un punto:
|
||||
.xline-specify-through-point = XLINE Specificare il punto di passaggio:
|
||||
|
|
@ -1975,6 +2034,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ oggetto/i tagliato negli appunti.
|
||||
|
||||
model =
|
||||
.create = Crea
|
||||
.boolean = Operazioni booleane
|
||||
.pyramid = Piramide
|
||||
.polysolid = Polisolido
|
||||
.extrude = Estrudi
|
||||
.revolve = Rivoluzione
|
||||
.loft = Loft
|
||||
.sweep = Scorrimento
|
||||
.presspull = Premi/tira
|
||||
.n-specify-center-point = __ocs_arg_n__ Specificare il punto centrale:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Specificare il primo angolo:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Specificare l'altezza <Enter for default>:
|
||||
|
|
@ -1987,6 +2055,11 @@ model =
|
|||
.cv-hull = CV Scafo
|
||||
.class-entity =Classe/Entità
|
||||
.curved = Curvo
|
||||
.cylindrical = Cilindrico
|
||||
.inverted-curved = Curvo invertito
|
||||
.inverted-cylindrical = Cilindrico invertito
|
||||
.inverted-hemispherical = Emisferico invertito
|
||||
.inverted-spherical = Sferico invertito
|
||||
.data-size = Dimensione dei dati
|
||||
.database-curves = Curve di database
|
||||
.design = Progettazione
|
||||
|
|
@ -2270,6 +2343,7 @@ view =
|
|||
.zoom-extents =
|
||||
Zoom
|
||||
Estensioni
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: nessun oggetto tratteggio.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: nessun testo o oggetto dimensione.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: non ancora implementato.
|
||||
.viewport-s-in-layout =Finestre __ocs_fmt_0__ nel layout "__ocs_fmt_1__":
|
||||
|
|
@ -3009,7 +3083,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nuovo valore [0=punto 1=none 2=+ 3=x 4=tick; +32 cerchio +64 quadrato]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE impostato su __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nuova dimensione in punti (0 = 5% dell'area visibile, <0 = assoluto):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nuova dimensione del punto (0 = 5% dell'area visibile, >0 = assoluto, <0 = percentuale dell'area visibile):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE impostato su __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = 作図
|
||||
.annotate = 注釈
|
||||
.insert = 挿入
|
||||
.model = モデル
|
||||
.model = モデリング
|
||||
.layout = レイアウト
|
||||
.manage = 管理
|
||||
.view = 表示
|
||||
|
|
@ -304,6 +304,7 @@ common =
|
|||
.block-rotation = ブロック回転
|
||||
.blocks = ブロック
|
||||
.blue = 青
|
||||
.bulge = ふくらみ
|
||||
.boolean-failed-the-solids-may-not-overlap = ブール演算に失敗しました — ソリッドが重なっていない可能性があります。
|
||||
.boolean-select-exactly-two-solids-created-this = ブール演算: このセッションで作成したソリッドを 2 つ選択してください。
|
||||
.bottom = 下
|
||||
|
|
@ -1319,6 +1320,18 @@ draw =
|
|||
.ray-specify-through-point = RAY 通過点を指定:
|
||||
.rect-specify-first-corner = RECT 最初のコーナーを指定:
|
||||
.rect-specify-opposite-corner = RECT 反対側のコーナーを指定:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT [長さ / 幅] に基づいて寸法を計算 <長さ>:
|
||||
.rect-specify-elevation = RECT 高度を指定 <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT フィレット半径を指定 <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT 1 番目の面取り距離を指定 <__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 2 番目の面取り距離を指定 <__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 隣接コーナーを指定(エッジ方向を定義):
|
||||
|
|
@ -1328,6 +1341,31 @@ draw =
|
|||
.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 スタイル [標準/カリグラフィー] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD 閉じた円、楕円、ポリライン、またはスプラインを選択: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD 円弧方向を反転 [はい/いいえ] <いいえ>:
|
||||
.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 = 2 つのコーナーでゼロでない領域を指定する必要があります。
|
||||
.revcloud-arc-length-positive = 円弧長はゼロより大きくする必要があります。
|
||||
.revcloud-enter-valid-arc-length = 有効な円弧長を入力してください。
|
||||
.revcloud-select-supported-closed-curve = 対応している閉じた曲線を選択してください。
|
||||
.revcloud-select-closed-entity = 閉じた円、楕円、ポリライン、またはスプラインを選択してください。
|
||||
.calligraphy = カリグラフィー
|
||||
.first-point = 最初の点
|
||||
.freehand = フリーハンド
|
||||
.rectangular = 長方形
|
||||
.rectangle-center = 長方形 - 中心
|
||||
.rectangle-rotated = 長方形 - 回転
|
||||
.rectangle-two-corners = 長方形 - 2 コーナー
|
||||
|
|
@ -1338,6 +1376,19 @@ draw =
|
|||
.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 オブジェクト タイプ [線分/ポリライン/スプライン]:
|
||||
.sketch-record-increment = SKETCH 記録増分 <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH スプライン フィット許容差 <__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 方向オフセット:
|
||||
|
|
@ -1369,11 +1420,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = 使用法: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = 使用法: POLYSOLID <width> <height> (先にポリラインを選択)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = 使用法: PYRAMID <radius> <height> [sides] (既定は 4 側面)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT 最初の点を指定または [Polyline]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT 最初の点を指定または [フレーム/ポリライン] <ポリライン>:
|
||||
.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 フレーム フレームモードを入力 [オフ/オン/表示して印刷しない] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT ポリライン 元のポリラインを削除しますか? [はい/いいえ] <いいえ>:
|
||||
.wipeout-polyline-invalid = WIPEOUT ポリライン: 交差しない3つ以上の頂点を持つ、直線の閉じた平面2Dポリラインを選択してください。
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ には 0、1、2 のいずれかが必要です。
|
||||
.frames = フレーム
|
||||
.display-but-not-plot = 表示するが印刷しない
|
||||
.frames-on-not-plotted = フレームオン、印刷なし
|
||||
.image-adjust = 画像調整
|
||||
.x-ray = X 線
|
||||
.xline-specify-a-point = XLINE 点を指定:
|
||||
.xline-specify-through-point = XLINE 通過点を指定:
|
||||
|
|
@ -1964,6 +2023,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ 個のオブジェクトを切り取ってクリップボードに保存しました。
|
||||
|
||||
model =
|
||||
.create = 作成
|
||||
.boolean = ブール演算
|
||||
.pyramid = ピラミッド
|
||||
.polysolid = ポリソリッド
|
||||
.extrude = 押し出し
|
||||
.revolve = 回転
|
||||
.loft = ロフト
|
||||
.sweep = スイープ
|
||||
.presspull = プレスプル
|
||||
.n-specify-center-point = __ocs_arg_n__ 中心点を指定:
|
||||
.n-specify-first-corner = __ocs_arg_n__ 最初のコーナーを指定:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ 高さを指定 <Enter で既定値>:
|
||||
|
|
@ -1976,6 +2044,11 @@ model =
|
|||
.cv-hull = 制御点ハル
|
||||
.class-entity = クラス / 図形
|
||||
.curved = 曲線
|
||||
.cylindrical = 円筒状
|
||||
.inverted-curved = 反転曲線
|
||||
.inverted-cylindrical = 反転円筒状
|
||||
.inverted-hemispherical = 反転半球状
|
||||
.inverted-spherical = 反転球状
|
||||
.data-size = データサイズ
|
||||
.database-curves = データベース曲線
|
||||
.design = 設計
|
||||
|
|
@ -2259,6 +2332,7 @@ view =
|
|||
.zoom-extents =
|
||||
全体表示
|
||||
ズーム
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: ÒâÅÒââÒâüÒâ│Òé░ Òé¬ÒâûÒé©ÒéºÒé»ÒâêÒüîÒüéÒéèÒü¥ÒüøÒéôÒÇé
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: テキストまたは寸法オブジェクトがありません。
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: まだ実装されていません。
|
||||
.viewport-s-in-layout = レイアウト「__ocs_fmt_1__」内の __ocs_fmt_0__ 個のビューポート:
|
||||
|
|
@ -2998,7 +3072,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 新しい値 [0=点 1=なし 2=+ 3=x 4=目盛り、+32 円 +64 正方形]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE を __ocs_fmt_0__ に設定しました
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 新しい点サイズ(0=ビューポートの 5%、<0=絶対値):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 新しい点サイズ(0=ビューポートの 5%、>0=絶対値、<0=ビューポートの割合):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE を __ocs_fmt_0__ に設定しました
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = 그리기
|
||||
.annotate = 주석
|
||||
.insert = 삽입
|
||||
.model = 모델
|
||||
.model = 모델링
|
||||
.layout = 배치
|
||||
.manage = 관리
|
||||
.view = 보기
|
||||
|
|
@ -313,6 +313,7 @@ common =
|
|||
.block-rotation = 블록 회전
|
||||
.blocks = 블록
|
||||
.blue = 블루
|
||||
.bulge = 벌지
|
||||
.boolean-failed-the-solids-may-not-overlap = 부울 실패 - 솔리드가 겹치지 않을 수 있습니다.
|
||||
.boolean-select-exactly-two-solids-created-this = 부울: 이번 세션에서 생성된 정확히 두 개의 솔리드를 선택합니다.
|
||||
.bottom = 바닥
|
||||
|
|
@ -1328,6 +1329,18 @@ draw =
|
|||
.ray-specify-through-point = RAY 통과점 지정:
|
||||
.rect-specify-first-corner = RECT 첫 번째 구석 지정:
|
||||
.rect-specify-opposite-corner = RECT 반대편 모서리 지정:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT [길이 / 폭]을 기준으로 치수 계산 <길이>:
|
||||
.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 인접 모서리 지정(모서리 방향 정의):
|
||||
|
|
@ -1337,6 +1350,31 @@ draw =
|
|||
.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 스타일 [일반/캘리그래피] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD 닫힌 원, 타원, 폴리선 또는 스플라인 선택: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD 호 방향 반전 [예/아니요] <아니요>:
|
||||
.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 = 두 구석점은 0이 아닌 영역을 정의해야 합니다.
|
||||
.revcloud-arc-length-positive = 호 길이는 0보다 커야 합니다.
|
||||
.revcloud-enter-valid-arc-length = 유효한 호 길이를 입력하십시오.
|
||||
.revcloud-select-supported-closed-curve = 지원되는 닫힌 곡선을 선택하십시오.
|
||||
.revcloud-select-closed-entity = 닫힌 원, 타원, 폴리선 또는 스플라인을 선택하십시오.
|
||||
.calligraphy = 캘리그래피
|
||||
.first-point = 첫 번째 점
|
||||
.freehand = 자유곡선
|
||||
.rectangular = 직사각형
|
||||
.rectangle-center = 직사각형 - 중심
|
||||
.rectangle-rotated = 직사각형 - 회전됨
|
||||
.rectangle-two-corners = 직사각형 - 두 모서리
|
||||
|
|
@ -1347,6 +1385,19 @@ draw =
|
|||
.section-offset-along-z = Z를 따라 SECTION 오프셋:
|
||||
.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 객체 유형 [선/폴리선/스플라인]:
|
||||
.sketch-record-increment = SKETCH 기록 증분 <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH 스플라인 맞춤 공차 <__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 = X를 따라 SLICE 오프셋:
|
||||
.slice-offset-along-y = Y를 따라 SLICE 오프셋:
|
||||
|
|
@ -1378,11 +1429,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = 사용법: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = 사용법: POLYSOLID <width> <height>(폴리라인을 먼저 선택하세요)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = 사용법: PYRAMID <radius> <height> [sides] (기본 4면)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT 첫 번째 점 또는 [Polyline] 지정:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT 첫 번째 점 또는 [프레임/폴리선] <폴리선> 지정:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT 다음 점 지정 또는 [실행 취소/닫기](__ocs_arg_n__ 점):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT 폴리선 닫힌 평면형 폴리선을 선택합니다.
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT 직사각형 첫 번째 구석 지정:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT 직사각형 반대쪽 모서리 지정:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT 프레임 프레임 모드 입력 [끄기/켜기/표시하고플롯안함] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT 폴리선 원본 폴리선을 지우시겠습니까? [예/아니오] <아니오>:
|
||||
.wipeout-polyline-invalid = WIPEOUT 폴리선: 교차하지 않는 꼭짓점이 3개 이상인 직선, 닫힌, 평면 2D 폴리선을 선택하세요.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__에는 0, 1 또는 2가 필요합니다.
|
||||
.frames = 프레임
|
||||
.display-but-not-plot = 표시하지만 플롯하지 않음
|
||||
.frames-on-not-plotted = 프레임 켜기, 플롯 안 함
|
||||
.image-adjust = 이미지 조정
|
||||
.x-ray = X-레이
|
||||
.xline-specify-a-point = XLINE 점 지정:
|
||||
.xline-specify-through-point = XLINE 통과점 지정:
|
||||
|
|
@ -1975,6 +2034,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ 개체를 클립보드로 잘라냈습니다.
|
||||
|
||||
model =
|
||||
.create = 생성
|
||||
.boolean = 불리언 연산
|
||||
.pyramid = 피라미드
|
||||
.polysolid = 폴리솔리드
|
||||
.extrude = 돌출
|
||||
.revolve = 회전
|
||||
.loft = 로프트
|
||||
.sweep = 스윕
|
||||
.presspull = 누르기/당기기
|
||||
.n-specify-center-point = __ocs_arg_n__ 중심점 지정:
|
||||
.n-specify-first-corner = __ocs_arg_n__ 첫 번째 구석 지정:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ 높이 지정 <Enter for default>:
|
||||
|
|
@ -1987,6 +2055,11 @@ model =
|
|||
.cv-hull = CV 선체
|
||||
.class-entity =클래스/엔티티
|
||||
.curved = 곡선
|
||||
.cylindrical = 원통형
|
||||
.inverted-curved = 반전 곡선형
|
||||
.inverted-cylindrical = 반전 원통형
|
||||
.inverted-hemispherical = 반전 반구형
|
||||
.inverted-spherical = 반전 구형
|
||||
.data-size = 데이터 크기
|
||||
.database-curves = 데이터베이스 곡선
|
||||
.design = 디자인
|
||||
|
|
@ -2270,6 +2343,7 @@ view =
|
|||
.zoom-extents =
|
||||
줌
|
||||
범위
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: Ýò┤ý╣ÿ Û░Øý▓┤Û░Ç ýùåýèÁÙïêÙïñ.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: 문자 또는 치수 객체가 없습니다.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: 아직 구현되지 않았습니다.
|
||||
.viewport-s-in-layout ="__ocs_fmt_1__" 레이아웃의 __ocs_fmt_0__ 뷰포트:
|
||||
|
|
@ -3009,7 +3083,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 새 값 [0=dot 1=none 2=+ 3=x 4=tick; +32원 +64사각형]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE가 __ocs_fmt_0__로 설정됨
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 새 포인트 크기(0 = 뷰포트의 5%, <0 = 절대값):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 새 포인트 크기(0 = 뷰포트의 5%, >0 = 절대값, <0 = 뷰포트 백분율):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE가 __ocs_fmt_0__로 설정됨
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Tekenen
|
||||
.annotate = Annoteren
|
||||
.insert = Invoegen
|
||||
.model = Model
|
||||
.model = Modelleren
|
||||
.layout = Indeling
|
||||
.manage = Beheren
|
||||
.view = Beeld
|
||||
|
|
@ -314,6 +314,7 @@ common =
|
|||
.block-rotation = Blokrotatie
|
||||
.blocks = Blokken
|
||||
.blue = Blauw
|
||||
.bulge = Bolling
|
||||
.boolean-failed-the-solids-may-not-overlap = Boolean mislukt . . de vaste stoffen mogen niet overlappen.
|
||||
.boolean-select-exactly-two-solids-created-this = Boolean: selecteer precies twee vaste stoffen die deze sessie hebben gemaakt.
|
||||
.bottom = Onderkant
|
||||
|
|
@ -1329,6 +1330,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Door het punt opgeven:
|
||||
.rect-specify-first-corner = RECT Eerste hoek opgeven:
|
||||
.rect-specify-opposite-corner = RECT Geef de andere hoek op:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Bereken afmetingen op basis van [Lengte / Breedte] <Lengte>:
|
||||
.rect-specify-elevation = RECT Geef hoogte op <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Geef afrondingsstraal op <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT Geef eerste afschuiningsafstand op <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Geef richting vanaf de eerste hoek op:
|
||||
.rect-specify-rectangle-area = RECT Geef oppervlakte van rechthoek op:
|
||||
.rect-specify-rectangle-length = RECT Geef lengte van rechthoek op:
|
||||
.rect-specify-rectangle-width = RECT Geef breedte van rechthoek op:
|
||||
.rect-specify-rotation-angle = RECT Geef rotatiehoek op <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT Geef tweede afschuiningsafstand op <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Geef dikte op <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Geef breedte op <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Centrumpunt opgeven:
|
||||
.rect-cen-specify-corner-point = RECT CEN Geef hoekpunt op:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Aangrenzende hoek opgeven (randrichting definiëren):
|
||||
|
|
@ -1338,6 +1351,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: selecteer gesloten polylijnen of cirkels.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Geef het volgende punt op (__ocs_arg_count__ punten, Enter om te sluiten):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Geef het beginpunt op (booglengte = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD Eerste hoek (__ocs_arg_style__, booglengte __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Tegenoverliggende hoek: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Veelhoekig beginpunt (__ocs_arg_style__, booglengte __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Volgend veelhoekpunt (__ocs_arg_count__ punten, Enter om te sluiten): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Eerste vrijehandspunt (__ocs_arg_style__, booglengte __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Leid de cursor en klik of druk op Enter om te sluiten: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Geschatte boogkoorde-lengte <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Stijl [Normaal/Kalligrafie] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Selecteer een gesloten cirkel, ellips, polylijn of spline: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Boogrichting omkeren [Ja/Nee] <Nee>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Selecteer een gesloten revisiewolk-polylijn bij het begin van de vervanging: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Vervangingspunt __ocs_arg_count__; eindig op een ander wolkhoekpunt:
|
||||
.revcloud-pick-side = REVCLOUD Kies de te wissen zijde van de oorspronkelijke wolk:
|
||||
.revcloud-path-cannot-form = Het geselecteerde pad kan geen revisiewolk vormen.
|
||||
.revcloud-select-closed-revision-cloud = Selecteer een gesloten revisiewolk-polylijn.
|
||||
.revcloud-polyline-not-planar = De geselecteerde polylijn is niet vlak.
|
||||
.revcloud-corners-non-zero-area = De twee hoeken moeten een oppervlak groter dan nul bepalen.
|
||||
.revcloud-arc-length-positive = De booglengte moet groter zijn dan nul.
|
||||
.revcloud-enter-valid-arc-length = Voer een geldige booglengte in.
|
||||
.revcloud-select-supported-closed-curve = Selecteer een ondersteunde gesloten kromme.
|
||||
.revcloud-select-closed-entity = Selecteer een gesloten cirkel, ellips, polylijn of spline.
|
||||
.calligraphy = Kalligrafie
|
||||
.first-point = Eerste punt
|
||||
.freehand = Vrije hand
|
||||
.rectangular = Rechthoekig
|
||||
.rectangle-center = Rechthoek - midden
|
||||
.rectangle-rotated = Rechthoek - gedraaid
|
||||
.rectangle-two-corners = Rechthoek - twee hoeken
|
||||
|
|
@ -1348,6 +1386,19 @@ draw =
|
|||
.section-offset-along-z = SECTION offset langs Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Pen naar beneden
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pen omhoog
|
||||
.sketch-pen = Pen
|
||||
.sketch-increment = Stapgrootte
|
||||
.sketch-record = Vastleggen
|
||||
.sketch-stop-erasing = Wissen stoppen
|
||||
.sketch-connect = Verbinden
|
||||
.sketch-exit = Afsluiten
|
||||
.sketch-quit = Annuleren
|
||||
.sketch-object-type = SKETCH Objecttype [Lijn/Polylijn/Spline]:
|
||||
.sketch-record-increment = SKETCH Opnamestap <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline-aanpassingstolerantie <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Wissen — beweeg terug over de tijdelijke schets:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, stap __ocs_arg_increment__) Pen omlaag — beweeg om te schetsen:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, stap __ocs_arg_increment__) Pen omhoog — klik om de pen neer te zetten:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE snijvlakas [X / Y / Z] (voeg TOP/BOTTOM toe door te typen):
|
||||
.slice-offset-along-x = SLICE offset langs X:
|
||||
.slice-offset-along-y = SLICE offset langs Y:
|
||||
|
|
@ -1379,11 +1430,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Gebruik: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Gebruik: POLYSOLID <width> <height> (select a polyline first)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Gebruik: PYRAMID <radius> <height> [sides] (default 4 sides)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Geef het eerste punt of [Polylijn]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Geef het eerste punt of [Kaders/Polylijn] <Polylijn>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Geef volgend punt op of [Ongedaan maken/Sluiten] (__ocs_arg_n__ punten):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polylijn Selecteer een gesloten planaire polylijn:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Rechthoekige eerste hoek opgeven:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Rechthoekig Geef tegenoverhoek aan:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Kaders Voer kadermodus in [Uit/Aan/WeergevenNietPlotten] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Polylijn Bronpolylijn wissen? [Ja/Nee] <Nee>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Polylijn: selecteer een rechte, gesloten, vlakke 2D-polylijn met ten minste 3 niet-kruisende hoekpunten.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ vereist 0, 1 of 2.
|
||||
.frames = Kaders
|
||||
.display-but-not-plot = Weergeven maar niet plotten
|
||||
.frames-on-not-plotted = Kaders aan, niet geplot
|
||||
.image-adjust = Beeldaanpassing
|
||||
.x-ray = X-Ray
|
||||
.xline-specify-a-point = XLINE Geef een punt op:
|
||||
.xline-specify-through-point = XLINE Geef door punt:
|
||||
|
|
@ -1976,6 +2035,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ object(en) naar het klembord geknipt.
|
||||
|
||||
model =
|
||||
.create = Maken
|
||||
.boolean = Booleaanse bewerkingen
|
||||
.pyramid = Piramide
|
||||
.polysolid = Polysolid
|
||||
.extrude = Extruderen
|
||||
.revolve = Omwentelen
|
||||
.loft = Loft
|
||||
.sweep = Vegen
|
||||
.presspull = Duwen/trekken
|
||||
.n-specify-center-point = __ocs_arg_n__ Centrumpunt opgeven:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Geef eerste hoek:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Hoogte <Enter voor standaard> opgeven:
|
||||
|
|
@ -1988,6 +2056,11 @@ model =
|
|||
.cv-hull = CV Romp
|
||||
.class-entity = Klasse/object
|
||||
.curved = gebogen
|
||||
.cylindrical = Cilindrisch
|
||||
.inverted-curved = Omgekeerd gebogen
|
||||
.inverted-cylindrical = Omgekeerd cilindrisch
|
||||
.inverted-hemispherical = Omgekeerd hemisferisch
|
||||
.inverted-spherical = Omgekeerd sferisch
|
||||
.data-size = Gegevensgrootte
|
||||
.database-curves = Databasecurves
|
||||
.design = Ontwerp
|
||||
|
|
@ -2271,6 +2344,7 @@ view =
|
|||
.zoom-extents =
|
||||
Zoomen
|
||||
Maten
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: geen arceerobjecten.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: geen tekst- of maatobjecten.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: nog niet geïmplementeerd.
|
||||
.viewport-s-in-layout = __ocs_fmt_0__ weergavevenster(s) in indeling "__ocs_fmt_1__":
|
||||
|
|
@ -3010,7 +3084,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nieuwe waarde [0=punt 1=geen 2=+ 3=x 4=streepje; +32 cirkel +64 vierkant]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE ingesteld op __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nieuwe puntgrootte (0 = 5% van weergavevenster, <0 = absoluut):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nieuwe puntgrootte (0 = 5% van weergavevenster, >0 = absoluut, <0 = percentage van weergavevenster):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE ingesteld op __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Rysowanie
|
||||
.annotate = Opisy
|
||||
.insert = Wstaw
|
||||
.model = Model
|
||||
.model = Modelowanie
|
||||
.layout = Układ
|
||||
.manage = Zarządzaj
|
||||
.view = Widok
|
||||
|
|
@ -313,6 +313,7 @@ common =
|
|||
.block-rotation = Obrót bloku
|
||||
.blocks = Bloki
|
||||
.blue = Niebieski
|
||||
.bulge = Wybrzuszenie
|
||||
.boolean-failed-the-solids-may-not-overlap = Wartość logiczna nie powiodła się — bryły nie mogą się nakładać.
|
||||
.boolean-select-exactly-two-solids-created-this = Boolean: wybierz dokładnie dwie bryły utworzone w tej sesji.
|
||||
.bottom = Dół
|
||||
|
|
@ -1328,6 +1329,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Określ punkt przelotowy:
|
||||
.rect-specify-first-corner = RECT Określ pierwszy narożnik:
|
||||
.rect-specify-opposite-corner = RECT Określ przeciwny narożnik:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Oblicz wymiary na podstawie [Długości / Szerokości] <Długość>:
|
||||
.rect-specify-elevation = RECT Określ rzędną <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Określ promień zaokrąglenia <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT Określ pierwszą odległość fazowania <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Określ orientację od pierwszego narożnika:
|
||||
.rect-specify-rectangle-area = RECT Określ pole prostokąta:
|
||||
.rect-specify-rectangle-length = RECT Określ długość prostokąta:
|
||||
.rect-specify-rectangle-width = RECT Określ szerokość prostokąta:
|
||||
.rect-specify-rotation-angle = RECT Określ kąt obrotu <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT Określ drugą odległość fazowania <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Określ grubość <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Określ szerokość <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Określ punkt środkowy:
|
||||
.rect-cen-specify-corner-point =RECT CEN Określ punkt narożny:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Określ sąsiadujący narożnik (określa kierunek krawędzi):
|
||||
|
|
@ -1337,6 +1350,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: wybierz zamknięte polilinie lub okręgi.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Określ następny punkt (__ocs_arg_count__ pkt, Enter aby zamknąć):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Podaj punkt początkowy (długość łuku = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD Pierwszy narożnik (__ocs_arg_style__, długość łuku __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Przeciwległy narożnik: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Punkt początkowy wielokąta (__ocs_arg_style__, długość łuku __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Następny punkt wielokąta (__ocs_arg_count__ punktów, Enter aby zamknąć): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Pierwszy punkt odręczny (__ocs_arg_style__, długość łuku __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Prowadź kursor, następnie kliknij lub naciśnij Enter, aby zamknąć: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Przybliżona długość cięciwy łuku <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Styl [Normalny/Kaligrafia] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Wybierz zamknięty okrąg, elipsę, polilinię lub splajn: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Odwrócić kierunek łuków [Tak/Nie] <Nie>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Wybierz zamkniętą polilinię chmury rewizyjnej w pobliżu początku zastąpienia: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Punkt zastąpienia __ocs_arg_count__; zakończ na innym wierzchołku chmury:
|
||||
.revcloud-pick-side = REVCLOUD Wybierz stronę pierwotnej chmury do usunięcia:
|
||||
.revcloud-path-cannot-form = Wybrana ścieżka nie może utworzyć chmury rewizyjnej.
|
||||
.revcloud-select-closed-revision-cloud = Wybierz zamkniętą polilinię chmury rewizyjnej.
|
||||
.revcloud-polyline-not-planar = Wybrana polilinia nie jest płaska.
|
||||
.revcloud-corners-non-zero-area = Dwa narożniki muszą wyznaczać pole różne od zera.
|
||||
.revcloud-arc-length-positive = Długość łuku musi być większa od zera.
|
||||
.revcloud-enter-valid-arc-length = Wprowadź prawidłową długość łuku.
|
||||
.revcloud-select-supported-closed-curve = Wybierz obsługiwaną zamkniętą krzywą.
|
||||
.revcloud-select-closed-entity = Wybierz zamknięty okrąg, elipsę, polilinię lub splajn.
|
||||
.calligraphy = Kaligrafia
|
||||
.first-point = Pierwszy punkt
|
||||
.freehand = Odręcznie
|
||||
.rectangular = Prostokątny
|
||||
.rectangle-center = Prostokąt - środek
|
||||
.rectangle-rotated = Prostokąt — obrócony
|
||||
.rectangle-two-corners = Prostokąt - dwa narożniki
|
||||
|
|
@ -1347,6 +1385,19 @@ draw =
|
|||
.section-offset-along-z = Przesunięcie SECTION wzdłuż Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Opuść pióro — przejdź do szkicu, kliknij, aby podnieść, Enter, aby nagrać:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pióro w górę — kliknij, aby opuścić pióro, Enter, aby nagrać:
|
||||
.sketch-pen = Pióro
|
||||
.sketch-increment = Przyrost
|
||||
.sketch-record = Zapisz
|
||||
.sketch-stop-erasing = Zatrzymaj wymazywanie
|
||||
.sketch-connect = Połącz
|
||||
.sketch-exit = Zakończ
|
||||
.sketch-quit = Porzuć
|
||||
.sketch-object-type = SKETCH Typ obiektu [Linia/Polilinia/Spline]:
|
||||
.sketch-record-increment = SKETCH Przyrost zapisu <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolerancja dopasowania spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Wymazywanie — przesuń się wstecz po szkicu tymczasowym:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, przyrost __ocs_arg_increment__) Pióro opuszczone — przesuń, aby szkicować:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, przyrost __ocs_arg_increment__) Pióro podniesione — kliknij, aby opuścić:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = Oś płaszczyzny cięcia SLICE [X / Y / Z] (dodaj TOP/BOTTOM wpisując):
|
||||
.slice-offset-along-x = Przesunięcie SLICE wzdłuż X:
|
||||
.slice-offset-along-y = Przesunięcie SLICE wzdłuż Y:
|
||||
|
|
@ -1378,11 +1429,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Zastosowanie: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Sposób użycia: POLYSOLID <width> <height> (najpierw wybierz polilinię)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Zastosowanie: PYRAMID <radius> <height> [sides] (domyślnie 4 strony)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Podaj pierwszy punkt lub [Polyline]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Podaj pierwszy punkt lub [Ramki/Polilinia] <Polilinia>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Określ następny punkt lub [Cofnij/Zamknij] (punkty __ocs_arg_n__):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polilinia Wybierz zamkniętą planarną polilinię:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Prostokątny Określ pierwszy narożnik:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Prostokątny Określ przeciwny narożnik:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Ramki Wprowadź tryb ramki [Wyłącz/Włącz/WyświetlBezDrukowania] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Polilinia Usunąć polilinię źródłową? [Tak/Nie] <Nie>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Polilinia: wybierz prostą, zamkniętą, płaską polilinię 2D z co najmniej 3 nieprzecinającymi się wierzchołkami.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ wymaga wartości 0, 1 lub 2.
|
||||
.frames = Ramki
|
||||
.display-but-not-plot = Wyświetlaj, ale nie drukuj
|
||||
.frames-on-not-plotted = Ramki włączone, niedrukowane
|
||||
.image-adjust = Dopasowanie obrazu
|
||||
.x-ray = X-Ray
|
||||
.xline-specify-a-point = XLINE Określ punkt:
|
||||
.xline-specify-through-point = XLINE Określ punkt przelotowy:
|
||||
|
|
@ -1975,6 +2034,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = Obiekt(y) __ocs_fmt_0__ wycięty do schowka.
|
||||
|
||||
model =
|
||||
.create = Utwórz
|
||||
.boolean = Operacje boolowskie
|
||||
.pyramid = Ostrosłup
|
||||
.polysolid = Bryła wielosegmentowa
|
||||
.extrude = Wyciągnij
|
||||
.revolve = Obróć
|
||||
.loft = Loft
|
||||
.sweep = Przeciągnij
|
||||
.presspull = Naciśnij/wyciągnij
|
||||
.n-specify-center-point = __ocs_arg_n__ Określ punkt środkowy:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Określ pierwszy narożnik:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Podaj wysokość <Enter for default>:
|
||||
|
|
@ -1987,6 +2055,11 @@ model =
|
|||
.cv-hull = Kadłub CV
|
||||
.class-entity =Klasa/jednostka
|
||||
.curved = Zakrzywiony
|
||||
.cylindrical = Cylindryczny
|
||||
.inverted-curved = Odwrócony zakrzywiony
|
||||
.inverted-cylindrical = Odwrócony cylindryczny
|
||||
.inverted-hemispherical = Odwrócony półkulisty
|
||||
.inverted-spherical = Odwrócony kulisty
|
||||
.data-size = Rozmiar danych
|
||||
.database-curves = Krzywe bazy danych
|
||||
.design = Projekt
|
||||
|
|
@ -2270,6 +2343,7 @@ view =
|
|||
.zoom-extents =
|
||||
Powiększ
|
||||
Zakresy
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: brak obiekt├│w kreskowania.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: brak obiektów tekstowych i wymiarowych.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: jeszcze nie wdrożony.
|
||||
.viewport-s-in-layout =Okna widokowe __ocs_fmt_0__ w układzie „__ocs_fmt_1__”:
|
||||
|
|
@ -3009,7 +3083,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nowa wartość [0=dot 1=none 2=+ 3=x 4=tick; +32 koło +64 kwadraty]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE ustawione na __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nowy rozmiar punktu (0 = 5% rzutni, <0 = bezwzględnie):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nowy rozmiar punktu (0 = 5% rzutni, >0 = bezwzględnie, <0 = procent rzutni):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE ustawione na __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Desenhar
|
||||
.annotate = Anotar
|
||||
.insert = Inserir
|
||||
.model = Modelo
|
||||
.model = Modelagem
|
||||
.layout = Layout
|
||||
.manage = Gerenciar
|
||||
.view = Exibir
|
||||
|
|
@ -314,6 +314,7 @@ common =
|
|||
.block-rotation = Rotação do bloco
|
||||
.blocks = Blocos
|
||||
.blue = Azul
|
||||
.bulge = Curvatura
|
||||
.boolean-failed-the-solids-may-not-overlap = A operação booleana falhou — os sólidos podem não se sobrepor.
|
||||
.boolean-select-exactly-two-solids-created-this = Operação booleana: selecione exatamente dois sólidos criados nesta sessão.
|
||||
.bottom = Inferior
|
||||
|
|
@ -1329,6 +1330,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Especifique o ponto de passagem:
|
||||
.rect-specify-first-corner = RECT Especifique o primeiro canto:
|
||||
.rect-specify-opposite-corner = RECT Especifique o canto oposto:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Calcule as dimensões com base em [Comprimento / Largura] <Comprimento>:
|
||||
.rect-specify-elevation = RECT Especifique a elevação <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Especifique o raio de concordância <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT Especifique a primeira distância do chanfro <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT Especifique a orientação a partir do primeiro canto:
|
||||
.rect-specify-rectangle-area = RECT Especifique a área do retângulo:
|
||||
.rect-specify-rectangle-length = RECT Especifique o comprimento do retângulo:
|
||||
.rect-specify-rectangle-width = RECT Especifique a largura do retângulo:
|
||||
.rect-specify-rotation-angle = RECT Especifique o ângulo de rotação <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT Especifique a segunda distância do chanfro <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Especifique a espessura <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Especifique a largura <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Especifique o ponto central:
|
||||
.rect-cen-specify-corner-point = RECT CEN Especifique o ponto de canto:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Especifique o canto adjacente (define a direção da aresta):
|
||||
|
|
@ -1338,6 +1351,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: selecione polilinhas fechadas ou círculos.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Especifique o próximo ponto (__ocs_arg_count__ pontos, Enter para fechar):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Especifique o ponto inicial (comprimento do arco = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD Primeiro canto (__ocs_arg_style__, comprimento do arco __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Canto oposto: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Ponto inicial poligonal (__ocs_arg_style__, comprimento do arco __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Próximo ponto poligonal (__ocs_arg_count__ pontos, Enter para fechar): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Primeiro ponto à mão livre (__ocs_arg_style__, comprimento do arco __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD Guie o cursor e clique ou pressione Enter para fechar: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Comprimento aproximado da corda do arco <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Estilo [Normal/Caligrafia] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Selecione um círculo, elipse, polilinha ou spline fechados: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Inverter direção do arco [Sim/Não] <Não>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Selecione uma polilinha de nuvem de revisão fechada perto do início da substituição: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Ponto de substituição __ocs_arg_count__; termine em outro vértice da nuvem:
|
||||
.revcloud-pick-side = REVCLOUD Escolha o lado da nuvem original a apagar:
|
||||
.revcloud-path-cannot-form = O caminho selecionado não pode formar uma nuvem de revisão.
|
||||
.revcloud-select-closed-revision-cloud = Selecione uma polilinha de nuvem de revisão fechada.
|
||||
.revcloud-polyline-not-planar = A polilinha selecionada não é plana.
|
||||
.revcloud-corners-non-zero-area = Os dois cantos devem definir uma área diferente de zero.
|
||||
.revcloud-arc-length-positive = O comprimento do arco deve ser maior que zero.
|
||||
.revcloud-enter-valid-arc-length = Digite um comprimento de arco válido.
|
||||
.revcloud-select-supported-closed-curve = Selecione uma curva fechada compatível.
|
||||
.revcloud-select-closed-entity = Selecione um círculo, elipse, polilinha ou spline fechados.
|
||||
.calligraphy = Caligrafia
|
||||
.first-point = Primeiro ponto
|
||||
.freehand = Mão livre
|
||||
.rectangular = Retangular
|
||||
.rectangle-center = Retângulo - Centro
|
||||
.rectangle-rotated = Retângulo - Girado
|
||||
.rectangle-two-corners = Retângulo - Dois cantos
|
||||
|
|
@ -1348,6 +1386,19 @@ draw =
|
|||
.section-offset-along-z = SECTION deslocamento ao longo de Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Caneta abaixada — mova para esboçar, clique para levantar, Enter para registrar:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Caneta levantada — clique para abaixar, Enter para registrar:
|
||||
.sketch-pen = Caneta
|
||||
.sketch-increment = Incremento
|
||||
.sketch-record = Registrar
|
||||
.sketch-stop-erasing = Parar de apagar
|
||||
.sketch-connect = Conectar
|
||||
.sketch-exit = Sair
|
||||
.sketch-quit = Descartar
|
||||
.sketch-object-type = SKETCH Tipo de objeto [Linha/Polilinha/Spline]:
|
||||
.sketch-record-increment = SKETCH Incremento de registro <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolerância de ajuste da spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Apagar — mova para trás sobre o esboço temporário:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Caneta abaixada — mova para esboçar:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Caneta levantada — clique para abaixar:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE eixo do plano de corte [X / Y / Z] (digite TOP/BOTTOM para adicionar):
|
||||
.slice-offset-along-x = SLICE deslocamento ao longo de X:
|
||||
.slice-offset-along-y = SLICE deslocamento ao longo de Y:
|
||||
|
|
@ -1379,11 +1430,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Uso: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Uso: POLYSOLID <largura> <altura> (selecione primeiro uma polilinha)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Uso: PYRAMID <raio> <altura> [lados] (padrão: 4 lados)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Especifique o primeiro ponto ou [Polilinha]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Especifique o primeiro ponto ou [Molduras/Polilinha] <Polilinha>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Especifique o próximo ponto ou [Desfazer/Fechar] (__ocs_arg_n__ pontos):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Polilinha Selecione uma polilinha planar fechada:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Retangular Especifique o primeiro canto:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Retangular Especifique o canto oposto:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Molduras Informe o modo da moldura [Desativado/Ativado/ExibirSemPlotar] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Polilinha Apagar a polilinha de origem? [Sim/Não] <Não>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Polilinha: selecione uma polilinha 2D reta, fechada e planar com pelo menos 3 vértices sem interseção.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ requer 0, 1 ou 2.
|
||||
.frames = Molduras
|
||||
.display-but-not-plot = Exibir, mas não plotar
|
||||
.frames-on-not-plotted = Molduras ativadas, não plotadas
|
||||
.image-adjust = Ajuste de imagem
|
||||
.x-ray = Raio X
|
||||
.xline-specify-a-point = XLINE Especifique um ponto:
|
||||
.xline-specify-through-point = XLINE Especifique o ponto de passagem:
|
||||
|
|
@ -1976,6 +2035,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ objetos recortados para a área de transferência.
|
||||
|
||||
model =
|
||||
.create = Criar
|
||||
.boolean = Operações booleanas
|
||||
.pyramid = Pirâmide
|
||||
.polysolid = Polissólido
|
||||
.extrude = Extrusão
|
||||
.revolve = Revolução
|
||||
.loft = Loft
|
||||
.sweep = Varredura
|
||||
.presspull = Pressionar/puxar
|
||||
.n-specify-center-point = __ocs_arg_n__ Especifique o ponto central:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Especifique o primeiro canto:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Especifique a altura <Enter para o padrão>:
|
||||
|
|
@ -1988,6 +2056,11 @@ model =
|
|||
.cv-hull = Casco de VC
|
||||
.class-entity = Classe / Entidade
|
||||
.curved = Curvo
|
||||
.cylindrical = Cilíndrico
|
||||
.inverted-curved = Curvo invertido
|
||||
.inverted-cylindrical = Cilíndrico invertido
|
||||
.inverted-hemispherical = Hemisférico invertido
|
||||
.inverted-spherical = Esférico invertido
|
||||
.data-size = Tamanho dos dados
|
||||
.database-curves = Curvas do banco de dados
|
||||
.design = Projeto
|
||||
|
|
@ -2271,6 +2344,7 @@ view =
|
|||
.zoom-extents =
|
||||
Zoom nas
|
||||
extensões
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: nenhum objeto de hachura.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: nenhum objeto de texto ou cota.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: ainda não implementado.
|
||||
.viewport-s-in-layout = __ocs_fmt_0__ janelas de visualização no layout “__ocs_fmt_1__”:
|
||||
|
|
@ -3010,7 +3084,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE novo valor [0=ponto 1=nenhum 2=+ 3=x 4=marca; +32 círculo +64 quadrado]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE definido como __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE novo tamanho do ponto (0 = 5% da janela de visualização, <0 = absoluto):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE novo tamanho do ponto (0 = 5% da janela de visualização, >0 = absoluto, <0 = porcentagem da janela de visualização):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE definido como __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Начертить
|
||||
.annotate = Аннотации
|
||||
.insert = Вставка
|
||||
.model = Модель
|
||||
.model = Моделирование
|
||||
.layout = Расположение
|
||||
.manage = Управление
|
||||
.view = Просмотр
|
||||
|
|
@ -317,6 +317,7 @@ common =
|
|||
.block-rotation = Вращение блока
|
||||
.blocks = Блоки
|
||||
.blue = Синий
|
||||
.bulge = Выпуклость
|
||||
.boolean-failed-the-solids-may-not-overlap = Булевый не сработал — твердые вещества могут не перекрываться.
|
||||
.boolean-select-exactly-two-solids-created-this = Булев: выберите ровно два твердых тела, созданных на этом сеансе.
|
||||
.bottom = Снизу
|
||||
|
|
@ -1332,6 +1333,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Укажите через точку:
|
||||
.rect-specify-first-corner = RECT Укажите первый угол:
|
||||
.rect-specify-opposite-corner = RECT Укажите противоположный угол:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Рассчитать размеры по [Длине / Ширине] <Длина>:
|
||||
.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 Укажите смежный угол (определите направление кромки):
|
||||
|
|
@ -1341,6 +1354,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: выберите замкнутые полилины или окружности.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Укажите следующую точку (__ocs_arg_count__ pts, Введите, чтобы закрыть):
|
||||
.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 Стиль [Обычный/Каллиграфия] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Выберите замкнутую окружность, эллипс, полилинию или сплайн: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Обратить направление дуг [Да/Нет] <Нет>:
|
||||
.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 = Выберите замкнутую окружность, эллипс, полилинию или сплайн.
|
||||
.calligraphy = Каллиграфия
|
||||
.first-point = Первая точка
|
||||
.freehand = От руки
|
||||
.rectangular = Прямоугольный
|
||||
.rectangle-center = Прямоугольник - центр
|
||||
.rectangle-rotated = Прямоугольник - повернутый
|
||||
.rectangle-two-corners = Прямоугольник - два угла
|
||||
|
|
@ -1351,6 +1389,19 @@ draw =
|
|||
.section-offset-along-z = SECTION Смещение вдоль Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Нажмите вниз — перейдите к эскизу, нажмите, чтобы поднять, Введите, чтобы записать:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pen up — нажмите, чтобы опустить ручку, Введите для записи:
|
||||
.sketch-pen = Перо
|
||||
.sketch-increment = Шаг
|
||||
.sketch-record = Записать
|
||||
.sketch-stop-erasing = Остановить стирание
|
||||
.sketch-connect = Соединить
|
||||
.sketch-exit = Завершить
|
||||
.sketch-quit = Отменить
|
||||
.sketch-object-type = SKETCH Тип объекта [Отрезок/Полилиния/Сплайн]:
|
||||
.sketch-record-increment = SKETCH Шаг записи <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Допуск аппроксимации сплайна <__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:
|
||||
|
|
@ -1382,11 +1433,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Использование: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Использование: POLYSOLID <width> <height> (select a polyline first)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Использование: PYRAMID <radius> <height> [sides] (default 4 sides)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Укажите первую точку или [Полилайн]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT Укажите первую точку или [Рамки/Полилиния] <Полилиния>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Укажите следующую точку или [Отменить / Замкнуть] (точек: __ocs_arg_n__):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Выберите замкнутый плоский полилин:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Прямоугольный Укажите первый угол:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Прямоугольный Укажите противоположный угол:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Рамки Введите режим рамки [Выкл/Вкл/ПоказатьНеПечатать] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Полилиния Удалить исходную полилинию? [Да/Нет] <Нет>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Полиния: выберите прямую, замкнутую, плоскую 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 = Рентгеновский
|
||||
.xline-specify-a-point = XLINE Укажите точку:
|
||||
.xline-specify-through-point = XLINE Укажите через точку:
|
||||
|
|
@ -1979,6 +2038,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = В буфер обмена вырезано объектов: __ocs_fmt_0__.
|
||||
|
||||
model =
|
||||
.create = Создание
|
||||
.boolean = Булевы операции
|
||||
.pyramid = Пирамида
|
||||
.polysolid = Политело
|
||||
.extrude = Выдавливание
|
||||
.revolve = Вращение
|
||||
.loft = Лофт
|
||||
.sweep = Сдвиг по траектории
|
||||
.presspull = Нажать/вытянуть
|
||||
.n-specify-center-point = __ocs_arg_n__ Укажите центральную точку:
|
||||
.n-specify-first-corner = __ocs_arg_n__ Укажите первый угол:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Укажите высоту <Введите по умолчанию>:
|
||||
|
|
@ -1991,6 +2059,11 @@ model =
|
|||
.cv-hull = CV Корпус
|
||||
.class-entity = Класс / Объект
|
||||
.curved = искривленный
|
||||
.cylindrical = цилиндрический
|
||||
.inverted-curved = инвертированный искривленный
|
||||
.inverted-cylindrical = инвертированный цилиндрический
|
||||
.inverted-hemispherical = инвертированный полусферический
|
||||
.inverted-spherical = инвертированный сферический
|
||||
.data-size = Размер данных
|
||||
.database-curves = Кривые базы данных
|
||||
.design = Дизайн
|
||||
|
|
@ -2274,6 +2347,7 @@ view =
|
|||
.zoom-extents =
|
||||
Зум
|
||||
размеры
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: ð¢ðÁÐé ð¥ð▒ÐèðÁð║Ðéð¥ð▓ ÐêÐéÐÇð©Ðàð¥ð▓ð║ð©.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: нет текстовых или размерных объектов.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: еще не реализовано.
|
||||
.viewport-s-in-layout = Видовые экраны (__ocs_fmt_0__) на листе «__ocs_fmt_1__»:
|
||||
|
|
@ -3013,7 +3087,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE Новое значение [0=точка 1=нет 2=+ 3=× 4=штрих; +32 окружность +64 квадрат]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE, установленный на __ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE новый размер точки (0 = 5% от площади обзора, <0 = абсолютный):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE новый размер точки (0 = 5% от области просмотра, >0 = абсолютный, <0 = процент области просмотра):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE, установленный на __ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = Çizim
|
||||
.annotate = Açıklama
|
||||
.insert = Ekle
|
||||
.model = Model
|
||||
.model = Modelleme
|
||||
.layout = Yerleşim
|
||||
.manage = Yönet
|
||||
.view = Görünüm
|
||||
|
|
@ -300,6 +300,7 @@ common =
|
|||
.block-rotation = Blok döndürmesi
|
||||
.blocks = Bloklar
|
||||
.blue = Mavi
|
||||
.bulge = Kavis
|
||||
.boolean-failed-the-solids-may-not-overlap = Boolean işlemi başarısız oldu; katılar çakışmıyor olabilir.
|
||||
.boolean-select-exactly-two-solids-created-this = Boolean: bu oturumda oluşturulmuş tam iki katı seçin.
|
||||
.bottom = Alt
|
||||
|
|
@ -1151,6 +1152,15 @@ draw =
|
|||
.tag-val = [__ocs_fmt_0__] = __ocs_fmt_1__
|
||||
.no-closed-boundary-found = ⚠ Kapalı sınır bulunamadı.
|
||||
.inverted = , ters çevrilmiş
|
||||
.loaded-multiline-styles = Yüklü çoklu çizgi stilleri: __ocs_fmt_0__
|
||||
.mline-current-settings = MLINE Geçerli ayarlar: Hizalama = __ocs_fmt_0__, Ölçek = __ocs_fmt_1__, Stil = __ocs_fmt_2__
|
||||
Başlangıç noktasını belirtin veya [Hizalama/Ölçek/Stil]:
|
||||
.mline-enter-justification = MLINE Hizalama türünü girin [Üst/Sıfır/Alt] <__ocs_fmt_0__>:
|
||||
.mline-enter-scale-factor-default = MLINE Ölçek katsayısını girin <__ocs_fmt_0__>:
|
||||
.mline-enter-style-name = MLINE Stil adını veya [?] girin <__ocs_fmt_0__>:
|
||||
.mline-specify-next-point-close-undo = MLINE Sonraki noktayı belirtin veya [Kapat/Geri al]:
|
||||
.mline-specify-next-point-undo = MLINE Sonraki noktayı belirtin veya [Geri al]:
|
||||
.multiline-style-was-not-found = "__ocs_arg_style__" çoklu çizgi stili bulunamadı.
|
||||
.value-2-point = 2 Nokta
|
||||
.value-2d-solid = 2B Katı
|
||||
.value-3-point = 3 Nokta
|
||||
|
|
@ -1311,6 +1321,18 @@ draw =
|
|||
.ray-specify-through-point = RAY Geçiş noktasını belirtin:
|
||||
.rect-specify-first-corner = RECT İlk köşeyi belirtin:
|
||||
.rect-specify-opposite-corner = RECT Karşı köşeyi belirtin:
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT Boyutları [Uzunluk / Genişlik] temelinde hesaplayın <Uzunluk>:
|
||||
.rect-specify-elevation = RECT Kotu belirtin <__ocs_fmt_0__>:
|
||||
.rect-specify-fillet-radius = RECT Yuvarlatma yarıçapını belirtin <__ocs_fmt_0__>:
|
||||
.rect-specify-first-chamfer-distance = RECT İlk pah mesafesini belirtin <__ocs_fmt_0__>:
|
||||
.rect-specify-orientation-from-the-first-corner = RECT İlk köşeden yönü belirtin:
|
||||
.rect-specify-rectangle-area = RECT Dikdörtgen alanını belirtin:
|
||||
.rect-specify-rectangle-length = RECT Dikdörtgen uzunluğunu belirtin:
|
||||
.rect-specify-rectangle-width = RECT Dikdörtgen genişliğini belirtin:
|
||||
.rect-specify-rotation-angle = RECT Döndürme açısını belirtin <__ocs_fmt_0__>:
|
||||
.rect-specify-second-chamfer-distance = RECT İkinci pah mesafesini belirtin <__ocs_fmt_0__>:
|
||||
.rect-specify-thickness = RECT Kalınlığı belirtin <__ocs_fmt_0__>:
|
||||
.rect-specify-width = RECT Genişliği belirtin <__ocs_fmt_0__>:
|
||||
.rect-cen-specify-center-point = RECT CEN Merkez noktasını belirtin:
|
||||
.rect-cen-specify-corner-point = RECT CEN Köşe noktasını belirtin:
|
||||
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Bitişik köşeyi belirtin (kenar yönünü tanımlar):
|
||||
|
|
@ -1320,6 +1342,31 @@ draw =
|
|||
.region-select-closed-polylines-or-circles = REGION: Kapalı çoklu çizgileri veya çemberleri seçin.
|
||||
.revcloud-specify-next-point-count-pts-enter-to = REVCLOUD Sonraki noktayı belirtin (__ocs_arg_count__ nokta, kapatmak için Enter):
|
||||
.revcloud-specify-start-point-arc-length-arc-le = REVCLOUD Başlangıç noktasını belirtin (yay uzunluğu = __ocs_arg_arc_length__):
|
||||
.revcloud-first-corner = REVCLOUD İlk köşe (__ocs_arg_style__, yay uzunluğu __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-opposite-corner = REVCLOUD Karşı köşe: __ocs_arg_message__
|
||||
.revcloud-polygonal-start-point = REVCLOUD Çokgensel başlangıç noktası (__ocs_arg_style__, yay uzunluğu __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-next-polygonal-point = REVCLOUD Sonraki çokgensel nokta (__ocs_arg_count__ nokta, kapatmak için Enter): __ocs_arg_message__
|
||||
.revcloud-freehand-first-point = REVCLOUD Serbest çizim ilk noktası (__ocs_arg_style__, yay uzunluğu __ocs_arg_length__): __ocs_arg_message__
|
||||
.revcloud-guide-cursor = REVCLOUD İmleci yönlendirin, ardından kapatmak için tıklayın veya Enter'a basın: __ocs_arg_message__
|
||||
.revcloud-approximate-arc-chord-length = REVCLOUD Yaklaşık yay kirişi uzunluğu <__ocs_arg_length__>:
|
||||
.revcloud-style = REVCLOUD Stil [Normal/Kaligrafi] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD Kapalı bir çember, elips, çoklu çizgi veya eğri seçin: __ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD Yay yönü ters çevrilsin mi [Evet/Hayır] <Hayır>:
|
||||
.revcloud-select-revision-cloud = REVCLOUD Değiştirmenin başlangıcına yakın kapalı bir revizyon bulutu çoklu çizgisi seçin: __ocs_arg_message__
|
||||
.revcloud-replacement-point = REVCLOUD Değiştirme noktası __ocs_arg_count__; başka bir bulut köşesinde bitirin:
|
||||
.revcloud-pick-side = REVCLOUD Özgün bulutun silinecek tarafını seçin:
|
||||
.revcloud-path-cannot-form = Seçilen yoldan revizyon bulutu oluşturulamıyor.
|
||||
.revcloud-select-closed-revision-cloud = Kapalı bir revizyon bulutu çoklu çizgisi seçin.
|
||||
.revcloud-polyline-not-planar = Seçilen çoklu çizgi düzlemsel değil.
|
||||
.revcloud-corners-non-zero-area = İki köşe sıfırdan farklı bir alan tanımlamalıdır.
|
||||
.revcloud-arc-length-positive = Yay uzunluğu sıfırdan büyük olmalıdır.
|
||||
.revcloud-enter-valid-arc-length = Geçerli bir yay uzunluğu girin.
|
||||
.revcloud-select-supported-closed-curve = Desteklenen kapalı bir eğri seçin.
|
||||
.revcloud-select-closed-entity = Kapalı bir çember, elips, çoklu çizgi veya eğri seçin.
|
||||
.calligraphy = Kaligrafi
|
||||
.first-point = İlk nokta
|
||||
.freehand = Serbest çizim
|
||||
.rectangular = Dikdörtgensel
|
||||
.rectangle-center = Dikdörtgen - Merkez
|
||||
.rectangle-rotated = Dikdörtgen - Döndürülmüş
|
||||
.rectangle-two-corners = Dikdörtgen - İki Köşe
|
||||
|
|
@ -1330,6 +1377,19 @@ draw =
|
|||
.section-offset-along-z = SECTION Z boyunca ofset:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Kalem aşağıda — çizmek için hareket ettirin, kaldırmak için tıklayın, kaydetmek için Enter:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Kalem yukarıda — indirmek için tıklayın, kaydetmek için Enter:
|
||||
.sketch-pen = Kalem
|
||||
.sketch-increment = Artış
|
||||
.sketch-record = Kaydet
|
||||
.sketch-stop-erasing = Silmeyi durdur
|
||||
.sketch-connect = Bağla
|
||||
.sketch-exit = Çıkış
|
||||
.sketch-quit = Vazgeç
|
||||
.sketch-object-type = SKETCH Nesne türü [Çizgi/Çoklu çizgi/Spline]:
|
||||
.sketch-record-increment = SKETCH Kayıt artışı <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline uyum toleransı <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Sil — geçici çizim üzerinde geriye doğru hareket edin:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, artış __ocs_arg_increment__) Kalem aşağıda — çizmek için hareket ettirin:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, artış __ocs_arg_increment__) Kalem yukarıda — indirmek için tıklayın:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE Kesme düzlemi ekseni [X / Y / Z] (TOP/BOTTOM yazarak ekleyin):
|
||||
.slice-offset-along-x = SLICE X boyunca ofset:
|
||||
.slice-offset-along-y = SLICE Y boyunca ofset:
|
||||
|
|
@ -1339,6 +1399,7 @@ draw =
|
|||
.solid-specify-second-point = SOLID İkinci noktayı belirtin:
|
||||
.solid-specify-third-point = SOLID Üçüncü noktayı belirtin:
|
||||
.spline-specify-first-control-point = SPLINE İlk kontrol noktasını belirtin:
|
||||
.spline-specify-first-point = SPLINE İlk noktayı belirtin:
|
||||
.spline-specify-next-point-n-pts = SPLINE Sonraki noktayı belirtin [__ocs_arg_n__ nokta]:
|
||||
.sketch = Serbest Çizim
|
||||
.start-center-angle = Başlangıç, Merkez, Açı
|
||||
|
|
@ -1361,11 +1422,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = Kullanım: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = Kullanım: POLYSOLID <width> <height> (önce bir çoklu çizgi seçin)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = Kullanım: PYRAMID <radius> <height> [sides] (varsayılan 4 kenar)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT İlk noktayı belirtin veya [Çoklu Çizgi]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT İlk noktayı belirtin veya [Çerçeveler/Çoklu Çizgi] <Çoklu Çizgi>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT Sonraki noktayı belirtin veya [Geri Al/Kapat] (__ocs_arg_n__ nokta):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT Çoklu Çizgi Kapalı düzlemsel bir çoklu çizgi seçin:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT Dikdörtgen İlk köşeyi belirtin:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT Dikdörtgen Karşı köşeyi belirtin:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT Çerçeveler Çerçeve modunu girin [Kapalı/Açık/GösterAmaÇizdirme] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT Çoklu Çizgi Kaynak çoklu çizgi silinsin mi? [Evet/Hayır] <Hayır>:
|
||||
.wipeout-polyline-invalid = WIPEOUT Çoklu Çizgi: en az 3 kesişmeyen köşesi olan düz, kapalı, düzlemsel bir 2B çoklu çizgi seçin.
|
||||
.setvar-frame-range = SETVAR: __ocs_fmt_0__ için 0, 1 veya 2 gerekir.
|
||||
.frames = Çerçeveler
|
||||
.display-but-not-plot = Göster ama çizdirme
|
||||
.frames-on-not-plotted = Çerçeveler Açık, Çizdirilmez
|
||||
.image-adjust = Görüntü Ayarı
|
||||
.x-ray = X Işını
|
||||
.xline-specify-a-point = XLINE Bir nokta belirtin:
|
||||
.xline-specify-through-point = XLINE Geçiş noktasını belirtin:
|
||||
|
|
@ -1374,6 +1443,24 @@ draw =
|
|||
modify =
|
||||
.count-objects = (__ocs_arg_count__ nesne)
|
||||
.edge-extend-indented-title-case = [Kenar: Uzat]
|
||||
.add-vertex = Tepe Noktası Ekle
|
||||
.closed-cross = Kapalı Kesişim
|
||||
.closed-tee = Kapalı T Birleşimi
|
||||
.corner-joint = Köşe Birleşimi
|
||||
.cut-all = Tümünü Kes
|
||||
.cut-single = Tekini Kes
|
||||
.delete-vertex = Tepe Noktasını Sil
|
||||
.merged-cross = Birleşik Kesişim
|
||||
.merged-tee = Birleşik T Birleşimi
|
||||
.mledit-choose-an-edit-tool = MLEDIT Bir düzenleme aracı seçin:
|
||||
.mledit-select-first-multiline = MLEDIT İlk çoklu çizgiyi seçin:
|
||||
.mledit-select-second-multiline = MLEDIT İkinci çoklu çizgiyi seçin:
|
||||
.mledit-specify-end-of-weld-range = MLEDIT Kaynak aralığının sonunu belirtin:
|
||||
.mledit-specify-second-cut-point = MLEDIT İkinci kesim noktasını belirtin:
|
||||
.open-cross = Açık Kesişim
|
||||
.open-tee = Açık T Birleşimi
|
||||
.remove-vertex = Tepe Noktasını Kaldır
|
||||
.weld-all = Tümünü Birleştir
|
||||
.align-scale-objects-based-on-alignment-points = ALIGN Nesneler hizalama noktalarına göre ölçeklensin mi? [E/H]:
|
||||
.align-select-objects-count-selected-enter-when = ALIGN Nesneleri seçin (__ocs_arg_count__ seçili, bitirmek için Enter):
|
||||
.align-specify-1st-destination-point = ALIGN 1. hedef noktasını belirtin:
|
||||
|
|
@ -1936,6 +2023,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ nesne panoya kesildi.
|
||||
|
||||
model =
|
||||
.create = Oluştur
|
||||
.boolean = Boole işlemleri
|
||||
.pyramid = Piramit
|
||||
.polysolid = Çoklu katı
|
||||
.extrude = Katılaştır
|
||||
.revolve = Döndür
|
||||
.loft = Kesitler arası
|
||||
.sweep = Yol boyunca süpür
|
||||
.presspull = İt/Çek
|
||||
.n-specify-center-point = __ocs_arg_n__ Merkez noktasını belirtin:
|
||||
.n-specify-first-corner = __ocs_arg_n__ İlk köşeyi belirtin:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ Yüksekliği belirtin <varsayılan için Enter>:
|
||||
|
|
@ -1948,6 +2044,11 @@ model =
|
|||
.cv-hull = Kontrol Noktası Kabuğu
|
||||
.class-entity = Sınıf / Nesne
|
||||
.curved = Eğri
|
||||
.cylindrical = Silindirik
|
||||
.inverted-curved = Ters eğri
|
||||
.inverted-cylindrical = Ters silindirik
|
||||
.inverted-hemispherical = Ters yarım küresel
|
||||
.inverted-spherical = Ters küresel
|
||||
.data-size = Veri Boyutu
|
||||
.database-curves = Veritabanı Eğrileri
|
||||
.design = Tasarım
|
||||
|
|
@ -2211,6 +2312,7 @@ view =
|
|||
.zoom-window-specify-first-corner = ZOOM WINDOW İlk köşeyi belirtin:
|
||||
.zoom-window-specify-opposite-corner = ZOOM WINDOW Karşı köşeyi belirtin:
|
||||
.zoom-extents = Sınırlara Yakınlaştır
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__: Tarama nesnesi yok.
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__: Metin veya ölçü nesnesi yok.
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__: Henüz uygulanmadı.
|
||||
.viewport-s-in-layout = "__ocs_fmt_1__" yerleşiminde __ocs_fmt_0__ görünüm alanı:
|
||||
|
|
@ -2292,6 +2394,7 @@ properties =
|
|||
.chord-x = Kiriş X
|
||||
.chord-y = Kiriş Y
|
||||
.chord-z = Kiriş Z
|
||||
.chord = Kiriş
|
||||
.circumference = Çevre
|
||||
.class = Sınıf
|
||||
.class-id = Sınıf Kimliği
|
||||
|
|
@ -2318,6 +2421,10 @@ properties =
|
|||
.control-point-x = Kontrol Noktası X
|
||||
.control-point-y = Kontrol Noktası Y
|
||||
.control-point-z = Kontrol Noktası Z
|
||||
.control-point-x-sentence-case = Kontrol noktası X
|
||||
.control-point-y-sentence-case = Kontrol noktası Y
|
||||
.control-point-z-sentence-case = Kontrol noktası Z
|
||||
.control-vertices = Kontrol Köşeleri
|
||||
.coordination-model = Koordinasyon Modeli
|
||||
.creased-edges = Keskin Kenarlar
|
||||
.croppings = Kırpmalar
|
||||
|
|
@ -2325,6 +2432,9 @@ properties =
|
|||
.current-color-scheme = Geçerli Renk Düzeni
|
||||
.current-vertex-title-case = Geçerli Köşe
|
||||
.current-vertex-sentence-case = Geçerli köşe
|
||||
.current-control-point = Geçerli kontrol noktası
|
||||
.current-fit-point = Geçerli uydurma noktası
|
||||
.cv-frame = Kontrol çerçevesi
|
||||
.cutting-line = Kesme Çizgisi
|
||||
.dwg-payload = DWG Verisi
|
||||
.dwg-version = DWG Sürümü
|
||||
|
|
@ -2384,6 +2494,9 @@ properties =
|
|||
.end-tangent-x = Bitiş Teğeti X
|
||||
.end-tangent-y = Bitiş Teğeti Y
|
||||
.end-tangent-z = Bitiş Teğeti Z
|
||||
.end-tangent-vector-x = Bitiş teğet vektörü X
|
||||
.end-tangent-vector-y = Bitiş teğet vektörü Y
|
||||
.end-tangent-vector-z = Bitiş teğet vektörü Z
|
||||
.end-x = Bitiş X
|
||||
.end-y = Bitiş Y
|
||||
.end-z = Bitiş Z
|
||||
|
|
@ -2421,6 +2534,10 @@ properties =
|
|||
.fit-point-y = Uydurma Noktası Y
|
||||
.fit-point-z = Uydurma Noktası Z
|
||||
.fit-tolerance = Uydurma Toleransı
|
||||
.fit-point-x-sentence-case = Uydurma noktası X
|
||||
.fit-point-y-sentence-case = Uydurma noktası Y
|
||||
.fit-point-z-sentence-case = Uydurma noktası Z
|
||||
.fit-tolerance-sentence-case = Uydurma toleransı
|
||||
.fit-smooth-title-case = Uydur/Yumuşat
|
||||
.fit-smooth-sentence-case = Uydur/yumuşat
|
||||
.fixed-elevation-range = Sabit Kot Aralığı
|
||||
|
|
@ -2466,6 +2583,7 @@ properties =
|
|||
.jog-z = Kırılma Z
|
||||
.justify = Hizala
|
||||
.knot-parameterization = Düğüm Parametrelemesi
|
||||
.knot-parameterization-sentence-case = Düğüm parametrelemesi
|
||||
.lamp-preset = Lamba Ön Ayarı
|
||||
.landing-gap = Yatay Bağlantı Boşluğu
|
||||
.layout-print-configuration = Yerleşim Yazdırma Yapılandırması
|
||||
|
|
@ -2558,6 +2676,8 @@ properties =
|
|||
.number-of-grips = Tutamaç Sayısı
|
||||
.number-of-vertices = Köşe Sayısı
|
||||
.number-of-turns = Sarım sayısı
|
||||
.number-of-control-points = Kontrol noktası sayısı
|
||||
.number-of-fit-points = Uydurma noktası sayısı
|
||||
.ole-frame = OLE Çerçevesi
|
||||
.object-references = Nesne Referansları
|
||||
.obliquing = Eğiklik
|
||||
|
|
@ -2677,6 +2797,13 @@ properties =
|
|||
.start-tangent-x = Başlangıç Teğeti X
|
||||
.start-tangent-y = Başlangıç Teğeti Y
|
||||
.start-tangent-z = Başlangıç Teğeti Z
|
||||
.start-tangent-vector-x = Başlangıç teğet vektörü X
|
||||
.start-tangent-vector-y = Başlangıç teğet vektörü Y
|
||||
.start-tangent-vector-z = Başlangıç teğet vektörü Z
|
||||
.hide = Gizle
|
||||
.periodic = Periyodik
|
||||
.square-root = Karekök
|
||||
.uniform = Eşit Aralıklı
|
||||
.start-x = Başlangıç X
|
||||
.start-y = Başlangıç Y
|
||||
.start-z = Başlangıç Z
|
||||
|
|
@ -2950,7 +3077,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE Yeni değer [0=nokta 1=yok 2=+ 3=x 4=çentik; +32 çember +64 kare]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__
|
||||
.pdmode-set-to-v = PDMODE __ocs_fmt_0__ olarak ayarlandı
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Yeni nokta boyutu (0 = görünüm alanının %5'i, <0 = mutlak):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Yeni nokta boyutu (0 = görünüm alanının %5'i, >0 = mutlak, <0 = görünüm alanı yüzdesi):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__
|
||||
.pdsize-set-to-v-4 = PDSIZE __ocs_fmt_0__ olarak ayarlandı
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = 绘制
|
||||
.annotate = 注释
|
||||
.insert = 插入
|
||||
.model = 模型
|
||||
.model = 建模
|
||||
.layout = 布局
|
||||
.manage = 管理
|
||||
.view = 视图
|
||||
|
|
@ -301,6 +301,7 @@ common =
|
|||
.block-rotation = 块旋转
|
||||
.blocks = 块状物
|
||||
.blue = 蓝色
|
||||
.bulge = 凸度
|
||||
.boolean-failed-the-solids-may-not-overlap = 布尔失败——固件可能不会重叠.
|
||||
.boolean-select-exactly-two-solids-created-this = 布尔: 选择创建此会话的正中两个固件 。
|
||||
.bottom = 底部
|
||||
|
|
@ -1316,6 +1317,18 @@ draw =
|
|||
.ray-specify-through-point = RAY通过点指定 :
|
||||
.rect-specify-first-corner = RECT 指定第一角 :
|
||||
.rect-specify-opposite-corner = RECT 指定相向角 :
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT 根据 [长度 / 宽度] 计算尺寸 <长度>:
|
||||
.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 = RECTROT指定相邻的角( 定边方向) :
|
||||
|
|
@ -1325,6 +1338,31 @@ draw =
|
|||
.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 样式 [普通/书法] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD 选择闭合的圆、椭圆、多段线或样条曲线:__ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD 反转圆弧方向 [是/否] <否>:
|
||||
.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 = 选择闭合的圆、椭圆、多段线或样条曲线。
|
||||
.calligraphy = 书法
|
||||
.first-point = 第一个点
|
||||
.freehand = 徒手绘制
|
||||
.rectangular = 矩形
|
||||
.rectangle-center = 矩形 - 中心
|
||||
.rectangle-rotated = 矩形 - 旋转
|
||||
.rectangle-two-corners = 矩形 - 两点
|
||||
|
|
@ -1335,6 +1373,19 @@ draw =
|
|||
.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 对象类型 [直线/多段线/样条曲线]:
|
||||
.sketch-record-increment = SKETCH 记录增量 <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH 样条曲线拟合公差 <__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 页:1
|
||||
.slice-offset-along-y = SLICE 与 Y 相冲:
|
||||
|
|
@ -1366,11 +1417,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = 用法: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = 用法: POLYSOLID <width> <height> (select a polyline first)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = 用法: PYRAMID <radius> <height> [sides] (default 4 sides)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT 指定第一点或 [多段线]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT 指定第一点或 [边框/多段线] <多段线>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT 指定下一点或 [放弃/闭合](__ocs_arg_n__ 个点):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT 多段线 选择闭合平面多段线:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT 矩形 指定第一个角点:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT 矩形 指定对角点:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT 边框 输入边框模式 [关/开/显示但不打印] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT 多段线 是否删除源多段线?[是/否] <否>:
|
||||
.wipeout-polyline-invalid = WIPEOUT 多段线:请选择一条由至少 3 个不相交顶点组成的直线、闭合、平面 2D 多段线。
|
||||
.setvar-frame-range = SETVAR:__ocs_fmt_0__ 必须为 0、1 或 2。
|
||||
.frames = 边框
|
||||
.display-but-not-plot = 显示但不打印
|
||||
.frames-on-not-plotted = 边框开,不打印
|
||||
.image-adjust = 图像调整
|
||||
.x-ray = X 射线
|
||||
.xline-specify-a-point = XLINE 指定点:
|
||||
.xline-specify-through-point = XLINE 指定通过点:
|
||||
|
|
@ -1959,6 +2018,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ 对象切入剪贴板。
|
||||
|
||||
model =
|
||||
.create = 创建
|
||||
.boolean = 布尔运算
|
||||
.pyramid = 棱锥
|
||||
.polysolid = 多段体
|
||||
.extrude = 拉伸
|
||||
.revolve = 旋转
|
||||
.loft = 放样
|
||||
.sweep = 扫掠
|
||||
.presspull = 按拉
|
||||
.n-specify-center-point = __ocs_arg_n__ 指定中心点:
|
||||
.n-specify-first-corner = __ocs_arg_n__ 指定第一个角点:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ 指定高度(按 Enter 使用默认值):
|
||||
|
|
@ -1971,6 +2039,11 @@ model =
|
|||
.cv-hull = CV 外壳
|
||||
.class-entity = 类/实体
|
||||
.curved = 曲线
|
||||
.cylindrical = 圆柱形
|
||||
.inverted-curved = 反向曲线形
|
||||
.inverted-cylindrical = 反向圆柱形
|
||||
.inverted-hemispherical = 反向半球形
|
||||
.inverted-spherical = 反向球形
|
||||
.data-size = 数据大小
|
||||
.database-curves = 数据库曲线
|
||||
.design = 设计
|
||||
|
|
@ -2254,6 +2327,7 @@ view =
|
|||
.zoom-extents =
|
||||
缩放
|
||||
范围
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__:µ▓íµ£ëÕø¥µíêÕí½ÕààÕ»╣Þ▒íÒÇé
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__:没有文本或维度对象。
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__:尚未执行。
|
||||
.viewport-s-in-layout = 布局“__ocs_fmt_1__”中的视口(__ocs_fmt_0__ 个):
|
||||
|
|
@ -2993,7 +3067,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 新值 [0=点 1=无 2=+ 3=× 4=短划;+32 圆 +64 方框]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__ 时间轴 :
|
||||
.pdmode-set-to-v = PDMODE 设置为__ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 指定新点大小(0 = 视口的 5%,<0 = 绝对值):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 指定新点大小(0 = 视口的 5%,>0 = 绝对值,<0 = 视口百分比):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ 时间轴 :
|
||||
.pdsize-set-to-v-4 = PDSIZE 设置为__ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ ribbon-tab =
|
|||
.draw = 繪製
|
||||
.annotate = 註解
|
||||
.insert = 插入
|
||||
.model = 模型
|
||||
.model = 建模
|
||||
.layout = 配置
|
||||
.manage = 管理
|
||||
.view = 視圖
|
||||
|
|
@ -313,6 +313,7 @@ common =
|
|||
.block-rotation = 圖塊旋轉
|
||||
.blocks = 圖塊
|
||||
.blue = 藍色
|
||||
.bulge = 凸度
|
||||
.boolean-failed-the-solids-may-not-overlap = 布爾失敗——固件可能不會重疊.
|
||||
.boolean-select-exactly-two-solids-created-this = 布爾: 選擇建立此會話的正中兩個固件 。
|
||||
.bottom = 底部
|
||||
|
|
@ -1328,6 +1329,18 @@ draw =
|
|||
.ray-specify-through-point = RAY通過點指定 :
|
||||
.rect-specify-first-corner = RECT 指定第一角 :
|
||||
.rect-specify-opposite-corner = RECT 指定相向角 :
|
||||
.rect-calculate-dimensions-based-on-length-width = RECT 根據 [長度 / 寬度] 計算尺寸 <長度>:
|
||||
.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 = RECTROT指定相鄰的角( 定邊方向) :
|
||||
|
|
@ -1337,6 +1350,31 @@ draw =
|
|||
.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 樣式 [一般/書法] <__ocs_arg_style__>:
|
||||
.revcloud-select-closed-curve = REVCLOUD 選取閉合的圓、橢圓、多段線或雲形線:__ocs_arg_message__
|
||||
.revcloud-reverse-arc-direction = REVCLOUD 反轉圓弧方向 [是/否] <否>:
|
||||
.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 = 選取閉合的圓、橢圓、多段線或雲形線。
|
||||
.calligraphy = 書法
|
||||
.first-point = 第一個點
|
||||
.freehand = 徒手繪製
|
||||
.rectangular = 矩形
|
||||
.rectangle-center = 矩形 - 中心
|
||||
.rectangle-rotated = 矩形 - 旋轉
|
||||
.rectangle-two-corners = 矩形 - 兩點
|
||||
|
|
@ -1347,6 +1385,19 @@ draw =
|
|||
.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 物件類型 [直線/聚合線/雲形線]:
|
||||
.sketch-record-increment = SKETCH 記錄增量 <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH 雲形線擬合公差 <__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 頁:1
|
||||
.slice-offset-along-y = SLICE 與 Y 相衝:
|
||||
|
|
@ -1378,11 +1429,19 @@ draw =
|
|||
.usage-attdisp-on-off-normal = 用法: ATTDISP ON | OFF | NORMAL
|
||||
.usage-polysolid-width-height-select-a-polyline = 用法: POLYSOLID <width> <height> (select a polyline first)
|
||||
.usage-pyramid-radius-height-sides-default-4-si = 用法: PYRAMID <radius> <height> [sides] (default 4 sides)
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT 指定第一點或 [多段線]:
|
||||
.wipeout-specify-first-point-or-polyline = WIPEOUT 指定第一點或 [邊框/多段線] <多段線>:
|
||||
.wipeout-specify-next-point-or-undo-close-n-poi = WIPEOUT 指定下一點或 [放棄/閉合](__ocs_arg_n__ 個點):
|
||||
.wipeout-polyline-select-a-closed-planar-polyli = WIPEOUT 多段線 選擇閉合平面多段線:
|
||||
.wipeout-rectangular-specify-first-corner = WIPEOUT 矩形 指定第一個角點:
|
||||
.wipeout-rectangular-specify-opposite-corner = WIPEOUT 矩形 指定對角點:
|
||||
.wipeout-frames-enter-frame-mode = WIPEOUT 邊框 輸入邊框模式 [關/開/顯示但不列印] <__ocs_arg_mode__>:
|
||||
.wipeout-polyline-erase-source = WIPEOUT 多段線 是否刪除來源多段線?[是/否] <否>:
|
||||
.wipeout-polyline-invalid = WIPEOUT 多段線:請選擇一條由至少 3 個不相交頂點組成的直線、閉合、平面 2D 多段線。
|
||||
.setvar-frame-range = SETVAR:__ocs_fmt_0__ 必須為 0、1 或 2。
|
||||
.frames = 邊框
|
||||
.display-but-not-plot = 顯示但不列印
|
||||
.frames-on-not-plotted = 邊框開,不列印
|
||||
.image-adjust = 圖像調整
|
||||
.x-ray = X 射線
|
||||
.xline-specify-a-point = XLINE 指定點:
|
||||
.xline-specify-through-point = XLINE 指定通過點:
|
||||
|
|
@ -1971,6 +2030,15 @@ insert =
|
|||
.object-s-cut-to-clipboard = __ocs_fmt_0__ 物件切入剪貼簿。
|
||||
|
||||
model =
|
||||
.create = 建立
|
||||
.boolean = 布林運算
|
||||
.pyramid = 角錐體
|
||||
.polysolid = 多段實體
|
||||
.extrude = 擠出
|
||||
.revolve = 旋轉
|
||||
.loft = 放樣
|
||||
.sweep = 掃掠
|
||||
.presspull = 按拉
|
||||
.n-specify-center-point = __ocs_arg_n__ 指定中心點:
|
||||
.n-specify-first-corner = __ocs_arg_n__ 指定第一個角點:
|
||||
.n-specify-height-enter-for-default = __ocs_arg_n__ 指定高度(按 Enter 使用預設值):
|
||||
|
|
@ -1983,6 +2051,11 @@ model =
|
|||
.cv-hull = CV 外殼
|
||||
.class-entity = 類/實體
|
||||
.curved = 曲線
|
||||
.cylindrical = 圓柱形
|
||||
.inverted-curved = 反向曲線形
|
||||
.inverted-cylindrical = 反向圓柱形
|
||||
.inverted-hemispherical = 反向半球形
|
||||
.inverted-spherical = 反向球形
|
||||
.data-size = 資料大小
|
||||
.database-curves = 資料庫曲線
|
||||
.design = 設計
|
||||
|
|
@ -2266,6 +2339,7 @@ view =
|
|||
.zoom-extents =
|
||||
縮放
|
||||
範圍
|
||||
.cmd-no-hatch-objects = __ocs_fmt_0__:µ▓Ƶ£ëÕí½ÕààþÀÜþë®õ╗ÂÒÇé
|
||||
.cmd-no-text-or-dimension-objects = __ocs_fmt_0__:沒有文字或維度物件。
|
||||
.cmd-not-yet-implemented = __ocs_fmt_0__:尚未實作。
|
||||
.viewport-s-in-layout = 配置“__ocs_fmt_1__”中的視口(__ocs_fmt_0__ 個):
|
||||
|
|
@ -3005,7 +3079,7 @@ styles =
|
|||
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 新值 [0=點 1=無 2=+ 3=× 4=短划;+32 圓 +64 方框]:
|
||||
.pdmode-v = PDMODE = __ocs_fmt_0__ 時間軸 :
|
||||
.pdmode-set-to-v = PDMODE 設定為__ocs_fmt_0__
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 指定新點大小(0 = 視口的 5%,<0 = 絕對值):
|
||||
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 指定新點大小(0 = 視口的 5%,>0 = 絕對值,<0 = 視口百分比):
|
||||
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ 時間軸 :
|
||||
.pdsize-set-to-v-4 = PDSIZE 設定為__ocs_fmt_0__
|
||||
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)
|
||||
|
|
|
|||
17
scripts/assemble-site.sh
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
output="${1:-dist}"
|
||||
test -f "$output/app/index.html"
|
||||
mkdir -p "$output/assets"
|
||||
|
||||
install -m 0644 index.html "$output/index.html"
|
||||
install -m 0644 site/site.css "$output/site.css"
|
||||
install -m 0644 site/robots.txt "$output/robots.txt"
|
||||
install -m 0644 site/sitemap.xml "$output/sitemap.xml"
|
||||
install -m 0644 site/site.webmanifest "$output/site.webmanifest"
|
||||
install -m 0644 site/CNAME "$output/CNAME"
|
||||
install -m 0644 site/og.png "$output/og.png"
|
||||
install -m 0644 site/workspace.png "$output/assets/workspace.png"
|
||||
install -m 0644 site/modeling.png "$output/assets/modeling.png"
|
||||
install -m 0644 assets/logo.svg "$output/assets/logo.svg"
|
||||
1
site/CNAME
Normal file
|
|
@ -0,0 +1 @@
|
|||
www.opencadstudio.com
|
||||
BIN
site/modeling.png
Normal file
|
After Width: | Height: | Size: 213 KiB |
BIN
site/og.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
4
site/robots.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://www.opencadstudio.com/sitemap.xml
|
||||
208
site/site.css
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
:root {
|
||||
--ink: #f5f7fa;
|
||||
--muted: #9aa8b6;
|
||||
--quiet: #71808f;
|
||||
--page: #0b1016;
|
||||
--panel: #111923;
|
||||
--panel-2: #16212d;
|
||||
--line: rgba(178, 211, 233, 0.14);
|
||||
--line-strong: rgba(178, 211, 233, 0.24);
|
||||
--cyan: #59c3ff;
|
||||
--red: #b03020;
|
||||
--shell: min(1180px, calc(100% - 40px));
|
||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
color: var(--ink);
|
||||
background: var(--page);
|
||||
font-synthesis: none;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body { margin: 0; min-width: 320px; background: var(--page); color: var(--ink); }
|
||||
body::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
background:
|
||||
linear-gradient(rgba(89, 195, 255, 0.025) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(89, 195, 255, 0.025) 1px, transparent 1px);
|
||||
background-size: 48px 48px;
|
||||
mask-image: linear-gradient(to bottom, black, transparent 82%);
|
||||
}
|
||||
|
||||
a { color: inherit; text-decoration: none; }
|
||||
img { display: block; max-width: 100%; }
|
||||
.shell { width: var(--shell); margin-inline: auto; }
|
||||
.skip-link { position: fixed; left: 16px; top: -60px; z-index: 100; padding: 10px 14px; background: var(--cyan); color: #05101a; }
|
||||
.skip-link:focus { top: 16px; }
|
||||
|
||||
.site-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: rgba(11, 16, 22, 0.86);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
.nav-shell { min-height: 74px; display: flex; align-items: center; gap: 30px; }
|
||||
.brand { display: inline-flex; align-items: center; gap: 12px; font-size: 16px; font-weight: 630; letter-spacing: -0.02em; white-space: nowrap; }
|
||||
.brand img { border-radius: 8px; box-shadow: 0 0 0 1px rgba(255,255,255,.08); }
|
||||
.brand strong { color: var(--cyan); }
|
||||
.nav-shell nav { display: flex; align-items: center; gap: 28px; margin-left: auto; color: var(--muted); font-size: 14px; }
|
||||
.nav-shell nav a, .site-footer nav a { transition: color .2s ease; }
|
||||
.nav-shell nav a:hover, .site-footer nav a:hover { color: var(--ink); }
|
||||
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
min-height: 50px;
|
||||
padding: 0 20px;
|
||||
border: 1px solid var(--cyan);
|
||||
border-radius: 6px;
|
||||
background: var(--cyan);
|
||||
color: #06121c;
|
||||
font-size: 14px;
|
||||
font-weight: 750;
|
||||
letter-spacing: .01em;
|
||||
transition: transform .2s ease, background .2s ease, border-color .2s ease;
|
||||
}
|
||||
.button:hover { transform: translateY(-2px); background: #84d3ff; border-color: #84d3ff; }
|
||||
.button-small { min-height: 40px; padding-inline: 15px; font-size: 13px; }
|
||||
.button-quiet { border-color: var(--line-strong); background: rgba(255,255,255,.025); color: var(--ink); }
|
||||
.button-quiet:hover { background: var(--panel-2); border-color: rgba(89,195,255,.5); }
|
||||
|
||||
.hero { display: grid; grid-template-columns: 1fr; gap: 60px; padding-block: 92px 78px; }
|
||||
.hero-copy { position: relative; z-index: 2; max-width: 760px; }
|
||||
.eyebrow { display: flex; align-items: center; gap: 11px; margin: 0 0 22px; color: var(--cyan); font-size: 12px; font-weight: 760; letter-spacing: .14em; text-transform: uppercase; }
|
||||
.eyebrow span { width: 28px; height: 1px; background: var(--cyan); }
|
||||
h1, h2, h3, p { margin-top: 0; }
|
||||
h1 { margin-bottom: 26px; font-size: clamp(48px, 5.15vw, 76px); line-height: .99; letter-spacing: -.055em; font-weight: 720; }
|
||||
h1 em { color: var(--cyan); font-style: normal; }
|
||||
.hero-lead { max-width: 650px; margin-bottom: 32px; color: var(--muted); font-size: 18px; line-height: 1.72; }
|
||||
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 28px; }
|
||||
.hero-notes { display: flex; flex-wrap: wrap; gap: 14px 26px; margin: 0; padding: 0; list-style: none; color: var(--quiet); font-size: 12px; }
|
||||
.hero-notes li { position: relative; }
|
||||
.hero-notes li:not(:first-child)::before { content: ""; position: absolute; left: -14px; top: 6px; width: 3px; height: 3px; border-radius: 50%; background: var(--cyan); }
|
||||
|
||||
.hero-visual { position: relative; width: 100%; min-width: 0; overflow: hidden; border: 1px solid var(--line-strong); border-radius: 10px; background: #101820; box-shadow: 0 36px 90px rgba(0,0,0,.42); }
|
||||
.hero-visual img { width: 100%; height: auto; border-radius: 0 0 9px 9px; }
|
||||
.visual-toolbar { display: flex; align-items: center; gap: 12px; height: 42px; padding: 0 14px; color: var(--quiet); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; }
|
||||
.visual-toolbar b { margin-left: auto; color: var(--cyan); font-weight: 700; }
|
||||
.window-mark { display: flex; gap: 5px; }
|
||||
.window-mark i { width: 6px; height: 6px; border-radius: 50%; background: #314150; }
|
||||
.window-mark i:first-child { background: var(--red); }
|
||||
.visual-badge { position: absolute; right: 18px; bottom: 18px; display: flex; align-items: center; gap: 12px; padding: 12px 16px 12px 12px; border: 1px solid rgba(89,195,255,.34); border-radius: 7px; background: rgba(11,16,22,.96); box-shadow: 0 18px 42px rgba(0,0,0,.35); }
|
||||
.visual-badge strong { display: grid; place-items: center; width: 40px; height: 32px; border-radius: 4px; background: var(--cyan); color: #07121b; font-size: 11px; }
|
||||
.visual-badge span { color: var(--muted); font-size: 12px; }
|
||||
|
||||
.signal-strip { border-block: 1px solid var(--line); background: rgba(17,25,35,.74); }
|
||||
.signal-grid { display: grid; grid-template-columns: repeat(4, 1fr); }
|
||||
.signal-grid div { display: flex; flex-direction: column; gap: 4px; padding: 23px 28px; border-left: 1px solid var(--line); }
|
||||
.signal-grid div:last-child { border-right: 1px solid var(--line); }
|
||||
.signal-grid strong { font: 700 13px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--ink); letter-spacing: .06em; }
|
||||
.signal-grid span { color: var(--quiet); font-size: 12px; }
|
||||
|
||||
.section { padding-block: 118px; }
|
||||
.section-heading { max-width: 720px; margin-bottom: 54px; }
|
||||
.section-heading.compact { max-width: 800px; }
|
||||
h2 { margin-bottom: 20px; font-size: clamp(38px, 4.4vw, 60px); line-height: 1.04; letter-spacing: -.045em; font-weight: 680; }
|
||||
.section-heading > p:last-child, .formats-grid > div > p:last-child, .model-copy > p { max-width: 640px; color: var(--muted); font-size: 17px; line-height: 1.7; }
|
||||
|
||||
.capability-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
.capability-card { position: relative; min-height: 340px; padding: 34px; overflow: hidden; border: 1px solid var(--line); border-radius: 8px; background: linear-gradient(145deg, rgba(22,33,45,.9), rgba(14,21,29,.9)); }
|
||||
.capability-wide { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr .8fr; align-items: center; min-height: 410px; }
|
||||
.capability-card h3, .edition-card h3 { max-width: 560px; margin-bottom: 18px; font-size: clamp(28px, 3vw, 42px); line-height: 1.08; letter-spacing: -.035em; }
|
||||
.capability-card p:not(.card-kicker) { max-width: 570px; color: var(--muted); font-size: 15px; line-height: 1.72; }
|
||||
.card-index { position: absolute; top: 25px; right: 28px; color: rgba(89,195,255,.42); font: 600 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||||
.card-kicker, .edition-label { margin-bottom: 20px; color: var(--cyan); font: 700 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .13em; text-transform: uppercase; }
|
||||
.card-accent { background: linear-gradient(145deg, rgba(19,46,62,.92), rgba(11,22,30,.92)); }
|
||||
.drafting-mark { position: relative; width: min(310px, 80%); aspect-ratio: 1; justify-self: center; border: 1px solid rgba(89,195,255,.18); border-radius: 50%; }
|
||||
.drafting-mark .axis { position: absolute; background: rgba(89,195,255,.25); }
|
||||
.drafting-mark .axis-x { left: -15%; right: -15%; top: 50%; height: 1px; }
|
||||
.drafting-mark .axis-y { top: -15%; bottom: -15%; left: 50%; width: 1px; }
|
||||
.drafting-mark .arc { position: absolute; inset: 20%; border: 2px solid var(--cyan); border-left-color: transparent; border-radius: 50%; transform: rotate(-28deg); }
|
||||
.drafting-mark i, .drafting-mark b { position: absolute; width: 9px; height: 9px; border: 2px solid var(--ink); border-radius: 50%; background: var(--panel); }
|
||||
.drafting-mark i { left: 25%; bottom: 18%; }.drafting-mark b { right: 16%; top: 30%; }
|
||||
|
||||
.model-section { border-block: 1px solid var(--line); background: #0e151d; }
|
||||
.model-grid { display: grid; grid-template-columns: 1fr; gap: 48px; }
|
||||
.model-copy { max-width: 760px; }
|
||||
.model-copy h2 { max-width: 580px; }
|
||||
.check-list { display: grid; gap: 13px; margin: 30px 0 0; padding: 0; list-style: none; color: var(--muted); font-size: 14px; }
|
||||
.check-list li::before { content: "+"; display: inline-block; width: 24px; color: var(--cyan); font-family: ui-monospace, monospace; }
|
||||
.model-frame { width: 100%; min-width: 0; margin: 0; overflow: hidden; border: 1px solid var(--line-strong); border-radius: 8px; background: #13191f; }
|
||||
.model-frame img { width: 100%; height: auto; }
|
||||
.model-frame figcaption { display: flex; justify-content: space-between; gap: 20px; padding: 14px 17px; border-top: 1px solid var(--line); color: var(--quiet); font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||||
.model-frame figcaption strong { color: var(--cyan); font-weight: 550; }
|
||||
|
||||
.edition-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
.edition-card { min-height: 390px; padding: 38px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); }
|
||||
.edition-primary { border-color: rgba(89,195,255,.36); background: linear-gradient(145deg, rgba(19,52,69,.85), rgba(13,24,33,.94)); }
|
||||
.edition-card p { max-width: 520px; color: var(--muted); line-height: 1.7; }
|
||||
.text-link { display: inline-flex; gap: 8px; margin-top: 36px; color: var(--cyan); font-size: 14px; font-weight: 720; }
|
||||
.text-link:hover { color: #91daff; }
|
||||
|
||||
.formats-section { border-block: 1px solid var(--line); background: linear-gradient(180deg, rgba(17,25,35,.75), rgba(11,16,22,.6)); }
|
||||
.formats-grid { display: grid; grid-template-columns: .75fr 1.25fr; gap: 90px; align-items: start; }
|
||||
.format-list { border-top: 1px solid var(--line-strong); }
|
||||
.format-list div { display: grid; grid-template-columns: 160px 1fr; gap: 24px; padding: 20px 0; border-bottom: 1px solid var(--line); }
|
||||
.format-list strong { color: var(--ink); font: 700 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .05em; }
|
||||
.format-list span { color: var(--muted); font-size: 14px; }
|
||||
|
||||
.faq-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||
.faq-grid details { border: 1px solid var(--line); border-radius: 7px; background: rgba(17,25,35,.72); }
|
||||
.faq-grid summary { padding: 21px 24px; cursor: pointer; font-size: 14px; font-weight: 680; }
|
||||
.faq-grid details p { margin: 0; padding: 0 24px 22px; color: var(--muted); font-size: 14px; line-height: 1.65; }
|
||||
.community-cta { display: grid; grid-template-columns: 1.2fr .8fr; gap: 60px; align-items: end; margin-bottom: 118px; padding: 58px; border: 1px solid rgba(89,195,255,.28); border-radius: 10px; background: linear-gradient(130deg, #142635, #0f1821 70%); }
|
||||
.community-cta h2 { max-width: 680px; }
|
||||
.community-cta p:last-child { max-width: 700px; color: var(--muted); line-height: 1.7; }
|
||||
.cta-links { display: flex; flex-direction: column; gap: 12px; }
|
||||
|
||||
.site-footer { border-top: 1px solid var(--line); background: #080c11; }
|
||||
.footer-grid { min-height: 150px; display: grid; grid-template-columns: auto 1fr auto; gap: 34px; align-items: center; }
|
||||
.footer-grid p { margin: 0; color: var(--quiet); font-size: 12px; }
|
||||
.footer-grid nav { display: flex; gap: 22px; color: var(--muted); font-size: 12px; }
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.nav-shell nav { display: none; }
|
||||
.nav-shell .button { margin-left: auto; }
|
||||
.hero { padding-top: 80px; }
|
||||
.formats-grid { grid-template-columns: 1fr; gap: 48px; }
|
||||
.community-cta { grid-template-columns: 1fr; }
|
||||
.cta-links { flex-direction: row; flex-wrap: wrap; }
|
||||
.footer-grid { grid-template-columns: 1fr; gap: 18px; padding-block: 34px; }
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
:root { --shell: min(100% - 28px, 1180px); }
|
||||
.nav-shell { min-height: 64px; }
|
||||
.brand { font-size: 14px; }.brand img { width: 34px; height: 34px; }
|
||||
.nav-shell .button-small { min-height: 36px; padding-inline: 11px; font-size: 11px; }
|
||||
.hero { padding-block: 64px 58px; }
|
||||
h1 { font-size: clamp(42px, 13vw, 62px); }
|
||||
.hero-lead { font-size: 16px; }
|
||||
.hero-actions .button { width: 100%; }
|
||||
.hero-notes { display: grid; gap: 8px; }.hero-notes li::before { display: none; }
|
||||
.visual-badge { right: 10px; bottom: 10px; }
|
||||
.signal-grid { grid-template-columns: 1fr 1fr; }
|
||||
.signal-grid div { padding: 19px 16px; border-bottom: 1px solid var(--line); }
|
||||
.section { padding-block: 82px; }
|
||||
.capability-grid, .edition-grid, .faq-grid { grid-template-columns: 1fr; }
|
||||
.capability-wide { grid-column: auto; display: block; min-height: 540px; }
|
||||
.drafting-mark { position: absolute; right: -30px; bottom: -55px; width: 290px; opacity: .75; }
|
||||
.capability-card, .edition-card { padding: 28px; }
|
||||
.format-list div { grid-template-columns: 110px 1fr; gap: 14px; }
|
||||
.model-frame figcaption { flex-direction: column; gap: 6px; }
|
||||
.community-cta { width: var(--shell); margin-bottom: 82px; padding: 34px 24px; }
|
||||
.cta-links .button { width: 100%; }
|
||||
.footer-grid nav { flex-wrap: wrap; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html { scroll-behavior: auto; }
|
||||
*, *::before, *::after { transition-duration: .01ms !important; }
|
||||
}
|
||||
24
site/site.webmanifest
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "Open CAD Studio",
|
||||
"short_name": "Open CAD Studio",
|
||||
"description": "Open-source 2D drafting and 3D modeling for desktop and web.",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#0b1016",
|
||||
"theme_color": "#0b1016",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/logo.svg",
|
||||
"sizes": "any",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
}
|
||||
],
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Launch web app",
|
||||
"url": "/app/"
|
||||
}
|
||||
]
|
||||
}
|
||||
8
site/sitemap.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://www.opencadstudio.com/</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
BIN
site/workspace.png
Normal file
|
After Width: | Height: | Size: 690 KiB |
|
|
@ -73,8 +73,9 @@ const WEB_ALIAS_KEY: &str = "opencadstudio.aliases";
|
|||
/// Version of the shipped default alias table. Bump this whenever the embedded
|
||||
/// defaults add aliases or change a default target, so existing profiles can be
|
||||
/// migrated forward (see `introduced_at` / `migrate_aliases`). Version 1 shipped
|
||||
/// the pre-REDRAW table; version 2 introduced the REDRAW-family aliases.
|
||||
const DEFAULT_ALIASES_VERSION: u32 = 2;
|
||||
/// the pre-REDRAW table; version 2 introduced the REDRAW-family aliases; version 3
|
||||
/// introduced HB (HATCHTOBACK).
|
||||
const DEFAULT_ALIASES_VERSION: u32 = 3;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
const WEB_ALIAS_VERSION_KEY: &str = "opencadstudio.aliases.version";
|
||||
|
|
@ -91,6 +92,7 @@ fn introduced_at(version: u32) -> &'static [(&'static str, &'static str)] {
|
|||
("RE", "REGEN"),
|
||||
("REA", "REGENALL"),
|
||||
],
|
||||
3 => &[("HB", "HATCHTOBACK")],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
|
@ -330,17 +332,18 @@ mod tests {
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// An existing profile that predates the REDRAW aliases gains exactly the
|
||||
/// An existing profile that predates the REDRAW and HB aliases gains exactly the
|
||||
/// new defaults, with every pre-existing mapping kept.
|
||||
#[test]
|
||||
fn migration_adds_new_defaults_preserving_existing() {
|
||||
let mut stored = map(&[("L", "LINE"), ("Z", "ZOOM"), ("HI", "HIDE")]);
|
||||
let changed = migrate_aliases(&mut stored, 0);
|
||||
assert!(changed, "migrating from v0 to v2 must add defaults");
|
||||
assert!(changed, "migrating from v0 to v3 must add defaults");
|
||||
assert_eq!(stored.get("R").map(String::as_str), Some("REDRAW"));
|
||||
assert_eq!(stored.get("RA").map(String::as_str), Some("REDRAWALL"));
|
||||
assert_eq!(stored.get("RE").map(String::as_str), Some("REGEN"));
|
||||
assert_eq!(stored.get("REA").map(String::as_str), Some("REGENALL"));
|
||||
assert_eq!(stored.get("HB").map(String::as_str), Some("HATCHTOBACK"));
|
||||
// Existing entries must survive untouched.
|
||||
assert_eq!(stored.get("L").map(String::as_str), Some("LINE"));
|
||||
assert_eq!(stored.get("Z").map(String::as_str), Some("ZOOM"));
|
||||
|
|
@ -375,10 +378,10 @@ mod tests {
|
|||
assert_eq!(stored.get("L").map(String::as_str), Some("LINE"));
|
||||
}
|
||||
|
||||
/// Migrating a mid-range profile (one that saw v1) only pulls in v2's
|
||||
/// Migrating a mid-range profile (one that saw v1) pulls in v2's and v3's
|
||||
/// additions, not hypothetical earlier ones.
|
||||
#[test]
|
||||
fn migration_from_version_one_adds_v2_aliases() {
|
||||
fn migration_from_version_one_adds_v2_and_v3_aliases() {
|
||||
let mut stored = map(&[("L", "LINE")]);
|
||||
let changed = migrate_aliases(&mut stored, 1);
|
||||
assert!(changed);
|
||||
|
|
@ -386,5 +389,16 @@ mod tests {
|
|||
assert_eq!(stored.get("RA").map(String::as_str), Some("REDRAWALL"));
|
||||
assert_eq!(stored.get("RE").map(String::as_str), Some("REGEN"));
|
||||
assert_eq!(stored.get("REA").map(String::as_str), Some("REGENALL"));
|
||||
assert_eq!(stored.get("HB").map(String::as_str), Some("HATCHTOBACK"));
|
||||
}
|
||||
|
||||
/// Migrating a profile that saw v2 pulls in v3's HB addition only.
|
||||
#[test]
|
||||
fn migration_from_version_two_adds_v3_aliases() {
|
||||
let mut stored = map(&[("L", "LINE")]);
|
||||
let changed = migrate_aliases(&mut stored, 2);
|
||||
assert!(changed);
|
||||
assert_eq!(stored.get("R"), None);
|
||||
assert_eq!(stored.get("HB").map(String::as_str), Some("HATCHTOBACK"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -642,9 +642,53 @@ impl OpenCADStudio {
|
|||
/// Pure-selection commands (SELECTALL, QSELECT, …) run without an active
|
||||
/// command, so `was_active` is false and their selection is preserved.
|
||||
pub(super) fn apply_cmd_result(&mut self, result: CmdResult) -> Task<Message> {
|
||||
let settings = self.tabs[self.active_tab]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.and_then(|command| command.sketch_settings());
|
||||
if let Some((sketch_type, increment, tolerance)) = settings {
|
||||
let tab = &mut self.tabs[self.active_tab];
|
||||
let changed = tab.scene.document.header.sketch_type != sketch_type
|
||||
|| (tab.scene.document.header.sketch_increment - increment).abs() > f64::EPSILON
|
||||
|| (tab.scene.document.header.sketch_tolerance - tolerance).abs()
|
||||
> f64::EPSILON;
|
||||
if changed {
|
||||
crate::io::set_sketch_settings(
|
||||
&mut tab.scene.document,
|
||||
sketch_type,
|
||||
increment,
|
||||
tolerance,
|
||||
);
|
||||
tab.dirty = true;
|
||||
}
|
||||
}
|
||||
let mline_settings = self.tabs[self.active_tab]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.and_then(|command| command.mline_settings());
|
||||
if let Some((scale, justification, style_name, style_handle)) = mline_settings {
|
||||
let tab = &mut self.tabs[self.active_tab];
|
||||
let header = &mut tab.scene.document.header;
|
||||
let style_handle = style_handle.unwrap_or(acadrust::Handle::NULL);
|
||||
let changed = (header.multiline_scale - scale).abs() > f64::EPSILON
|
||||
|| header.multiline_justification != justification
|
||||
|| !header.multiline_style.eq_ignore_ascii_case(&style_name)
|
||||
|| header.current_multiline_style_handle != style_handle;
|
||||
if changed {
|
||||
header.multiline_scale = scale;
|
||||
header.multiline_justification = justification;
|
||||
header.multiline_style = style_name;
|
||||
header.current_multiline_style_handle = style_handle;
|
||||
tab.dirty = true;
|
||||
}
|
||||
}
|
||||
let was_active = self.tabs[self.active_tab].active_cmd.is_some();
|
||||
let preserve_selection =
|
||||
matches!(result, CmdResult::Relaunch(..) | CmdResult::Dispatch(..));
|
||||
let preserve_selection = matches!(
|
||||
&result,
|
||||
CmdResult::Relaunch(..)
|
||||
| CmdResult::Dispatch(..)
|
||||
| CmdResult::SolidSubtract { .. }
|
||||
);
|
||||
let task = self.apply_cmd_result_inner(result);
|
||||
let i = self.active_tab;
|
||||
let preview_hidden = self.tabs[i]
|
||||
|
|
@ -744,7 +788,10 @@ impl OpenCADStudio {
|
|||
// leave the anchor untouched. (#327)
|
||||
if matches!(
|
||||
entity,
|
||||
acadrust::EntityType::Line(_) | acadrust::EntityType::Arc(_)
|
||||
acadrust::EntityType::Line(_)
|
||||
| acadrust::EntityType::Arc(_)
|
||||
| acadrust::EntityType::LwPolyline(_)
|
||||
| acadrust::EntityType::Polyline2D(_)
|
||||
) {
|
||||
self.update_cont_anchor(&entity);
|
||||
}
|
||||
|
|
@ -785,6 +832,7 @@ impl OpenCADStudio {
|
|||
self.commit_entity(entity);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
let prompt = self.tabs[i].active_cmd.as_ref().map(|c| c.prompt());
|
||||
if let Some(p) = prompt {
|
||||
self.command_line.push_info(&p);
|
||||
|
|
@ -1014,7 +1062,10 @@ impl OpenCADStudio {
|
|||
self.commit_undo_delta(i, pending);
|
||||
}
|
||||
}
|
||||
CmdResult::WipeoutFromPolyline(handle) => {
|
||||
CmdResult::WipeoutFromPolyline {
|
||||
handle,
|
||||
erase_source,
|
||||
} => {
|
||||
let wipeout = {
|
||||
let scene = &self.tabs[i].scene;
|
||||
scene
|
||||
|
|
@ -1026,16 +1077,23 @@ impl OpenCADStudio {
|
|||
)
|
||||
};
|
||||
if let Some(wipeout) = wipeout {
|
||||
if erase_source {
|
||||
return self.apply_cmd_result(CmdResult::ReplaceMany(
|
||||
vec![(handle, Vec::new())],
|
||||
vec![wipeout],
|
||||
));
|
||||
}
|
||||
return self.apply_cmd_result(CmdResult::CommitAndExit(wipeout));
|
||||
}
|
||||
self.command_line.push_error(
|
||||
"WIPEOUT Polyline: select a closed planar polyline with at least 3 vertices.",
|
||||
crate::t!("WIPEOUT Polyline: select a straight, closed, planar 2D polyline with at least 3 non-intersecting vertices.").as_ref(),
|
||||
);
|
||||
if let Some(prompt) =
|
||||
self.tabs[i].active_cmd.as_ref().map(|command| command.prompt())
|
||||
{
|
||||
self.command_line.push_info(&prompt);
|
||||
}
|
||||
let command =
|
||||
crate::modules::draw::draw::wipeout::WipeoutCommand::new_polyline();
|
||||
self.command_line.push_info(
|
||||
&crate::command::CadCommand::prompt(&command),
|
||||
);
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
CmdResult::MviewSwitchLayout(layout) => {
|
||||
let task = self.on_layout_switch_preserving_command(layout);
|
||||
|
|
@ -1150,15 +1208,17 @@ impl OpenCADStudio {
|
|||
} => {
|
||||
let label = self.history_label_from_active_cmd(i, "SOLID");
|
||||
let pending = self.begin_undo(i, label, 1, true);
|
||||
self.add_solid_model(entity, *solid, history);
|
||||
self.tabs[i].dirty = true;
|
||||
let handle = self.add_solid_model(entity, *solid, history);
|
||||
if !handle.is_null() {
|
||||
self.tabs[i].dirty = true;
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
}
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.restore_pre_cmd_tangent();
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
}
|
||||
CmdResult::CommitAndEditText(entity) => {
|
||||
let label = self.history_label_from_active_cmd(i, "ENTITY");
|
||||
|
|
@ -1344,6 +1404,13 @@ impl OpenCADStudio {
|
|||
.collect::<Vec<_>>();
|
||||
sources.push(handles);
|
||||
}
|
||||
if let Some(paths) = hatch.boundary_paths.as_mut() {
|
||||
for (path, handles) in std::sync::Arc::make_mut(paths).iter_mut().zip(&sources)
|
||||
{
|
||||
path.boundary_handles = handles.clone();
|
||||
path.flags.set_external(!handles.is_empty());
|
||||
}
|
||||
}
|
||||
hatch.boundary_sources = Some(std::sync::Arc::new(sources));
|
||||
let layer = self.tabs[i].active_layer.clone();
|
||||
let new_handle =
|
||||
|
|
@ -1363,6 +1430,33 @@ impl OpenCADStudio {
|
|||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
}
|
||||
CmdResult::CommitHatches {
|
||||
hatches,
|
||||
entity_style,
|
||||
} => {
|
||||
let label = self.history_label_from_active_cmd(i, "HATCH");
|
||||
let pending = self.begin_undo(i, label, hatches.len(), true);
|
||||
let layer = self.tabs[i].active_layer.clone();
|
||||
for hatch in hatches {
|
||||
let new_handle = self.tabs[i].scene.add_hatch(
|
||||
hatch,
|
||||
Some(&layer),
|
||||
entity_style.clone(),
|
||||
);
|
||||
if !new_handle.is_null() {
|
||||
self.tabs[i].scene.select_entity(new_handle, true);
|
||||
}
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.restore_pre_cmd_tangent();
|
||||
self.refresh_properties();
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
}
|
||||
CmdResult::BatchCopy(mut handles, transforms) => {
|
||||
handles.retain(|handle| !self.tabs[i].scene.is_layer_locked(*handle));
|
||||
if handles.is_empty() {
|
||||
|
|
@ -1464,6 +1558,26 @@ impl OpenCADStudio {
|
|||
}
|
||||
self.refresh_properties();
|
||||
}
|
||||
CmdResult::ReassociateCenterMark { target, source, point } => {
|
||||
if self.reject_locked_edit(i, target) {
|
||||
return Task::none();
|
||||
}
|
||||
self.push_undo_snapshot(i, "CENTERREASSOCIATE");
|
||||
if self.tabs[i].scene.reassociate_center_mark(target, source, point) {
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(
|
||||
"CENTERREASSOCIATE: center mark associated.",
|
||||
);
|
||||
} else {
|
||||
self.command_line.push_error(
|
||||
"CENTERREASSOCIATE: the selected source is not circular.",
|
||||
);
|
||||
}
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.refresh_properties();
|
||||
}
|
||||
CmdResult::ReplaceEntity(handle, new_entities) => {
|
||||
if self.reject_locked_edit(i, handle) {
|
||||
return Task::none();
|
||||
|
|
@ -1561,10 +1675,7 @@ impl OpenCADStudio {
|
|||
.into_iter()
|
||||
.map(|e| self.tabs[i].scene.add_entity(e))
|
||||
.collect();
|
||||
// A replaced dimension carries edited geometry/text but still
|
||||
// names its old *D block; drop that stale block so the next save
|
||||
// re-bakes it — otherwise BricsCAD/ODA draw the pre-edit
|
||||
// graphics while OCS shows the edit. (#181)
|
||||
// Rebuild replaced dimensions from edited data.
|
||||
for &nh in &new_handles {
|
||||
if matches!(
|
||||
self.tabs[i].scene.document.get_entity(nh),
|
||||
|
|
@ -1626,9 +1737,12 @@ impl OpenCADStudio {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// Inject attdefs so the command enters attr-filling mode.
|
||||
if let Some(cmd) = &mut self.tabs[i].active_cmd {
|
||||
cmd.attreq_set_attdefs(attdefs);
|
||||
let completed = self.tabs[i]
|
||||
.active_cmd
|
||||
.as_mut()
|
||||
.and_then(|cmd| cmd.attreq_set_attdefs(attdefs));
|
||||
if let Some(entity) = completed {
|
||||
return self.apply_cmd_result(CmdResult::CommitAndExit(entity));
|
||||
}
|
||||
let prompt = self.tabs[i].active_cmd.as_ref().map(|c| c.prompt());
|
||||
if let Some(p) = prompt {
|
||||
|
|
@ -1874,11 +1988,6 @@ impl OpenCADStudio {
|
|||
}
|
||||
// The command stays active after each apply so more targets
|
||||
// can keep being picked; Enter / Esc ends it (#362).
|
||||
// Special (type-specific) properties travel like AutoCAD's
|
||||
// Special Properties: each is captured from the source when it
|
||||
// carries it and applied only to destinations that support it
|
||||
// (#281). Text formatting crosses TEXT ↔ MTEXT (#361); the dim
|
||||
// style crosses Dimension / Leader / Tolerance.
|
||||
let src_clone = self.tabs[i].scene.document.get_entity(src).cloned();
|
||||
let src_common = src_clone.as_ref().map(|e| e.common().clone());
|
||||
let thickness = src_clone
|
||||
|
|
@ -2677,13 +2786,21 @@ impl OpenCADStudio {
|
|||
}
|
||||
}
|
||||
acadrust::EntityType::LwPolyline(p) => {
|
||||
for v in &mut p.vertices {
|
||||
let Some(mut world) =
|
||||
crate::entities::curve::lwpolyline_world_xy(p)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
for v in &mut world.vertices {
|
||||
if in_win(v.location.x, v.location.y) {
|
||||
v.location.x += dx;
|
||||
v.location.y += dy;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
if stretched {
|
||||
*p = world;
|
||||
}
|
||||
}
|
||||
acadrust::EntityType::Polyline2D(p) => {
|
||||
for v in &mut p.vertices {
|
||||
|
|
@ -2890,80 +3007,32 @@ impl OpenCADStudio {
|
|||
self.commit_undo_delta(i, pending);
|
||||
}
|
||||
}
|
||||
// ── Solid3D creation (BOX / SPHERE / CYLINDER) ────────────────
|
||||
CmdResult::CommitSolid3D { mesh_fn } => {
|
||||
use crate::modules::insert::solid3d_cmds::empty_solid3d;
|
||||
let pending = self.begin_undo(i, "SOLID3D", 1, true);
|
||||
let entity = empty_solid3d();
|
||||
let handle = self.tabs[i].scene.add_entity(entity);
|
||||
if !handle.is_null() {
|
||||
let name = format!("{}", handle.value());
|
||||
let color = [0.6f32, 0.6, 0.8, 1.0]; // default colour; command embedded it
|
||||
let _ = color; // color is captured inside mesh_fn
|
||||
if let Some(mesh) = mesh_fn(name) {
|
||||
let set = crate::scene::MeshLodSet::from_single(mesh);
|
||||
if let Some(acadrust::EntityType::Solid3D(entity)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
let center = set.metrics.centroid;
|
||||
entity.point_of_reference = acadrust::types::Vector3::new(
|
||||
center[0], center[1], center[2],
|
||||
);
|
||||
}
|
||||
self.tabs[i].scene.meshes.insert(handle, set);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(crate::t!("Solid created.").as_ref());
|
||||
}
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.restore_pre_cmd_tangent();
|
||||
}
|
||||
|
||||
// ── EXTRUDE ────────────────────────────────────────────────────
|
||||
CmdResult::ExtrudeEntity {
|
||||
handle,
|
||||
height,
|
||||
color,
|
||||
color: _,
|
||||
} => {
|
||||
if self.reject_locked_edit(i, handle) {
|
||||
self.tabs[i].active_cmd = None;
|
||||
return Task::none();
|
||||
}
|
||||
use crate::modules::insert::solid3d_cmds::empty_solid3d;
|
||||
use crate::scene::model::{solid_model, sweep_model};
|
||||
use crate::scene::model::sweep_model;
|
||||
|
||||
let entity_opt = self.tabs[i].scene.document.get_entity(handle).cloned();
|
||||
if let Some(entity) = entity_opt {
|
||||
// The kernel sweeps the profile into analytic surfaces —
|
||||
// a straight run becomes a plane and an arc a cylinder —
|
||||
// so the solid saves as exact ACIS rather than facets.
|
||||
let result = sweep_model::extruded(&entity, height as f64)
|
||||
.and_then(|body| Some((solid_model::mesh_from_solid(&body, color)?, body)));
|
||||
if let Some((mesh, solid)) = result {
|
||||
let history = crate::scene::model::solid_history::extrusion_op(
|
||||
&entity,
|
||||
height as f64,
|
||||
);
|
||||
if let Some(solid) = sweep_model::extruded(&entity, height) {
|
||||
let history = crate::scene::model::solid_history::brep_op(&solid);
|
||||
let pending = self.begin_undo(i, "EXTRUDE", 1, true);
|
||||
let mut s3d = empty_solid3d();
|
||||
if let acadrust::EntityType::Solid3D(inner) = &mut s3d {
|
||||
inner.wires = solid_model::edge_wires(&solid);
|
||||
}
|
||||
let new_handle = self.tabs[i].scene.add_entity(s3d);
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.create_solid_history(new_handle, history);
|
||||
self.tabs[i].scene.register_solid_model(new_handle, solid);
|
||||
let _ = mesh;
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(crate::t!("EXTRUDE: solid created.").as_ref());
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
let created = self.add_solid_model(empty_solid3d(), solid, history);
|
||||
if !created.is_null() {
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::t!("EXTRUDE: solid created.").as_ref());
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.command_line.push_error(crate::t!("EXTRUDE: could not build profile. Select a closed 2D entity (Circle, LwPolyline, etc.).").as_ref());
|
||||
|
|
@ -2977,26 +3046,80 @@ impl OpenCADStudio {
|
|||
self.restore_pre_cmd_tangent();
|
||||
}
|
||||
|
||||
CmdResult::PresspullEntity {
|
||||
handle,
|
||||
pick,
|
||||
distance,
|
||||
drag,
|
||||
color,
|
||||
} => {
|
||||
if matches!(
|
||||
self.tabs[i].scene.document.get_entity(handle),
|
||||
Some(acadrust::EntityType::Solid3D(_))
|
||||
) {
|
||||
let task = self.solid_face_presspull(handle, pick, distance, drag);
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.restore_pre_cmd_tangent();
|
||||
return task;
|
||||
}
|
||||
if self.reject_locked_edit(i, handle) {
|
||||
self.tabs[i].active_cmd = None;
|
||||
return Task::none();
|
||||
}
|
||||
use crate::modules::insert::solid3d_cmds::empty_solid3d;
|
||||
use crate::scene::model::{solid_history, sweep_model};
|
||||
|
||||
let result = self.tabs[i].scene.document.get_entity(handle).and_then(|entity| {
|
||||
let distance = match drag {
|
||||
Some(point) => sweep_model::projected_drag(entity, pick, point)?,
|
||||
None => distance,
|
||||
};
|
||||
sweep_model::extruded(entity, distance)
|
||||
});
|
||||
if let Some(solid) = result {
|
||||
let pending = self.begin_undo(i, "PRESSPULL", 1, true);
|
||||
let history = solid_history::brep_op(&solid);
|
||||
let created = self.add_solid_model(empty_solid3d(), solid, history);
|
||||
let _ = color;
|
||||
if !created.is_null() {
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::t!("PRESSPULL: solid created.").as_ref());
|
||||
if let Some(delta) = pending {
|
||||
self.commit_undo_delta(i, delta);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.command_line.push_error(
|
||||
crate::t!("PRESSPULL: select a closed profile or planar solid face.")
|
||||
.as_ref(),
|
||||
);
|
||||
}
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.restore_pre_cmd_tangent();
|
||||
}
|
||||
|
||||
// ── REVOLVE ────────────────────────────────────────────────────
|
||||
CmdResult::RevolveEntity {
|
||||
handle,
|
||||
axis_start,
|
||||
axis_end,
|
||||
angle_deg,
|
||||
color,
|
||||
color: _,
|
||||
} => {
|
||||
if self.reject_locked_edit(i, handle) {
|
||||
self.tabs[i].active_cmd = None;
|
||||
return Task::none();
|
||||
}
|
||||
use crate::modules::insert::solid3d_cmds::empty_solid3d;
|
||||
use crate::scene::model::{solid_model, sweep_model};
|
||||
use crate::scene::model::sweep_model;
|
||||
|
||||
let entity_opt = self.tabs[i].scene.document.get_entity(handle).cloned();
|
||||
if let Some(entity) = entity_opt {
|
||||
// A line turned about the axis sweeps into a plane, a
|
||||
// cylinder or a cone, and an arc into a sphere or a
|
||||
// torus, so a revolved solid keeps exact geometry too.
|
||||
let result = sweep_model::revolved(
|
||||
&entity,
|
||||
[
|
||||
|
|
@ -3006,31 +3129,19 @@ impl OpenCADStudio {
|
|||
],
|
||||
[axis_end.x as f64, axis_end.y as f64, axis_end.z as f64],
|
||||
(angle_deg as f64).to_radians(),
|
||||
)
|
||||
.and_then(|body| Some((solid_model::mesh_from_solid(&body, color)?, body)));
|
||||
if let Some((mesh, solid)) = result {
|
||||
let history = crate::scene::model::solid_history::revolve_op(
|
||||
&entity,
|
||||
axis_start.to_array(),
|
||||
axis_end.to_array(),
|
||||
(angle_deg as f64).to_radians(),
|
||||
);
|
||||
);
|
||||
if let Some(solid) = result {
|
||||
let history = crate::scene::model::solid_history::brep_op(&solid);
|
||||
let pending = self.begin_undo(i, "REVOLVE", 1, true);
|
||||
let mut s3d = empty_solid3d();
|
||||
if let acadrust::EntityType::Solid3D(inner) = &mut s3d {
|
||||
inner.wires = solid_model::edge_wires(&solid);
|
||||
}
|
||||
let new_handle = self.tabs[i].scene.add_entity(s3d);
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.create_solid_history(new_handle, history);
|
||||
self.tabs[i].scene.register_solid_model(new_handle, solid);
|
||||
let _ = mesh;
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::tf!("REVOLVE: solid created ({:.0}°).", angle_deg).as_ref());
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
let created = self.add_solid_model(empty_solid3d(), solid, history);
|
||||
if !created.is_null() {
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(
|
||||
crate::tf!("REVOLVE: solid created ({:.0}°).", angle_deg).as_ref(),
|
||||
);
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.command_line
|
||||
|
|
@ -3049,7 +3160,7 @@ impl OpenCADStudio {
|
|||
CmdResult::SweepEntity {
|
||||
profile_handle,
|
||||
path_handle,
|
||||
color,
|
||||
color: _,
|
||||
} => {
|
||||
if self.reject_locked_edit(i, profile_handle)
|
||||
|| self.reject_locked_edit(i, path_handle)
|
||||
|
|
@ -3058,7 +3169,7 @@ impl OpenCADStudio {
|
|||
return Task::none();
|
||||
}
|
||||
use crate::modules::insert::solid3d_cmds::empty_solid3d;
|
||||
use crate::scene::model::sweep_model;
|
||||
use crate::scene::model::{solid_history, sweep_model};
|
||||
|
||||
let profile_ent = self.tabs[i]
|
||||
.scene
|
||||
|
|
@ -3066,39 +3177,21 @@ impl OpenCADStudio {
|
|||
.get_entity(profile_handle)
|
||||
.cloned();
|
||||
let path_ent = self.tabs[i].scene.document.get_entity(path_handle).cloned();
|
||||
let history = profile_ent
|
||||
.as_ref()
|
||||
.zip(path_ent.as_ref())
|
||||
.map(|(profile, path)| {
|
||||
crate::scene::model::solid_history::sweep_op(profile, path)
|
||||
});
|
||||
let result = profile_ent
|
||||
.zip(path_ent)
|
||||
.and_then(|(profile, path)| sweep_model::swept(&profile, &path, color));
|
||||
.and_then(|(profile, path)| sweep_model::swept(&profile, &path));
|
||||
|
||||
if let Some(mut set) = result {
|
||||
if let Some(solid) = result {
|
||||
let pending = self.begin_undo(i, "SWEEP", 1, true);
|
||||
let mut entity = empty_solid3d();
|
||||
if let acadrust::EntityType::Solid3D(solid) = &mut entity {
|
||||
let center = set.metrics.centroid;
|
||||
solid.point_of_reference = acadrust::types::Vector3::new(
|
||||
center[0], center[1], center[2],
|
||||
);
|
||||
}
|
||||
let new_handle = self.tabs[i].scene.add_entity(entity);
|
||||
if let Some(history) = history {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.create_solid_history(new_handle, history);
|
||||
}
|
||||
for mesh in &mut set.lods {
|
||||
mesh.name = format!("{}", new_handle.value());
|
||||
}
|
||||
self.tabs[i].scene.meshes.insert(new_handle, set);
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(crate::t!("SWEEP: solid created.").as_ref());
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
let history = solid_history::brep_op(&solid);
|
||||
let created = self.add_solid_model(empty_solid3d(), solid, history);
|
||||
if !created.is_null() {
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::t!("SWEEP: solid created.").as_ref());
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.command_line.push_error(crate::t!("SWEEP: could not sweep the profile along the path.").as_ref());
|
||||
|
|
@ -3110,7 +3203,7 @@ impl OpenCADStudio {
|
|||
}
|
||||
|
||||
// ── LOFT ──────────────────────────────────────────────────────
|
||||
CmdResult::LoftEntities { handles, color } => {
|
||||
CmdResult::LoftEntities { handles, color: _ } => {
|
||||
if let Some(handle) = handles
|
||||
.iter()
|
||||
.find(|handle| self.tabs[i].scene.is_layer_locked(**handle))
|
||||
|
|
@ -3121,34 +3214,23 @@ impl OpenCADStudio {
|
|||
return Task::none();
|
||||
}
|
||||
use crate::modules::insert::solid3d_cmds::empty_solid3d;
|
||||
use crate::scene::model::sweep_model;
|
||||
use crate::scene::model::{solid_history, sweep_model};
|
||||
|
||||
let profiles: Vec<acadrust::EntityType> = handles
|
||||
.iter()
|
||||
.filter_map(|handle| self.tabs[i].scene.document.get_entity(*handle).cloned())
|
||||
.collect();
|
||||
if let Some(mut set) = sweep_model::lofted(&profiles, color) {
|
||||
let history = crate::scene::model::solid_history::loft_op(&profiles);
|
||||
if let Some(solid) = sweep_model::lofted(&profiles) {
|
||||
let pending = self.begin_undo(i, "LOFT", 1, true);
|
||||
let mut entity = empty_solid3d();
|
||||
if let acadrust::EntityType::Solid3D(solid) = &mut entity {
|
||||
let center = set.metrics.centroid;
|
||||
solid.point_of_reference = acadrust::types::Vector3::new(
|
||||
center[0], center[1], center[2],
|
||||
);
|
||||
}
|
||||
let new_handle = self.tabs[i].scene.add_entity(entity);
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.create_solid_history(new_handle, history);
|
||||
for mesh in &mut set.lods {
|
||||
mesh.name = format!("{}", new_handle.value());
|
||||
}
|
||||
self.tabs[i].scene.meshes.insert(new_handle, set);
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(crate::t!("LOFT: solid created.").as_ref());
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
let history = solid_history::brep_op(&solid);
|
||||
let created = self.add_solid_model(empty_solid3d(), solid, history);
|
||||
if !created.is_null() {
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::t!("LOFT: solid created.").as_ref());
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.command_line.push_error(crate::t!("LOFT: select at least two closed profiles.").as_ref());
|
||||
|
|
@ -3159,47 +3241,246 @@ impl OpenCADStudio {
|
|||
self.restore_pre_cmd_tangent();
|
||||
}
|
||||
|
||||
CmdResult::SolidEdgeBlend {
|
||||
handle,
|
||||
pick,
|
||||
value,
|
||||
fillet,
|
||||
} => {
|
||||
let task = self.solid_edge_blend(handle, pick, value, fillet);
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.restore_pre_cmd_tangent();
|
||||
return task;
|
||||
}
|
||||
|
||||
CmdResult::SolidSubtract { bases, cutters } => {
|
||||
let task = self.solid_subtract(&bases, &cutters);
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.restore_pre_cmd_tangent();
|
||||
return task;
|
||||
}
|
||||
|
||||
CmdResult::HatcheditApply {
|
||||
handle,
|
||||
name,
|
||||
scale,
|
||||
angle,
|
||||
operation,
|
||||
} => {
|
||||
if self.reject_locked_edit(i, handle) {
|
||||
return Task::none();
|
||||
}
|
||||
if let Some(mut model) = self.tabs[i].scene.hatches.get(&handle).cloned() {
|
||||
let layer = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.map(|entity| entity.as_entity().layer().to_string())
|
||||
.unwrap_or_else(|| "0".to_string());
|
||||
// Update model fields
|
||||
if !name.is_empty() {
|
||||
use crate::scene::model::hatch_model::HatchPattern;
|
||||
use crate::scene::model::hatch_patterns;
|
||||
model.name = name.clone();
|
||||
if name.to_uppercase() == "SOLID" {
|
||||
model.pattern = HatchPattern::Solid;
|
||||
} else if let Some(entry) = hatch_patterns::find(&name) {
|
||||
model.pattern = entry.gpu.clone();
|
||||
}
|
||||
// If not found in catalog, keep existing pattern type
|
||||
}
|
||||
model.scale = scale;
|
||||
model.angle_offset = angle;
|
||||
|
||||
self.push_undo_snapshot(i, "HATCHEDIT");
|
||||
// Remove old hatch (entity + GPU model)
|
||||
self.tabs[i].scene.erase_entities(&[handle]);
|
||||
// Re-add with updated model
|
||||
self.tabs[i].scene.add_hatch(model, Some(&layer), None);
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(crate::t!("HATCHEDIT: hatch updated.").as_ref());
|
||||
} else {
|
||||
if !matches!(
|
||||
self.tabs[i].scene.document.get_entity(handle),
|
||||
Some(acadrust::EntityType::Hatch(_))
|
||||
) {
|
||||
self.command_line
|
||||
.push_error(crate::t!("HATCHEDIT: hatch entity not found.").as_ref());
|
||||
} else {
|
||||
use crate::command::HatchEditOperation;
|
||||
if matches!(
|
||||
&operation,
|
||||
HatchEditOperation::DrawOrderFront | HatchEditOperation::DrawOrderBack
|
||||
) {
|
||||
let command = if matches!(&operation, HatchEditOperation::DrawOrderFront) {
|
||||
"DRAWORDER FRONT"
|
||||
} else {
|
||||
"DRAWORDER BACK"
|
||||
};
|
||||
self.tabs[i].scene.deselect_all();
|
||||
self.tabs[i].scene.select_entity(handle, false);
|
||||
self.tabs[i].active_cmd = None;
|
||||
return self.dispatch_view(command, i).unwrap_or_else(Task::none);
|
||||
}
|
||||
self.push_undo_snapshot(i, "HATCHEDIT");
|
||||
match operation {
|
||||
HatchEditOperation::Update {
|
||||
origin,
|
||||
disassociate,
|
||||
style,
|
||||
annotative,
|
||||
} => {
|
||||
if let Some(acadrust::EntityType::Hatch(hatch)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
if !name.is_empty() && name != hatch.pattern.name {
|
||||
if let Some(entry) =
|
||||
crate::scene::model::hatch_patterns::find(&name)
|
||||
{
|
||||
let old_origin = hatch
|
||||
.pattern
|
||||
.lines
|
||||
.first()
|
||||
.map(|line| line.base_point);
|
||||
let mut pattern = crate::scene::model::hatch_patterns::build_dxf_pattern(entry);
|
||||
crate::entities::hatch::scale_pattern_geometry(
|
||||
&mut pattern,
|
||||
scale.max(1.0e-6) as f64,
|
||||
);
|
||||
crate::entities::hatch::rotate_pattern_geometry(
|
||||
&mut pattern,
|
||||
(angle as f64).to_radians(),
|
||||
);
|
||||
if let (Some(old), Some(new)) = (
|
||||
old_origin,
|
||||
pattern.lines.first().map(|line| line.base_point),
|
||||
) {
|
||||
crate::entities::hatch::translate_pattern_geometry(
|
||||
&mut pattern,
|
||||
old.x - new.x,
|
||||
old.y - new.y,
|
||||
);
|
||||
}
|
||||
hatch.pattern = pattern;
|
||||
hatch.is_solid = matches!(
|
||||
entry.gpu,
|
||||
crate::scene::model::hatch_model::HatchPattern::Solid
|
||||
);
|
||||
hatch.pattern_type =
|
||||
acadrust::entities::HatchPatternType::Predefined;
|
||||
hatch.gradient_color.enabled = false;
|
||||
}
|
||||
} else {
|
||||
let requested_scale = scale.max(1.0e-6) as f64;
|
||||
if hatch.pattern_scale > 1.0e-12 {
|
||||
let factor = requested_scale / hatch.pattern_scale;
|
||||
crate::entities::hatch::scale_pattern_geometry(
|
||||
&mut hatch.pattern,
|
||||
factor,
|
||||
);
|
||||
}
|
||||
let requested_angle = (angle as f64).to_radians();
|
||||
let delta = requested_angle - hatch.pattern_angle;
|
||||
crate::entities::hatch::rotate_pattern_geometry(
|
||||
&mut hatch.pattern,
|
||||
delta,
|
||||
);
|
||||
}
|
||||
hatch.pattern_scale = scale.max(1.0e-6) as f64;
|
||||
hatch.pattern_angle = (angle as f64).to_radians();
|
||||
if let Some((x, y)) = origin {
|
||||
if let Some(current) =
|
||||
hatch.pattern.lines.first().map(|line| line.base_point)
|
||||
{
|
||||
crate::entities::hatch::translate_pattern_geometry(
|
||||
&mut hatch.pattern,
|
||||
x - current.x,
|
||||
y - current.y,
|
||||
);
|
||||
}
|
||||
}
|
||||
if disassociate {
|
||||
for path in &mut hatch.paths {
|
||||
path.boundary_handles.clear();
|
||||
path.flags.set_external(false);
|
||||
}
|
||||
hatch.is_associative = false;
|
||||
}
|
||||
if let Some(style) = style {
|
||||
hatch.style = style;
|
||||
}
|
||||
}
|
||||
if let Some(value) = annotative {
|
||||
crate::scene::annotative::set_entity_annotative(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
value,
|
||||
);
|
||||
if value {
|
||||
if let Some(scale_handle) =
|
||||
self.tabs[i].scene.creation_annotation_scale_handle()
|
||||
{
|
||||
crate::scene::annotative::create_annotation_context(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
scale_handle,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.tabs[i].scene.bump_entities(&[(
|
||||
handle,
|
||||
crate::scene::ChangeKind::Modified,
|
||||
)]);
|
||||
}
|
||||
HatchEditOperation::AddBoundaries(handles) => {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.edit_hatch_boundary_handles(handle, &handles, true);
|
||||
}
|
||||
HatchEditOperation::RemoveBoundaries(handles) => {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.edit_hatch_boundary_handles(handle, &handles, false);
|
||||
}
|
||||
HatchEditOperation::RecreateBoundary => {
|
||||
let source = self.tabs[i].scene.document.get_entity(handle).cloned();
|
||||
if let Some(acadrust::EntityType::Hatch(source)) = source {
|
||||
let storage = crate::entities::curve::ocs_plane(
|
||||
source.normal,
|
||||
source.elevation,
|
||||
);
|
||||
let plane = crate::command::WorkingPlane::new(
|
||||
glam::DVec3::from_array(storage.origin),
|
||||
glam::DVec3::from_array(storage.x_axis),
|
||||
glam::DVec3::from_array(storage.y_axis),
|
||||
);
|
||||
let rings = crate::scene::hatch_boundary_rings(&source);
|
||||
let entities = crate::scene::boundary_entities(&rings, plane);
|
||||
let mut handles = Vec::new();
|
||||
for entity in entities {
|
||||
if let Some(boundary) = self.commit_entity_handle(entity) {
|
||||
handles.push(boundary);
|
||||
}
|
||||
}
|
||||
if let Some(acadrust::EntityType::Hatch(hatch)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
for (path, boundary) in
|
||||
hatch.paths.iter_mut().zip(handles.iter().copied())
|
||||
{
|
||||
path.boundary_handles = vec![boundary];
|
||||
path.flags.set_external(true);
|
||||
}
|
||||
hatch.is_associative = !handles.is_empty();
|
||||
}
|
||||
self.tabs[i].scene.bump_entities(&[(
|
||||
handle,
|
||||
crate::scene::ChangeKind::Modified,
|
||||
)]);
|
||||
}
|
||||
}
|
||||
HatchEditOperation::Separate => {
|
||||
let source = self.tabs[i].scene.document.get_entity(handle).cloned();
|
||||
if let Some(acadrust::EntityType::Hatch(hatch)) = source {
|
||||
let groups = crate::scene::separated_hatch_path_groups(&hatch);
|
||||
if groups.len() > 1 {
|
||||
for paths in groups {
|
||||
let mut separated = hatch.clone();
|
||||
separated.common.handle = acadrust::Handle::NULL;
|
||||
separated.paths = paths;
|
||||
separated.is_associative = separated
|
||||
.paths
|
||||
.iter()
|
||||
.any(|path| !path.boundary_handles.is_empty());
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.add_entity(acadrust::EntityType::Hatch(separated));
|
||||
}
|
||||
self.tabs[i].scene.erase_entities(&[handle]);
|
||||
}
|
||||
}
|
||||
}
|
||||
HatchEditOperation::DrawOrderFront
|
||||
| HatchEditOperation::DrawOrderBack => unreachable!(),
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::t!("HATCHEDIT: hatch updated.").as_ref());
|
||||
}
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,22 @@
|
|||
use super::*;
|
||||
|
||||
fn selected_solid(app: &OpenCADStudio, tab: usize) -> Option<acadrust::Handle> {
|
||||
let scene = &app.tabs.get(tab)?.scene;
|
||||
let selected = scene.selected_handles_in_order();
|
||||
let [handle] = selected.as_slice() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if matches!(
|
||||
scene.document.get_entity(*handle),
|
||||
Some(acadrust::EntityType::Solid3D(_))
|
||||
) {
|
||||
Some(*handle)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(super) fn dispatch_dim(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
match cmd {
|
||||
|
|
@ -920,30 +937,7 @@ impl OpenCADStudio {
|
|||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
}
|
||||
|
||||
// Bare ZOOM prompts with the mode options as clickable keywords;
|
||||
// the sub-keyword forms (ZOOM EXTENTS / WINDOW / …) run directly.
|
||||
"ZOOM" => {
|
||||
use crate::command::KeywordCommand;
|
||||
let c = KeywordCommand::new(
|
||||
"ZOOM",
|
||||
"ZOOM [Window / Extents / Previous / Object / All / Dynamic / Extents All / In / Out / Scale]:",
|
||||
vec![
|
||||
("Window", "W", None),
|
||||
("Extents", "E", None),
|
||||
("Previous", "P", None),
|
||||
("Object", "O", None),
|
||||
("All", "A", None),
|
||||
("Dynamic", "D", None),
|
||||
("Extents All", "EA", None),
|
||||
("In", "I", None),
|
||||
("Out", "OUT", None),
|
||||
("Scale", "S", Some("ZOOM scale factor (e.g. 2 or 0.5):")),
|
||||
],
|
||||
);
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
}
|
||||
"ZOOM WINDOW" | "ZOOM W" | "ZW" => {
|
||||
"ZOOM" | "ZOOM WINDOW" | "ZOOM W" | "ZW" => {
|
||||
use crate::modules::view::zoom_window::ZoomWindowCommand;
|
||||
let new_cmd = ZoomWindowCommand::new();
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
|
|
@ -973,6 +967,20 @@ impl OpenCADStudio {
|
|||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
||||
"SOLIDFILLET" => {
|
||||
use crate::modules::model::edge_cmd::{EdgeOperation, SolidEdgeCommand};
|
||||
let command = SolidEdgeCommand::new(EdgeOperation::Fillet, selected_solid(self, i));
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
|
||||
"FILLET" if selected_solid(self, i).is_some() => {
|
||||
use crate::modules::model::edge_cmd::{EdgeOperation, SolidEdgeCommand};
|
||||
let command = SolidEdgeCommand::new(EdgeOperation::Fillet, selected_solid(self, i));
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
|
||||
"FILLET" => {
|
||||
use crate::modules::draw::modify::fillet::FilletCommand;
|
||||
let entities: Vec<_> = self.tabs[i]
|
||||
|
|
@ -1106,6 +1114,20 @@ impl OpenCADStudio {
|
|||
}
|
||||
}
|
||||
|
||||
"SOLIDCHAMFER" => {
|
||||
use crate::modules::model::edge_cmd::{EdgeOperation, SolidEdgeCommand};
|
||||
let command = SolidEdgeCommand::new(EdgeOperation::Chamfer, selected_solid(self, i));
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
|
||||
"CHAMFER" if selected_solid(self, i).is_some() => {
|
||||
use crate::modules::model::edge_cmd::{EdgeOperation, SolidEdgeCommand};
|
||||
let command = SolidEdgeCommand::new(EdgeOperation::Chamfer, selected_solid(self, i));
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
|
||||
"CHAMFER" => {
|
||||
use crate::modules::draw::modify::fillet::ChamferCommand;
|
||||
let entities: Vec<_> = self.tabs[i]
|
||||
|
|
|
|||
|
|
@ -466,31 +466,55 @@ impl OpenCADStudio {
|
|||
}
|
||||
}
|
||||
|
||||
// BOX / SPHERE / CYLINDER / CONE / WEDGE / TORUS are handled by the
|
||||
// Model-tab primitive command above (with the kernel boolean caching).
|
||||
|
||||
// ── EXTRUDE ────────────────────────────────────────────────────
|
||||
// PRESSPULL on a closed boundary creates a solid by extruding it to a
|
||||
// height — the same operation as EXTRUDE. THICKEN turns a closed planar
|
||||
// profile into a solid of the given thickness, which is also an extrude.
|
||||
"EXTRUDE" | "PRESSPULL" | "THICKEN" => {
|
||||
"EXTRUDE" | "THICKEN" => {
|
||||
use crate::modules::insert::solid3d_cmds::ExtrudeCommand;
|
||||
// If a single entity is already selected, skip the pick step.
|
||||
let selected: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect();
|
||||
let color = self.tabs[i].scene.layer_color(&self.tabs[i].active_layer);
|
||||
if selected.len() == 1 {
|
||||
let handle = selected[0].0;
|
||||
let mut cmd = ExtrudeCommand::new(color);
|
||||
cmd.on_entity_pick(handle, glam::DVec3::ZERO);
|
||||
let mut cmd = ExtrudeCommand::new_named(cmd, color);
|
||||
if let Some(curve) = crate::entities::curve::entity_curve(selected[0].1) {
|
||||
cmd.set_entity_pick_direction(
|
||||
curve.plane.normal().map(glam::DVec3::from_array),
|
||||
);
|
||||
cmd.on_entity_pick(
|
||||
handle,
|
||||
glam::DVec3::from_array(curve.plane.origin),
|
||||
);
|
||||
}
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
} else {
|
||||
let cmd = ExtrudeCommand::new(color);
|
||||
let cmd = ExtrudeCommand::new_named(cmd, color);
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
}
|
||||
}
|
||||
|
||||
"PRESSPULL" => {
|
||||
use crate::modules::insert::solid3d_cmds::PresspullCommand;
|
||||
let selected: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect();
|
||||
let color = self.tabs[i].scene.layer_color(&self.tabs[i].active_layer);
|
||||
let mut command = PresspullCommand::new(color);
|
||||
if selected.len() == 1
|
||||
&& !matches!(selected[0].1, acadrust::EntityType::Solid3D(_))
|
||||
{
|
||||
if let Some(curve) = crate::entities::curve::entity_curve(selected[0].1) {
|
||||
command.set_entity_pick_direction(
|
||||
curve.plane.normal().map(glam::DVec3::from_array),
|
||||
);
|
||||
command.on_entity_pick(
|
||||
selected[0].0,
|
||||
glam::DVec3::from_array(curve.plane.origin),
|
||||
);
|
||||
}
|
||||
}
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
|
||||
// ── REVOLVE ────────────────────────────────────────────────────
|
||||
"REVOLVE" => {
|
||||
use crate::modules::insert::solid3d_cmds::RevolveCommand;
|
||||
|
|
|
|||
|
|
@ -13,27 +13,27 @@ impl OpenCADStudio {
|
|||
|
||||
"MLINE" => {
|
||||
use crate::modules::draw::draw::mline::MlineCommand;
|
||||
let style_name = self.tabs[i].scene.document.header.multiline_style.clone();
|
||||
let style = self.tabs[i]
|
||||
let header = &self.tabs[i].scene.document.header;
|
||||
let style_name = header.multiline_style.clone();
|
||||
let scale = header.multiline_scale;
|
||||
let justification = header.multiline_justification;
|
||||
let styles = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.objects
|
||||
.iter()
|
||||
.find_map(|(handle, object)| match object {
|
||||
acadrust::objects::ObjectType::MLineStyle(style)
|
||||
if style.name.eq_ignore_ascii_case(&style_name) =>
|
||||
{
|
||||
Some((*handle, style.elements.len()))
|
||||
.filter_map(|(handle, object)| match object {
|
||||
acadrust::objects::ObjectType::MLineStyle(style) => {
|
||||
Some((*handle, style.clone()))
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
let (style_handle, element_count) = style
|
||||
.map(|(handle, count)| (Some(handle), count))
|
||||
.unwrap_or((None, 2));
|
||||
let cmd_obj = MlineCommand::with_style(
|
||||
})
|
||||
.collect();
|
||||
let cmd_obj = MlineCommand::with_styles(
|
||||
styles,
|
||||
style_name,
|
||||
style_handle,
|
||||
element_count,
|
||||
scale,
|
||||
justification,
|
||||
);
|
||||
self.command_line.push_info(&cmd_obj.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
|
||||
|
|
@ -48,7 +48,11 @@ impl OpenCADStudio {
|
|||
let wo_cmd = match args.as_str() {
|
||||
"P" | "POLYLINE" => WipeoutCommand::new_polyline(),
|
||||
"R" | "RECTANGULAR" => WipeoutCommand::new_rectangular(),
|
||||
_ => WipeoutCommand::new_polygonal(),
|
||||
_ => WipeoutCommand::new_polygonal(
|
||||
crate::modules::draw::draw::wipeout::wipeout_frame_mode(
|
||||
&self.tabs[i].scene.document,
|
||||
),
|
||||
),
|
||||
};
|
||||
self.command_line.push_info(&wo_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(wo_cmd));
|
||||
|
|
@ -60,7 +64,15 @@ impl OpenCADStudio {
|
|||
|
||||
"REVCLOUD" => {
|
||||
use crate::modules::draw::draw::revcloud::RevCloudCommand;
|
||||
let cmd = RevCloudCommand::new();
|
||||
let view_height = self.tabs[i].scene.camera.borrow().ortho_size() as f64 * 2.0;
|
||||
let default_arc_length = (view_height * 0.0125).max(1.0e-6);
|
||||
let sources = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.map(|entity| (entity.common().handle, entity.clone()))
|
||||
.collect();
|
||||
let cmd = RevCloudCommand::new(default_arc_length, sources);
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
}
|
||||
|
|
@ -288,8 +300,8 @@ impl OpenCADStudio {
|
|||
}
|
||||
|
||||
"ARC" => {
|
||||
use crate::modules::draw::draw::arc::ArcCommand;
|
||||
let new_cmd = ArcCommand::new();
|
||||
use crate::modules::draw::draw::arc::Arc3PCommand;
|
||||
let new_cmd = Arc3PCommand::new();
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
|
@ -299,6 +311,12 @@ impl OpenCADStudio {
|
|||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
"ARC_CSE" => {
|
||||
use crate::modules::draw::draw::arc::ArcCommand;
|
||||
let new_cmd = ArcCommand::new();
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
"ARC_SCE" => {
|
||||
use crate::modules::draw::draw::arc::ArcSCECommand;
|
||||
let new_cmd = ArcSCECommand::new();
|
||||
|
|
@ -461,21 +479,82 @@ impl OpenCADStudio {
|
|||
|
||||
"CENTERLINE" => {
|
||||
use crate::modules::draw::draw::centerline::CenterLineCommand;
|
||||
let new_cmd = CenterLineCommand::new();
|
||||
let settings = self.tabs[i].scene.centerline_settings();
|
||||
let new_cmd = CenterLineCommand::new(settings);
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
||||
"DIMCENTER" | "CENTERMARK" => {
|
||||
"CENTERRESET" => {
|
||||
let handles = self.tabs[i].scene.selected_handles_in_order();
|
||||
self.push_undo_snapshot(i, "CENTERRESET");
|
||||
let count = self.tabs[i].scene.reset_centerlines(&handles)
|
||||
+ self.tabs[i].scene.reset_center_marks(&handles);
|
||||
if count > 0 {
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
self.command_line
|
||||
.push_output(&format!("CENTERRESET: {count} center object(s) updated."));
|
||||
}
|
||||
|
||||
"CENTERREASSOCIATE" => {
|
||||
let handles = self.tabs[i].scene.selected_handles_in_order();
|
||||
let mark_targets: Vec<_> = handles.iter().copied().filter(|handle| {
|
||||
let Some(acadrust::EntityType::Line(line)) = self.tabs[i].scene.document.get_entity(*handle) else { return false; };
|
||||
acadrust::entities::CenterMarkAssociation::read(&line.common.extended_data).is_some()
|
||||
}).collect();
|
||||
if mark_targets.len() == 1 && handles.len() == 1 {
|
||||
use crate::modules::draw::draw::dimcenter::CenterMarkReassociateCommand;
|
||||
let new_cmd = CenterMarkReassociateCommand::new(mark_targets[0]);
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
self.push_undo_snapshot(i, "CENTERREASSOCIATE");
|
||||
let count = self.tabs[i].scene.set_centerline_association(&handles, true)
|
||||
+ self.tabs[i].scene.set_center_mark_association(&handles, true);
|
||||
if count > 0 {
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
self.command_line
|
||||
.push_output(&format!("CENTERREASSOCIATE: {count} center object(s) associated."));
|
||||
}
|
||||
|
||||
"CENTERDISASSOCIATE" => {
|
||||
let handles = self.tabs[i].scene.selected_handles_in_order();
|
||||
self.push_undo_snapshot(i, "CENTERDISASSOCIATE");
|
||||
let count = self.tabs[i].scene.set_centerline_association(&handles, false)
|
||||
+ self.tabs[i].scene.set_center_mark_association(&handles, false);
|
||||
if count > 0 {
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
self.command_line
|
||||
.push_output(&format!("CENTERDISASSOCIATE: {count} center object(s) detached."));
|
||||
}
|
||||
|
||||
"DIMCENTER" => {
|
||||
use crate::modules::draw::draw::dimcenter::DimCenterCommand;
|
||||
let new_cmd = DimCenterCommand::new();
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
||||
"CENTERMARK" => {
|
||||
use crate::modules::draw::draw::dimcenter::CenterMarkCommand;
|
||||
let settings = self.tabs[i].scene.centerline_settings();
|
||||
let new_cmd = CenterMarkCommand::new(settings);
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
||||
"SKETCH" => {
|
||||
use crate::modules::draw::draw::sketch::SketchCommand;
|
||||
let new_cmd = SketchCommand::new();
|
||||
let header = &self.tabs[i].scene.document.header;
|
||||
let new_cmd = SketchCommand::new(
|
||||
header.sketch_type,
|
||||
header.sketch_increment,
|
||||
header.sketch_tolerance,
|
||||
);
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
|
@ -595,9 +674,13 @@ impl OpenCADStudio {
|
|||
}
|
||||
}
|
||||
|
||||
"POINT" => {
|
||||
"POINT" | "MULTIPOINT" => {
|
||||
use crate::modules::draw::draw::point::PointCommand;
|
||||
let new_cmd = PointCommand::new();
|
||||
let new_cmd = if cmd == "MULTIPOINT" {
|
||||
PointCommand::multiple()
|
||||
} else {
|
||||
PointCommand::new()
|
||||
};
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
|
@ -618,8 +701,26 @@ impl OpenCADStudio {
|
|||
|
||||
"HATCH" => {
|
||||
use crate::modules::draw::draw::hatch::HatchCommand;
|
||||
let outlines = self.tabs[i].scene.hatch_boundary_outlines();
|
||||
let boundary_sources = self.tabs[i].scene.hatch_boundary_sources();
|
||||
let working_plane = if self.tabs[i].editing_model_space() {
|
||||
self.tabs[i].ucs_xform().working_plane()
|
||||
} else {
|
||||
crate::command::WorkingPlane::default()
|
||||
};
|
||||
let normal = working_plane.z.normalize_or(glam::DVec3::Z);
|
||||
let elevation = working_plane.origin.dot(normal);
|
||||
let storage = crate::entities::curve::ocs_plane(
|
||||
acadrust::types::Vector3::new(normal.x, normal.y, normal.z),
|
||||
elevation,
|
||||
);
|
||||
let plane = crate::command::WorkingPlane::new(
|
||||
glam::DVec3::from_array(storage.origin),
|
||||
glam::DVec3::from_array(storage.x_axis),
|
||||
glam::DVec3::from_array(storage.y_axis),
|
||||
);
|
||||
let boundary_sources = self.tabs[i]
|
||||
.scene
|
||||
.boundary_sources_on_plane(plane, 1.0e-6);
|
||||
let outlines = crate::scene::boundary_faces(&boundary_sources, 1.0e-6);
|
||||
let selected = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
|
|
@ -633,8 +734,13 @@ impl OpenCADStudio {
|
|||
let common = self.tabs[i].scene.document.get_entity(*handle)?.common();
|
||||
Some((model, common.color.clone(), common.transparency))
|
||||
});
|
||||
let new_cmd =
|
||||
HatchCommand::new(outlines, boundary_sources, selected, inherited);
|
||||
let new_cmd = HatchCommand::new(
|
||||
outlines,
|
||||
boundary_sources,
|
||||
selected,
|
||||
inherited,
|
||||
plane,
|
||||
);
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
self.refresh_area_preview(i);
|
||||
|
|
@ -647,11 +753,26 @@ impl OpenCADStudio {
|
|||
if sel.len() == 1 {
|
||||
let (h, _) = sel[0];
|
||||
if let Some(model) = self.tabs[i].scene.hatches.get(&h).cloned() {
|
||||
let entity = self.tabs[i].scene.document.get_entity(h);
|
||||
let annotative = entity.is_some_and(|entity| {
|
||||
crate::scene::annotative::is_annotative(
|
||||
&self.tabs[i].scene.document,
|
||||
entity,
|
||||
)
|
||||
});
|
||||
let (scale, angle) = match entity {
|
||||
Some(acadrust::EntityType::Hatch(hatch)) => (
|
||||
hatch.pattern_scale as f32,
|
||||
hatch.pattern_angle.to_degrees() as f32,
|
||||
),
|
||||
_ => (model.scale, model.angle_offset.to_degrees()),
|
||||
};
|
||||
let cmd = HatcheditCommand::with_handle(
|
||||
h,
|
||||
model.name.clone(),
|
||||
model.scale,
|
||||
model.angle_offset,
|
||||
scale,
|
||||
angle,
|
||||
annotative,
|
||||
);
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
|
|
@ -668,16 +789,32 @@ impl OpenCADStudio {
|
|||
|
||||
"GRADIENT" => {
|
||||
use crate::modules::draw::draw::hatch::GradientCommand;
|
||||
let outlines = self.tabs[i].scene.closed_outlines();
|
||||
let new_cmd = GradientCommand::new(outlines);
|
||||
let boundary_sources = self.tabs[i]
|
||||
.scene
|
||||
.boundary_sources_on_plane(crate::command::WorkingPlane::default(), 1.0e-6);
|
||||
let outlines = crate::scene::boundary_faces(&boundary_sources, 1.0e-6);
|
||||
let new_cmd = GradientCommand::new(outlines, boundary_sources);
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
||||
"BOUNDARY" => {
|
||||
use crate::modules::draw::draw::hatch::BoundaryCommand;
|
||||
let outlines = self.tabs[i].scene.closed_outlines();
|
||||
let new_cmd = BoundaryCommand::new(outlines);
|
||||
let plane = if self.tabs[i].editing_model_space() {
|
||||
self.tabs[i].ucs_xform().working_plane()
|
||||
} else {
|
||||
crate::command::WorkingPlane::default()
|
||||
};
|
||||
let sources = self.tabs[i]
|
||||
.scene
|
||||
.boundary_sources_on_plane(plane, 1.0e-6);
|
||||
let selected = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(handle, _)| *handle)
|
||||
.collect();
|
||||
let new_cmd = BoundaryCommand::new(sources, selected, plane);
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
|
@ -788,21 +925,66 @@ impl OpenCADStudio {
|
|||
}
|
||||
|
||||
// ── Model commands (3D primitives) ─────────────────────────────
|
||||
"BOX" | "WEDGE" | "CYLINDER" | "CONE" | "SPHERE" | "TORUS" => {
|
||||
"BOX" | "WEDGE" | "CYLINDER" | "CONE" | "SPHERE" | "PYRAMID" | "PYR"
|
||||
| "TORUS" => {
|
||||
use crate::modules::model::primitive_cmd::PrimitiveCommand;
|
||||
let new_cmd = PrimitiveCommand::new(cmd);
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
||||
// ── Design commands (solid booleans) ───────────────────────────
|
||||
"UNION" | "SUBTRACT" | "INTERSECT" => {
|
||||
// ── Solid booleans ─────────────────────────────────────────────
|
||||
"UNION" | "INTERSECT" => {
|
||||
use crate::modules::model::boolean_cmd::BoolOp;
|
||||
if let Some(op) = BoolOp::from_id(cmd) {
|
||||
return Some(self.solid_boolean(op));
|
||||
let solid_count = {
|
||||
let scene = &self.tabs[i].scene;
|
||||
scene
|
||||
.selected_handles_in_order()
|
||||
.into_iter()
|
||||
.filter(|handle| !scene.is_layer_locked(*handle))
|
||||
.filter(|handle| {
|
||||
matches!(
|
||||
scene.document.get_entity(*handle),
|
||||
Some(acadrust::EntityType::Solid3D(_))
|
||||
)
|
||||
})
|
||||
.take(2)
|
||||
.count()
|
||||
};
|
||||
if solid_count < 2 {
|
||||
use crate::modules::draw::select::SelectObjectsCommand;
|
||||
let selection = SelectObjectsCommand::new(cmd);
|
||||
self.command_line.push_info(&selection.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(selection));
|
||||
} else {
|
||||
return Some(self.solid_boolean(op));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"SUBTRACT" => {
|
||||
use crate::modules::model::boolean_cmd::SubtractCommand;
|
||||
let bases = {
|
||||
let scene = &self.tabs[i].scene;
|
||||
scene
|
||||
.selected_handles_in_order()
|
||||
.into_iter()
|
||||
.filter(|handle| !scene.is_layer_locked(*handle))
|
||||
.filter(|handle| {
|
||||
matches!(
|
||||
scene.document.get_entity(*handle),
|
||||
Some(acadrust::EntityType::Solid3D(_))
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
self.tabs[i].scene.deselect_all();
|
||||
let subtract = SubtractCommand::new(bases);
|
||||
self.command_line.push_info(&subtract.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(subtract));
|
||||
}
|
||||
|
||||
// INTERFERE — non-destructive intersect: solid from the overlap.
|
||||
"INTERFERE" => {
|
||||
return Some(self.solid_interfere());
|
||||
|
|
@ -857,33 +1039,21 @@ impl OpenCADStudio {
|
|||
use acadrust::types::Vector3;
|
||||
let mut loops: Vec<Vec<Vector3>> = Vec::new();
|
||||
for (_, e) in self.tabs[i].scene.selected_entities().iter() {
|
||||
match e {
|
||||
let supported = matches!(
|
||||
e,
|
||||
acadrust::EntityType::LwPolyline(pl)
|
||||
if pl.is_closed && pl.vertices.len() >= 3 =>
|
||||
{
|
||||
loops.push(
|
||||
pl.vertices
|
||||
.iter()
|
||||
.map(|v| Vector3::new(v.location.x, v.location.y, 0.0))
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
acadrust::EntityType::Circle(c) => {
|
||||
let n = 64;
|
||||
loops.push(
|
||||
(0..n)
|
||||
.map(|k| {
|
||||
let a = std::f64::consts::TAU * k as f64 / n as f64;
|
||||
Vector3::new(
|
||||
c.center.x + c.radius * a.cos(),
|
||||
c.center.y + c.radius * a.sin(),
|
||||
c.center.z,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
if pl.is_closed && pl.vertices.len() >= 3
|
||||
) || matches!(e, acadrust::EntityType::Circle(_));
|
||||
if supported {
|
||||
let Some(curve) = crate::entities::curve::entity_curve(e) else {
|
||||
continue;
|
||||
};
|
||||
loops.push(
|
||||
crate::entities::curve::curve_points(&curve)
|
||||
.into_iter()
|
||||
.map(|point| Vector3::new(point[0], point[1], point[2]))
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
}
|
||||
if loops.is_empty() {
|
||||
|
|
@ -908,17 +1078,7 @@ impl OpenCADStudio {
|
|||
}
|
||||
}
|
||||
|
||||
// PYRAMID <radius> <height> [sides] — create an n-sided pyramid mesh.
|
||||
"PYRAMID" | "PYR" => {
|
||||
use crate::command::TwoValuePromptCommand;
|
||||
let c = TwoValuePromptCommand::new(
|
||||
"PYRAMID",
|
||||
"PYRAMID base radius:",
|
||||
"PYRAMID height (add sides by typing a 3rd number):",
|
||||
);
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
}
|
||||
// PYRAMID <radius> <height> [sides] keeps the direct numeric form.
|
||||
cmd if cmd.starts_with("PYRAMID ") || cmd.starts_with("PYR ") => {
|
||||
let nums: Vec<f64> = cmd
|
||||
.split_whitespace()
|
||||
|
|
|
|||
|
|
@ -286,6 +286,34 @@ impl OpenCADStudio {
|
|||
self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
|
||||
}
|
||||
|
||||
"MLEDIT" => {
|
||||
use crate::modules::draw::modify::mledit::{
|
||||
MlineEditCommand, MlineEditTarget,
|
||||
};
|
||||
let document = &self.tabs[i].scene.document;
|
||||
let targets = document
|
||||
.entities()
|
||||
.filter_map(|entity| {
|
||||
let acadrust::EntityType::MLine(mline) = entity else {
|
||||
return None;
|
||||
};
|
||||
let style = crate::entities::mline::resolved_mline_style(mline, document)
|
||||
.cloned()
|
||||
.unwrap_or_else(acadrust::objects::MLineStyle::standard);
|
||||
Some((
|
||||
entity.common().handle.value(),
|
||||
MlineEditTarget {
|
||||
entity: mline.clone(),
|
||||
style,
|
||||
},
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
let command = MlineEditCommand::new(targets);
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
|
||||
"SPLINEDIT" => {
|
||||
use crate::modules::draw::modify::splinedit::SplineditCommand;
|
||||
let cmd_obj = SplineditCommand::new();
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ mod view;
|
|||
// `DrawOrderRefCommand` lives in the `view` family file but is referenced by
|
||||
// path (`commands::DrawOrderRefCommand`) from `update.rs`, so re-export it at
|
||||
// the module root to keep that path valid.
|
||||
pub(crate) use view::DrawOrderRefCommand;
|
||||
pub(crate) use view::DrawOrderCommand;
|
||||
|
||||
impl OpenCADStudio {
|
||||
/// First `"{prefix}{n}"` (n ≥ 1) not already used by a block record in the
|
||||
|
|
@ -397,9 +397,11 @@ inventory::submit!(crate::command::CommandRegistration {
|
|||
"CUILOAD",
|
||||
// Save every open drawing.
|
||||
"SAVEALL",
|
||||
// Draw-order: all text/dims to front or back.
|
||||
// Draw-order: all text/dims to front or back; hatches to back.
|
||||
"TEXTTOFRONT",
|
||||
"TEXTTOBACK",
|
||||
"HATCHTOBACK",
|
||||
"HB",
|
||||
// Criteria-based selection (same as QSELECT) + copy with picked base.
|
||||
"FILTER",
|
||||
"FI",
|
||||
|
|
@ -512,12 +514,24 @@ inventory::submit!(crate::command::CommandRegistration {
|
|||
"SHADEDGE",
|
||||
"MAXACTVP",
|
||||
"CMLJUST",
|
||||
"CMLSCALE",
|
||||
"CMLSTYLE",
|
||||
"TEXTQLTY",
|
||||
"SORTENTS",
|
||||
"FRAME",
|
||||
"IMAGEFRAME",
|
||||
"PDFFRAME",
|
||||
"POINTCLOUDCLIPFRAME",
|
||||
"XCLIPFRAME",
|
||||
"WIPEOUTFRAME",
|
||||
"FRAMES0",
|
||||
"FRAMES1",
|
||||
"FRAMES2",
|
||||
"HALOGAP",
|
||||
"TRACEWID",
|
||||
"SKETCHINC",
|
||||
"SKPOLY",
|
||||
"SKTOLERANCE",
|
||||
// Reset selected entities' overrides to follow their layer.
|
||||
"SETBYLAYER",
|
||||
// Remove duplicate objects; set drawing base point; audit integrity;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ use super::*;
|
|||
impl OpenCADStudio {
|
||||
pub(super) fn dispatch_styleprops(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
match cmd {
|
||||
"FRAMES0" => return self.dispatch_styleprops("SETVAR FRAME 0", i),
|
||||
"FRAMES1" => return self.dispatch_styleprops("SETVAR FRAME 1", i),
|
||||
"FRAMES2" => return self.dispatch_styleprops("SETVAR FRAME 2", i),
|
||||
// COLOR <ByLayer|ByBlock|1-255|name> — the colour applied to new
|
||||
// objects (CECOLOR). Bare COLOR reports the current value.
|
||||
"COLOR" | "COLOUR" | "CECOLOR" | "DDCOLOR" => {
|
||||
|
|
@ -917,12 +920,29 @@ impl OpenCADStudio {
|
|||
| "SHADEDGE"
|
||||
| "MAXACTVP"
|
||||
| "CMLJUST"
|
||||
| "CMLSCALE"
|
||||
| "CMLSTYLE"
|
||||
| "TEXTQLTY"
|
||||
| "SORTENTS"
|
||||
| "FRAME"
|
||||
| "IMAGEFRAME"
|
||||
| "PDFFRAME"
|
||||
| "POINTCLOUDCLIPFRAME"
|
||||
| "XCLIPFRAME"
|
||||
| "WIPEOUTFRAME"
|
||||
| "HALOGAP"
|
||||
| "TRACEWID"
|
||||
| "SKETCHINC"
|
||||
| "SKPOLY"
|
||||
| "SKTOLERANCE"
|
||||
| "CENTEREXE"
|
||||
| "CENTERLAYER"
|
||||
| "CENTERLTYPE"
|
||||
| "CENTERLTSCALE"
|
||||
| "CENTERLTYPEFILE"
|
||||
| "CENTERCROSSSIZE"
|
||||
| "CENTERCROSSGAP"
|
||||
| "CENTERMARKEXE"
|
||||
) =>
|
||||
{
|
||||
return self.dispatch_styleprops(&format!("SETVAR {cmd}"), i);
|
||||
|
|
@ -945,9 +965,122 @@ impl OpenCADStudio {
|
|||
let value = it.next().map(|s| s.trim().to_string());
|
||||
if name.is_empty() || name == "?" {
|
||||
self.command_line.push_info(
|
||||
"SETVAR: LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT ZOOMWHEEL ZOOMFACTOR CURSORSIZE PICKBOX CURSORTYPE SNAPANG ATTREQ ATTDIA DIMASSOC ANGBASE ANGDIR | CLAYER CELTYPE TEXTSTYLE (read-only)",
|
||||
"SETVAR: LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR CURSORSIZE PICKBOX CURSORTYPE SNAPANG ATTREQ ATTDIA DIMASSOC ANGBASE ANGDIR SKETCHINC SKPOLY SKTOLERANCE CENTEREXE CENTERLAYER CENTERLTYPE CENTERLTSCALE CENTERLTYPEFILE CENTERCROSSSIZE CENTERCROSSGAP CENTERMARKEXE | CLAYER CELTYPE TEXTSTYLE (read-only)",
|
||||
);
|
||||
} else {
|
||||
let frame_kind = crate::scene::frame::kind_for_name(&name);
|
||||
if name == "FRAME" || frame_kind.is_some() {
|
||||
let current = frame_kind.map_or_else(
|
||||
|| crate::scene::frame::master_mode(&self.tabs[i].scene.document),
|
||||
|kind| crate::scene::frame::mode(&self.tabs[i].scene.document, kind),
|
||||
);
|
||||
match &value {
|
||||
Some(value) => match value.parse::<i16>() {
|
||||
Ok(mode @ 0..=2) => {
|
||||
if current != mode {
|
||||
let changed_kinds: Vec<_> = frame_kind.map_or_else(
|
||||
|| {
|
||||
crate::scene::frame::ALL_KINDS
|
||||
.into_iter()
|
||||
.filter(|kind| {
|
||||
crate::scene::frame::mode(
|
||||
&self.tabs[i].scene.document,
|
||||
*kind,
|
||||
) != mode
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
|kind| vec![kind],
|
||||
);
|
||||
self.push_undo_snapshot(i, &name);
|
||||
if let Some(kind) = frame_kind {
|
||||
crate::scene::frame::set_mode(
|
||||
&mut self.tabs[i].scene.document,
|
||||
kind,
|
||||
mode,
|
||||
);
|
||||
} else {
|
||||
crate::scene::frame::set_master_mode(
|
||||
&mut self.tabs[i].scene.document,
|
||||
mode,
|
||||
);
|
||||
}
|
||||
let changes: Vec<_> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter_map(|entity| {
|
||||
changed_kinds
|
||||
.iter()
|
||||
.any(|kind| {
|
||||
crate::scene::frame::affected(entity, *kind)
|
||||
})
|
||||
.then_some((
|
||||
entity.common().handle,
|
||||
crate::scene::ChangeKind::Modified,
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
if !changes.is_empty() {
|
||||
self.tabs[i].scene.bump_entities(&changes);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
self.command_line
|
||||
.push_output(&format!("{name} = {mode}"));
|
||||
}
|
||||
_ => self.command_line.push_error(
|
||||
crate::tf!("SETVAR: {name} requires 0, 1, or 2.").as_ref(),
|
||||
),
|
||||
},
|
||||
None => {
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"Enter new value for {name} <{current}>:"
|
||||
).as_ref());
|
||||
self.pending_setvar = Some(name.clone());
|
||||
}
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
if matches!(
|
||||
name.as_str(),
|
||||
"CENTEREXE"
|
||||
| "CENTERLAYER"
|
||||
| "CENTERLTYPE"
|
||||
| "CENTERLTSCALE"
|
||||
| "CENTERLTYPEFILE"
|
||||
| "CENTERCROSSSIZE"
|
||||
| "CENTERCROSSGAP"
|
||||
| "CENTERMARKEXE"
|
||||
) {
|
||||
let settings = self.tabs[i].scene.centerline_settings();
|
||||
let current = match name.as_str() {
|
||||
"CENTEREXE" => settings.extension.to_string(),
|
||||
"CENTERLAYER" => settings.layer,
|
||||
"CENTERLTYPE" => settings.linetype,
|
||||
"CENTERLTSCALE" => settings.linetype_scale.to_string(),
|
||||
"CENTERLTYPEFILE" => settings.linetype_file,
|
||||
"CENTERCROSSSIZE" => settings.cross_size,
|
||||
"CENTERCROSSGAP" => settings.cross_gap,
|
||||
"CENTERMARKEXE" => i16::from(settings.mark_extensions).to_string(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if let Some(value) = &value {
|
||||
match self.tabs[i].scene.set_centerline_setting(&name, value) {
|
||||
Ok(message) => {
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(&message);
|
||||
}
|
||||
Err(error) => self.command_line.push_error(&error),
|
||||
}
|
||||
} else {
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"Enter new value for {name} <{current}>:"
|
||||
).as_ref());
|
||||
self.pending_setvar = Some(name.clone());
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
// Parse a boolean given as 0/1 or ON/OFF.
|
||||
let parse_bool = |s: &str| match s.to_uppercase().as_str() {
|
||||
"1" | "ON" | "TRUE" => Some(true),
|
||||
|
|
@ -1566,17 +1699,35 @@ impl OpenCADStudio {
|
|||
}
|
||||
},
|
||||
"CMLJUST" => match &value {
|
||||
Some(v) => v
|
||||
.parse::<i16>()
|
||||
.map(|x| {
|
||||
Some(v) => match v.parse::<i16>() {
|
||||
Ok(x @ 0..=2) => {
|
||||
h.multiline_justification = x;
|
||||
(format!("CMLJUST = {x}"), true)
|
||||
})
|
||||
.map_err(|_| "SETVAR: integer value required.".into()),
|
||||
Ok((format!("CMLJUST = {x}"), true))
|
||||
}
|
||||
_ => Err("SETVAR: integer value from 0 to 2 required.".into()),
|
||||
},
|
||||
None => {
|
||||
Ok((format!("CMLJUST = {}", h.multiline_justification), false))
|
||||
}
|
||||
},
|
||||
"CMLSCALE" => match &value {
|
||||
Some(v) => match v.parse::<f64>() {
|
||||
Ok(x) if x.is_finite() => {
|
||||
let changed = h.multiline_scale != x;
|
||||
h.multiline_scale = x;
|
||||
Ok((format!("CMLSCALE = {x}"), changed))
|
||||
}
|
||||
_ => Err("SETVAR: finite numeric value required.".into()),
|
||||
},
|
||||
None => Ok((format!("CMLSCALE = {}", h.multiline_scale), false)),
|
||||
},
|
||||
"CMLSTYLE" => match &value {
|
||||
Some(_) => Err(
|
||||
"SETVAR: CMLSTYLE is read-only here — use the MLSTYLE command."
|
||||
.into(),
|
||||
),
|
||||
None => Ok((format!("CMLSTYLE = {}", h.multiline_style), false)),
|
||||
},
|
||||
"TEXTQLTY" => match &value {
|
||||
Some(v) => v
|
||||
.parse::<i16>()
|
||||
|
|
@ -1597,16 +1748,6 @@ impl OpenCADStudio {
|
|||
.map_err(|_| "SETVAR: integer value required.".into()),
|
||||
None => Ok((format!("SORTENTS = {}", h.sort_entities), false)),
|
||||
},
|
||||
"XCLIPFRAME" => match &value {
|
||||
Some(v) => v
|
||||
.parse::<i16>()
|
||||
.map(|x| {
|
||||
h.xclip_frame = x;
|
||||
(format!("XCLIPFRAME = {x}"), true)
|
||||
})
|
||||
.map_err(|_| "SETVAR: integer value required.".into()),
|
||||
None => Ok((format!("XCLIPFRAME = {}", h.xclip_frame), false)),
|
||||
},
|
||||
"HALOGAP" => match &value {
|
||||
Some(v) => v
|
||||
.parse::<i16>()
|
||||
|
|
@ -1628,15 +1769,41 @@ impl OpenCADStudio {
|
|||
None => Ok((format!("TRACEWID = {}", h.trace_width), false)),
|
||||
},
|
||||
"SKETCHINC" => match &value {
|
||||
Some(v) => v
|
||||
.parse::<f64>()
|
||||
.map(|x| {
|
||||
Some(v) => match v.parse::<f64>() {
|
||||
Ok(x) if x.is_finite() && x > 0.0 => {
|
||||
let changed = h.sketch_increment != x;
|
||||
h.sketch_increment = x;
|
||||
(format!("SKETCHINC = {x}"), true)
|
||||
})
|
||||
.map_err(|_| "SETVAR: numeric value required.".into()),
|
||||
Ok((format!("SKETCHINC = {x}"), changed))
|
||||
}
|
||||
_ => Err("SETVAR: positive numeric value required.".into()),
|
||||
},
|
||||
None => Ok((format!("SKETCHINC = {}", h.sketch_increment), false)),
|
||||
},
|
||||
"SKPOLY" => match &value {
|
||||
Some(v) => match v.parse::<i16>() {
|
||||
Ok(x @ 0..=2) => {
|
||||
let changed = h.sketch_type != x;
|
||||
h.sketch_type = x;
|
||||
Ok((format!("SKPOLY = {x}"), changed))
|
||||
}
|
||||
_ => Err("SETVAR: integer value from 0 to 2 required.".into()),
|
||||
},
|
||||
None => Ok((format!("SKPOLY = {}", h.sketch_type), false)),
|
||||
},
|
||||
"SKTOLERANCE" => match &value {
|
||||
Some(v) => match v.parse::<f64>() {
|
||||
Ok(x) if x.is_finite() && (0.0..=1.0).contains(&x) => {
|
||||
let changed = h.sketch_tolerance != x;
|
||||
h.sketch_tolerance = x;
|
||||
Ok((format!("SKTOLERANCE = {x}"), changed))
|
||||
}
|
||||
_ => Err("SETVAR: numeric value from 0 to 1 required.".into()),
|
||||
},
|
||||
None => Ok((
|
||||
format!("SKTOLERANCE = {}", h.sketch_tolerance),
|
||||
false,
|
||||
)),
|
||||
},
|
||||
"CLAYER" => match &value {
|
||||
Some(_) => Err(
|
||||
"SETVAR: CLAYER is read-only here — use the CLAYER command."
|
||||
|
|
@ -1987,7 +2154,17 @@ impl OpenCADStudio {
|
|||
}
|
||||
"LTSCALE" => {
|
||||
use crate::command::ValuePromptCommand;
|
||||
let c = ValuePromptCommand::new("LTSCALE", "LTSCALE new global line-type scale:");
|
||||
|
||||
let current = self.tabs[i].scene.document.header.linetype_scale;
|
||||
|
||||
self.command_line
|
||||
.push_output(crate::tf!("LTSCALE = {current:.4}").as_ref());
|
||||
|
||||
let c = ValuePromptCommand::new(
|
||||
"LTSCALE",
|
||||
"LTSCALE new global line-type scale:",
|
||||
);
|
||||
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
}
|
||||
|
|
@ -2162,7 +2339,7 @@ impl OpenCADStudio {
|
|||
use crate::command::ValuePromptCommand;
|
||||
let c = ValuePromptCommand::new(
|
||||
"PDSIZE",
|
||||
"PDSIZE new point size (0 = 5% of viewport, <0 = absolute):",
|
||||
"PDSIZE new point size (0 = 5% of viewport, >0 = absolute, <0 = viewport percentage):",
|
||||
);
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
|
|
|
|||
|
|
@ -195,10 +195,6 @@ pub(super) struct DocumentTab {
|
|||
pub(super) active_mleader_style: String,
|
||||
/// Last camera_generation value written back to the document.
|
||||
pub(super) last_synced_camera_gen: u64,
|
||||
/// Render-state key of `scene.document.preview`. Matching saves reuse the
|
||||
/// encoded DWG thumbnail instead of rescanning every resident wire.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(super) thumbnail_cache_key: Option<super::ThumbnailCacheKey>,
|
||||
/// Sentinel "Welcome / Start" tab. Always at index 0 when present.
|
||||
/// Cannot be closed; the viewport area renders a welcome page instead
|
||||
/// of the model-space shader. The scene is still constructed so the
|
||||
|
|
@ -486,8 +482,6 @@ impl DocumentTab {
|
|||
active_block_edit: None,
|
||||
active_mleader_style: "Standard".to_string(),
|
||||
last_synced_camera_gen: 0,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
thumbnail_cache_key: None,
|
||||
is_start: false,
|
||||
pan_mode: false,
|
||||
orbit_mode: false,
|
||||
|
|
|
|||
|
|
@ -433,6 +433,9 @@ pub fn eval_to_string(input: &str) -> String {
|
|||
let mut found = false;
|
||||
// Try longest substring first (greedy)
|
||||
for end in (i + 1..=len).rev() {
|
||||
if !starts_and_ends_on_word_boundary(&chars, i, end) {
|
||||
continue;
|
||||
}
|
||||
let sub: String = chars[i..end].iter().collect();
|
||||
if let Some(v) = eval_number(&sub) {
|
||||
result.push_str(&format!("{}", v));
|
||||
|
|
@ -449,6 +452,37 @@ pub fn eval_to_string(input: &str) -> String {
|
|||
result
|
||||
}
|
||||
|
||||
/// Rejects expression candidates cut from a surrounding identifier.
|
||||
fn starts_and_ends_on_word_boundary(chars: &[char], start: usize, end: usize) -> bool {
|
||||
fn is_word(c: char) -> bool {
|
||||
c.is_alphanumeric() || c == '_'
|
||||
}
|
||||
fn starts_ident(c: char) -> bool {
|
||||
c.is_alphabetic() || c == '_'
|
||||
}
|
||||
|
||||
// First token is an identifier: a word character just before it means this
|
||||
// is the tail of a longer word rather than a name of its own.
|
||||
if starts_ident(chars[start]) && start > 0 && is_word(chars[start - 1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Last token is an identifier when the trailing run of word characters
|
||||
// begins with a letter or `_`. A word character just after `end` means that
|
||||
// identifier continues past the candidate.
|
||||
if end < chars.len() && is_word(chars[end]) {
|
||||
let mut run = end;
|
||||
while run > start && is_word(chars[run - 1]) {
|
||||
run -= 1;
|
||||
}
|
||||
if run < end && starts_ident(chars[run]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
// ── Tests ───────────────────────────────────────────────────────────
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -615,6 +649,27 @@ mod tests {
|
|||
assert_eq!(eval_to_string("abs(-5),ceil(3.1)"), "5,4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn embedded_identifier_inside_a_word_is_left_alone() {
|
||||
// Known names must stay unchanged inside identifiers.
|
||||
assert_eq!(eval_to_string("ASPIRATORE"), "ASPIRATORE");
|
||||
assert_eq!(eval_to_string("PIRATE"), "PIRATE");
|
||||
assert_eq!(eval_to_string("copies"), "copies");
|
||||
assert_eq!(eval_to_string("TOPI"), "TOPI");
|
||||
assert_eq!(eval_to_string("pi_2"), "pi_2");
|
||||
assert_eq!(eval_to_string("şpiş"), "şpiş");
|
||||
assert_eq!(eval_to_string("копияpi"), "копияpi");
|
||||
// A standalone `pi` still is a constant.
|
||||
assert_eq!(
|
||||
eval_to_string("pipe,pi"),
|
||||
format!("pipe,{}", std::f64::consts::PI)
|
||||
);
|
||||
assert_eq!(
|
||||
eval_to_string("2*pi,0"),
|
||||
format!("{},0", 2.0 * std::f64::consts::PI)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn embedded_expr_no_evaluables() {
|
||||
assert_eq!(eval_to_string("abc,def"), "abc,def");
|
||||
|
|
|
|||
|
|
@ -668,9 +668,8 @@ impl OpenCADStudio {
|
|||
true
|
||||
}
|
||||
|
||||
/// Add is delta-safe on an already-existing layer unless it creates block
|
||||
/// records. RasterImage definitions are captured as exact object-map deltas;
|
||||
/// Viewport commits explicitly record their added entity handle.
|
||||
/// Add is delta-safe when its layer and XData application IDs already exist
|
||||
/// and it creates no block records.
|
||||
pub(super) fn delta_add_safe(&self, i: usize, entity: &EntityType) -> bool {
|
||||
if matches!(
|
||||
entity,
|
||||
|
|
@ -680,7 +679,18 @@ impl OpenCADStudio {
|
|||
return false;
|
||||
}
|
||||
let layer = entity.common().layer.clone();
|
||||
layer.trim().is_empty() || self.tabs[i].scene.document.layers.contains(&layer)
|
||||
let doc = &self.tabs[i].scene.document;
|
||||
let layer_exists = layer.trim().is_empty() || doc.layers.contains(&layer);
|
||||
let app_ids_exist = entity
|
||||
.common()
|
||||
.extended_data
|
||||
.records()
|
||||
.iter()
|
||||
.all(|record| {
|
||||
record.application_name.trim().is_empty()
|
||||
|| doc.app_ids.contains(&record.application_name)
|
||||
});
|
||||
layer_exists && app_ids_exist
|
||||
}
|
||||
|
||||
/// Close the delta transaction opened by [`OpenCADStudio::begin_undo`]:
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ pub struct GripPopup {
|
|||
pub anchor: iced::Point,
|
||||
pub items: Vec<crate::scene::model::object::GripMenuItem>,
|
||||
pub selected: usize,
|
||||
/// Whether a click-opened menu stays visible away from its grip.
|
||||
pub pinned: bool,
|
||||
}
|
||||
|
||||
/// Pending follow-up value for grip-menu actions that need a number
|
||||
|
|
@ -606,6 +608,12 @@ pub(super) struct OpenCADStudio {
|
|||
/// OS window Id for the floating Layer Properties Manager (None when closed).
|
||||
/// OS window Id of the primary application window.
|
||||
main_window: Option<window::Id>,
|
||||
/// Hides drawing UI overlays for one thumbnail capture frame.
|
||||
thumbnail_capture_clean: bool,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pending_native_thumbnail_save: Option<PendingNativeThumbnailSave>,
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pending_web_thumbnail_save: Option<PendingWebThumbnailSave>,
|
||||
// ── Floating panel windows ────────────────────────────────────────────
|
||||
/// Active `iced_aw` colour picker: destination plus its initial colour.
|
||||
color_pick_target: Option<(ColorPickTarget, AcadColor)>,
|
||||
|
|
@ -1129,6 +1137,28 @@ pub(super) enum SaveContinuation {
|
|||
Quit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(super) struct PendingNativeThumbnailSave {
|
||||
tab_id: u64,
|
||||
path: PathBuf,
|
||||
version: acadrust::DxfVersion,
|
||||
purpose: SavePurpose,
|
||||
continuation: SaveContinuation,
|
||||
set_current_path: bool,
|
||||
check_external_change: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(super) struct PendingWebThumbnailSave {
|
||||
tab_id: u64,
|
||||
filename: String,
|
||||
ext: String,
|
||||
version: acadrust::DxfVersion,
|
||||
bounds: iced::Rectangle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(super) struct PendingSaveFailure {
|
||||
|
|
@ -1152,16 +1182,6 @@ pub(super) struct PendingExternalChange {
|
|||
set_current_path: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(super) struct ThumbnailCacheKey {
|
||||
epoch: u64,
|
||||
camera_generation: u64,
|
||||
bg_color: [u32; 4],
|
||||
png: bool,
|
||||
viewport: [u32; 2],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub struct SaveOutcome {
|
||||
|
|
@ -1176,7 +1196,6 @@ pub struct SaveOutcome {
|
|||
set_current_path: bool,
|
||||
purpose: SavePurpose,
|
||||
continuation: SaveContinuation,
|
||||
thumbnail_key: Option<ThumbnailCacheKey>,
|
||||
refreshed_preview: Option<Option<acadrust::Preview>>,
|
||||
result: Result<(), crate::io::SaveFailure>,
|
||||
}
|
||||
|
|
@ -1913,9 +1932,23 @@ pub enum Message {
|
|||
AecDropBack,
|
||||
/// Periodic autosave tick — write `.sv$` recovery files for dirty tabs.
|
||||
AutoSave,
|
||||
/// A clean viewport frame is ready for thumbnail capture.
|
||||
ThumbnailCaptureFrame,
|
||||
/// Restore drawing UI after the compositor screenshot is captured.
|
||||
ThumbnailCaptureFinished,
|
||||
/// Native background save/autosave completed.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
SaveFinished(SaveOutcome),
|
||||
/// Web viewport capture completed; serialize and download the drawing.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
WebSaveScreenshot {
|
||||
tab_id: u64,
|
||||
filename: String,
|
||||
ext: String,
|
||||
version: acadrust::DxfVersion,
|
||||
bounds: Option<iced::Rectangle>,
|
||||
screenshot: Option<iced::window::Screenshot>,
|
||||
},
|
||||
/// Retry the failed save after the other application releases the file.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
SaveFileInUseRetry,
|
||||
|
|
@ -1970,9 +2003,9 @@ pub enum Message {
|
|||
},
|
||||
/// A widget captured Up/Down; resolve it only if the command input owns
|
||||
/// keyboard focus.
|
||||
CommandLineArrowProbe { direction: ArrowKey },
|
||||
CommandLineArrowProbe { direction: ArrowKey, extend_selection: bool },
|
||||
/// Result of the command-input focus query for a captured Up/Down key.
|
||||
CommandLineArrowResolved { direction: ArrowKey, focused: bool },
|
||||
CommandLineArrowResolved { direction: ArrowKey, focused: bool, extend_selection: bool },
|
||||
/// Toggle the dropdown listing the full command-line history.
|
||||
CommandHistoryToggle,
|
||||
/// Grab/move/release the expanded history panel's top resize edge.
|
||||
|
|
@ -2011,6 +2044,7 @@ pub enum Message {
|
|||
LayerToggleVisible(usize),
|
||||
LayerToggleLock(usize),
|
||||
LayerToggleFreeze(usize),
|
||||
LayerTogglePlot(usize),
|
||||
/// Sort the Layer Manager table by a clicked column header.
|
||||
LayerSort(crate::ui::window::layers::LayerSortCol),
|
||||
/// Toggle per-viewport freeze: (layer_index, vp_col_index)
|
||||
|
|
@ -3158,6 +3192,11 @@ impl OpenCADStudio {
|
|||
show_layout_tabs: true,
|
||||
last_point: None,
|
||||
main_window: None,
|
||||
thumbnail_capture_clean: false,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pending_native_thumbnail_save: None,
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pending_web_thumbnail_save: None,
|
||||
color_pick_target: None,
|
||||
color_picker_tab: ColorPickerTab::Index,
|
||||
recent_colors: Vec::new(),
|
||||
|
|
@ -3723,6 +3762,7 @@ pub fn run_web() -> iced::Result {
|
|||
.subscription(OpenCADStudio::subscription)
|
||||
.title(|_state: &OpenCADStudio| "Open CAD Studio".to_string())
|
||||
.theme(|state: &OpenCADStudio| state.active_theme.clone())
|
||||
.backend(iced::Backend::Hardware(iced::backend::Api::OpenGL))
|
||||
.font(iced_aw::ICED_AW_FONT_BYTES)
|
||||
.run()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,9 @@
|
|||
// 3D solid modelling support on the App: committing Model-tab primitives,
|
||||
// and the Design-group boolean operations over the scene's session-cached
|
||||
// B-reps.
|
||||
//
|
||||
// The bodies come from the geometry kernel, so every operation here is on
|
||||
// analytic surfaces rather than on facets — a turned cylinder is still a
|
||||
// cylinder afterwards, and saves as one.
|
||||
// Kernel B-rep solid modelling and exact ACIS persistence.
|
||||
|
||||
use acadrust::entities::Solid3D;
|
||||
use acadrust::objects::SolidHistoryOperation;
|
||||
use acadrust::{
|
||||
entities::Solid3D, objects::SolidHistoryOperation, EntityType, Handle,
|
||||
};
|
||||
use cadkernel::brep::Body;
|
||||
use acadrust::{EntityType, Handle};
|
||||
use iced::Task;
|
||||
|
||||
use super::Message;
|
||||
|
|
@ -18,16 +12,25 @@ use crate::scene::model::solid_history;
|
|||
use crate::scene::model::solid_model::{self, Bool};
|
||||
|
||||
impl super::OpenCADStudio {
|
||||
/// Commit a Model-tab solid: add its acadrust entity to the document, then
|
||||
/// register the B-rep (caches it for booleans + tessellates it into the
|
||||
/// shaded mesh pipeline). Returns the new entity handle.
|
||||
/// Add a solid and register its persistent B-rep.
|
||||
pub(super) fn add_solid_model(
|
||||
&mut self,
|
||||
entity: EntityType,
|
||||
mut entity: EntityType,
|
||||
solid: Body,
|
||||
history: SolidHistoryOperation,
|
||||
) -> Handle {
|
||||
let i = self.active_tab;
|
||||
let EntityType::Solid3D(inner) = &mut entity else {
|
||||
return Handle::NULL;
|
||||
};
|
||||
inner.wires = solid_model::edge_wires(&solid);
|
||||
let Some(document) = crate::scene::convert::acis_export::solid_to_sat(&solid)
|
||||
else {
|
||||
self.command_line
|
||||
.push_error(crate::t!("The solid could not be encoded as ACIS.").as_ref());
|
||||
return Handle::NULL;
|
||||
};
|
||||
inner.set_sat_document(&document);
|
||||
let Some(handle) = self.commit_entity_handle(entity) else {
|
||||
return Handle::NULL;
|
||||
};
|
||||
|
|
@ -36,43 +39,202 @@ impl super::OpenCADStudio {
|
|||
handle
|
||||
}
|
||||
|
||||
/// Run a boolean (`union` / `subtract` / `intersect`) on exactly two
|
||||
/// selected solids whose B-reps are in the session cache.
|
||||
pub(super) fn solid_boolean(&mut self, op: BoolOp) -> Task<Message> {
|
||||
fn selected_solid_handles(&mut self) -> Vec<Handle> {
|
||||
let i = self.active_tab;
|
||||
// Selected entities that have a cached B-rep.
|
||||
let handles: Vec<Handle> = self.tabs[i]
|
||||
let mut handles: Vec<Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|h| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.filter(|h| self.tabs[i].scene.solid_models.contains_key(h))
|
||||
.selected_handles_in_order()
|
||||
.into_iter()
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.filter(|handle| {
|
||||
matches!(
|
||||
self.tabs[i].scene.document.get_entity(*handle),
|
||||
Some(EntityType::Solid3D(_))
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
if handles.len() != 2 {
|
||||
self.tabs[i].scene.restore_solid_models(&handles);
|
||||
handles.retain(|handle| self.tabs[i].scene.solid_models.contains_key(handle));
|
||||
handles
|
||||
}
|
||||
|
||||
fn replace_solid_body(&mut self, handle: Handle, result: Body, label: &str) -> bool {
|
||||
let i = self.active_tab;
|
||||
let Some(document) =
|
||||
crate::scene::convert::acis_export::solid_to_sat(&result)
|
||||
else {
|
||||
self.command_line
|
||||
.push_error(crate::t!("Boolean: select exactly two solids created this session.").as_ref());
|
||||
.push_error(crate::t!("The result could not be encoded as ACIS.").as_ref());
|
||||
return false;
|
||||
};
|
||||
let Some(EntityType::Solid3D(mut entity)) =
|
||||
self.tabs[i].scene.document.get_entity(handle).cloned()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
entity.wires = solid_model::edge_wires(&result);
|
||||
entity.silhouettes.clear();
|
||||
entity.history_handle = None;
|
||||
entity.set_sat_document(&document);
|
||||
|
||||
self.push_undo_snapshot(i, label);
|
||||
self.tabs[i].scene.delete_solid_history(handle);
|
||||
if !self.tabs[i]
|
||||
.scene
|
||||
.update_entity(EntityType::Solid3D(entity))
|
||||
{
|
||||
self.command_line
|
||||
.push_error(crate::t!("The solid could not be updated.").as_ref());
|
||||
return false;
|
||||
}
|
||||
let history = solid_history::brep_op(&result);
|
||||
self.tabs[i].scene.create_solid_history(handle, history);
|
||||
self.tabs[i].scene.register_solid_model(handle, result);
|
||||
self.tabs[i].scene.deselect_all();
|
||||
self.tabs[i].scene.select_entity(handle, false);
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) fn solid_edge_blend(
|
||||
&mut self,
|
||||
handle: Handle,
|
||||
pick: glam::DVec3,
|
||||
value: f64,
|
||||
fillet: bool,
|
||||
) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
if self.reject_locked_edit(i, handle) {
|
||||
return Task::none();
|
||||
}
|
||||
if !matches!(
|
||||
self.tabs[i].scene.document.get_entity(handle),
|
||||
Some(EntityType::Solid3D(_))
|
||||
) {
|
||||
self.command_line
|
||||
.push_error(crate::t!("Select a 3D solid edge.").as_ref());
|
||||
return Task::none();
|
||||
}
|
||||
self.tabs[i].scene.restore_solid_models(&[handle]);
|
||||
let Some(body) = self.tabs[i].scene.solid_models.get(&handle).cloned() else {
|
||||
self.command_line
|
||||
.push_error(crate::t!("The solid geometry could not be restored.").as_ref());
|
||||
return Task::none();
|
||||
};
|
||||
let Some(edge) = solid_model::nearest_edge(&body, pick.to_array()) else {
|
||||
self.command_line
|
||||
.push_error(crate::t!("Select a solid edge.").as_ref());
|
||||
return Task::none();
|
||||
};
|
||||
let result = if fillet {
|
||||
cadkernel::brep::fillet(&body, edge, value)
|
||||
} else {
|
||||
cadkernel::brep::chamfer(&body, edge, value)
|
||||
};
|
||||
let Some(result) = result else {
|
||||
self.command_line.push_error(
|
||||
crate::t!("Edge operation failed. Use a convex planar solid and a smaller value.")
|
||||
.as_ref(),
|
||||
);
|
||||
return Task::none();
|
||||
};
|
||||
let label = if fillet { "SOLIDFILLET" } else { "SOLIDCHAMFER" };
|
||||
if self.replace_solid_body(handle, result, label) {
|
||||
self.command_line
|
||||
.push_output(crate::tf!("{label}: solid updated.").as_ref());
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
pub(super) fn solid_face_presspull(
|
||||
&mut self,
|
||||
handle: Handle,
|
||||
pick: glam::DVec3,
|
||||
distance: f64,
|
||||
drag: Option<glam::DVec3>,
|
||||
) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
if self.reject_locked_edit(i, handle) {
|
||||
return Task::none();
|
||||
}
|
||||
self.tabs[i].scene.restore_solid_models(&[handle]);
|
||||
let Some(body) = self.tabs[i].scene.solid_models.get(&handle).cloned() else {
|
||||
self.command_line
|
||||
.push_error(crate::t!("The solid geometry could not be restored.").as_ref());
|
||||
return Task::none();
|
||||
};
|
||||
let Some(face) = solid_model::nearest_planar_face(&body, pick.to_array()) else {
|
||||
self.command_line
|
||||
.push_error(crate::t!("Select a planar solid face.").as_ref());
|
||||
return Task::none();
|
||||
};
|
||||
let distance = match drag {
|
||||
Some(point) => {
|
||||
let Some(normal) = solid_model::planar_face_normal(&body, face) else {
|
||||
self.command_line
|
||||
.push_error(crate::t!("Select a planar solid face.").as_ref());
|
||||
return Task::none();
|
||||
};
|
||||
(point - pick).dot(glam::DVec3::from_array(normal))
|
||||
}
|
||||
None => distance,
|
||||
};
|
||||
if !distance.is_finite() || distance.abs() <= 1e-6 {
|
||||
self.command_line.push_error(
|
||||
crate::t!("PRESSPULL: drag along the selected face normal.").as_ref(),
|
||||
);
|
||||
return Task::none();
|
||||
}
|
||||
let Some(result) = cadkernel::brep::presspull(&body, face, distance) else {
|
||||
self.command_line.push_error(
|
||||
crate::t!("PRESSPULL: the face move would collapse or invalidate the solid.")
|
||||
.as_ref(),
|
||||
);
|
||||
return Task::none();
|
||||
};
|
||||
if self.replace_solid_body(handle, result, "PRESSPULL") {
|
||||
self.command_line
|
||||
.push_output(crate::t!("PRESSPULL: solid updated.").as_ref());
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// Run a boolean over the selected solids in selection order.
|
||||
pub(super) fn solid_boolean(&mut self, op: BoolOp) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let handles = self.selected_solid_handles();
|
||||
if handles.len() < 2 {
|
||||
self.command_line
|
||||
.push_error(crate::t!("Boolean: select at least two solids.").as_ref());
|
||||
return Task::none();
|
||||
}
|
||||
let a = self.tabs[i].scene.solid_models[&handles[0]].clone();
|
||||
let b = self.tabs[i].scene.solid_models[&handles[1]].clone();
|
||||
let kind = match op {
|
||||
BoolOp::Union => Bool::Union,
|
||||
BoolOp::Subtract => Bool::Subtract,
|
||||
BoolOp::Intersect => Bool::Intersect,
|
||||
};
|
||||
let Some(result) = solid_model::boolean(kind, &a, &b) else {
|
||||
let mut operands = handles
|
||||
.iter()
|
||||
.filter_map(|handle| self.tabs[i].scene.solid_models.get(handle).cloned());
|
||||
let mut result = operands.next().expect("at least two restored solids");
|
||||
for operand in operands {
|
||||
let Some(combined) = solid_model::boolean(kind, &result, &operand) else {
|
||||
self.command_line.push_error(
|
||||
crate::t!("Boolean failed while combining the selected solids.").as_ref(),
|
||||
);
|
||||
return Task::none();
|
||||
};
|
||||
result = combined;
|
||||
}
|
||||
if crate::scene::convert::acis_export::solid_to_sat(&result).is_none() {
|
||||
self.command_line
|
||||
.push_error(crate::t!("Boolean failed — the solids may not overlap.").as_ref());
|
||||
.push_error(crate::t!("The boolean result could not be encoded as ACIS.").as_ref());
|
||||
return Task::none();
|
||||
};
|
||||
}
|
||||
|
||||
self.push_undo_snapshot(i, "BOOLEAN");
|
||||
// Remove the two operands (entity + mesh + cached B-rep).
|
||||
self.tabs[i].scene.erase_entities(&handles);
|
||||
// The result is freshly combined geometry with no ACIS parametrisation,
|
||||
// so it lives as a Solid3D whose render/boolean data is the injected
|
||||
// mesh + cached B-rep; its edge wires make it pickable.
|
||||
let mut s3d = Solid3D::new();
|
||||
s3d.wires = solid_model::edge_wires(&result);
|
||||
let history = solid_history::brep_op(&result);
|
||||
|
|
@ -86,20 +248,87 @@ impl super::OpenCADStudio {
|
|||
Task::none()
|
||||
}
|
||||
|
||||
pub(super) fn solid_subtract(
|
||||
&mut self,
|
||||
bases: &[Handle],
|
||||
cutters: &[Handle],
|
||||
) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let valid_solid = |handle: &Handle| {
|
||||
!self.tabs[i].scene.is_layer_locked(*handle)
|
||||
&& matches!(
|
||||
self.tabs[i].scene.document.get_entity(*handle),
|
||||
Some(EntityType::Solid3D(_))
|
||||
)
|
||||
};
|
||||
let mut base_handles: Vec<_> = bases.iter().copied().filter(valid_solid).collect();
|
||||
let mut cutter_handles: Vec<_> = cutters.iter().copied().filter(valid_solid).collect();
|
||||
cutter_handles.retain(|handle| !base_handles.contains(handle));
|
||||
|
||||
let mut operands = base_handles.clone();
|
||||
operands.extend(cutter_handles.iter().copied());
|
||||
self.tabs[i].scene.restore_solid_models(&operands);
|
||||
base_handles.retain(|handle| self.tabs[i].scene.solid_models.contains_key(handle));
|
||||
cutter_handles.retain(|handle| self.tabs[i].scene.solid_models.contains_key(handle));
|
||||
if base_handles.is_empty() || cutter_handles.is_empty() {
|
||||
self.command_line.push_error(
|
||||
crate::t!("SUBTRACT: select at least one base solid and one cutter solid.")
|
||||
.as_ref(),
|
||||
);
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
let mut result = self.tabs[i].scene.solid_models[&base_handles[0]].clone();
|
||||
for handle in &base_handles[1..] {
|
||||
let operand = &self.tabs[i].scene.solid_models[handle];
|
||||
let Some(combined) = solid_model::boolean(Bool::Union, &result, operand) else {
|
||||
self.command_line.push_error(
|
||||
crate::t!("SUBTRACT failed while combining the base solids.").as_ref(),
|
||||
);
|
||||
return Task::none();
|
||||
};
|
||||
result = combined;
|
||||
}
|
||||
for handle in &cutter_handles {
|
||||
let operand = &self.tabs[i].scene.solid_models[handle];
|
||||
let Some(difference) = solid_model::boolean(Bool::Subtract, &result, operand) else {
|
||||
self.command_line.push_error(
|
||||
crate::t!("SUBTRACT failed while removing the selected solids.").as_ref(),
|
||||
);
|
||||
return Task::none();
|
||||
};
|
||||
result = difference;
|
||||
}
|
||||
if crate::scene::convert::acis_export::solid_to_sat(&result).is_none() {
|
||||
self.command_line
|
||||
.push_error(crate::t!("The boolean result could not be encoded as ACIS.").as_ref());
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
operands = base_handles;
|
||||
operands.extend(cutter_handles);
|
||||
self.push_undo_snapshot(i, "SUBTRACT");
|
||||
self.tabs[i].scene.erase_entities(&operands);
|
||||
let mut entity = Solid3D::new();
|
||||
entity.wires = solid_model::edge_wires(&result);
|
||||
let history = solid_history::brep_op(&result);
|
||||
let handle = self.add_solid_model(EntityType::Solid3D(entity), result, history);
|
||||
self.tabs[i].scene.deselect_all();
|
||||
if !handle.is_null() {
|
||||
self.tabs[i].scene.select_entity(handle, false);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// Slice the one selected solid with an axis-aligned plane (axis 0/1/2 =
|
||||
/// X/Y/Z at `value`), keeping the lower side when `keep_low` is true. The
|
||||
/// kept half is the intersection of the solid with a half-space box, reusing
|
||||
/// the same boolean path the Design-group tools use.
|
||||
/// the same boolean path as the modelling tools.
|
||||
pub(super) fn solid_slice(&mut self, axis: usize, value: f64, keep_low: bool) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let handles: Vec<Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|h| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.filter(|h| self.tabs[i].scene.solid_models.contains_key(h))
|
||||
.collect();
|
||||
let handles = self.selected_solid_handles();
|
||||
if handles.len() != 1 {
|
||||
self.command_line
|
||||
.push_error(crate::t!("SLICE: select exactly one solid created this session.").as_ref());
|
||||
|
|
@ -168,14 +397,7 @@ impl super::OpenCADStudio {
|
|||
/// leaving the originals in place (a non-destructive boolean intersect).
|
||||
pub(super) fn solid_interfere(&mut self) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let handles: Vec<Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|h| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.filter(|h| self.tabs[i].scene.solid_models.contains_key(h))
|
||||
.collect();
|
||||
let handles = self.selected_solid_handles();
|
||||
if handles.len() != 2 {
|
||||
self.command_line
|
||||
.push_error(crate::t!("INTERFERE: select exactly two solids created this session.").as_ref());
|
||||
|
|
@ -208,14 +430,7 @@ impl super::OpenCADStudio {
|
|||
/// orientation, so it reuses the cached B-rep directly.
|
||||
pub(super) fn solid_rotate3d(&mut self, axis: usize, angle_deg: f64) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let handles: Vec<Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|h| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.filter(|h| self.tabs[i].scene.solid_models.contains_key(h))
|
||||
.collect();
|
||||
let handles = self.selected_solid_handles();
|
||||
if handles.len() != 1 {
|
||||
self.command_line
|
||||
.push_error(crate::t!("3DROTATE: select exactly one solid created this session.").as_ref());
|
||||
|
|
@ -253,72 +468,34 @@ impl super::OpenCADStudio {
|
|||
Task::none()
|
||||
}
|
||||
|
||||
/// POLYSOLID — build a wall-like solid from a selected polyline: one box per
|
||||
/// segment (oriented along it, `width` wide, `height` tall) unioned together,
|
||||
/// reusing the box primitive + the boolean union path.
|
||||
/// POLYSOLID — build a wall solid around an exact polyline offset.
|
||||
pub(super) fn solid_polysolid(&mut self, width: f64, height: f64) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let found: Option<(Handle, Vec<[f64; 2]>, bool)> = self.tabs[i]
|
||||
let found: Option<(Handle, EntityType)> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.filter(|(h, _)| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.find_map(|(h, e)| match e {
|
||||
EntityType::LwPolyline(pl) => Some((
|
||||
*h,
|
||||
pl.vertices
|
||||
.iter()
|
||||
.map(|v| [v.location.x, v.location.y])
|
||||
.collect(),
|
||||
pl.is_closed,
|
||||
)),
|
||||
EntityType::LwPolyline(_) => Some((*h, (*e).clone())),
|
||||
_ => None,
|
||||
});
|
||||
let Some((handle, pts, closed)) = found else {
|
||||
let Some((handle, entity)) = found else {
|
||||
self.command_line
|
||||
.push_error(crate::t!("POLYSOLID: select a polyline first.").as_ref());
|
||||
return Task::none();
|
||||
};
|
||||
let mut segs: Vec<([f64; 2], [f64; 2])> = pts.windows(2).map(|w| (w[0], w[1])).collect();
|
||||
if closed && pts.len() > 2 {
|
||||
segs.push((pts[pts.len() - 1], pts[0]));
|
||||
}
|
||||
let mut acc: Option<Body> = None;
|
||||
let mut used = 0usize;
|
||||
for (a, b) in &segs {
|
||||
let (dx, dy) = (b[0] - a[0], b[1] - a[1]);
|
||||
let len = (dx * dx + dy * dy).sqrt();
|
||||
if len < 1e-9 {
|
||||
continue;
|
||||
}
|
||||
let angle = dy.atan2(dx);
|
||||
// Local box: X 0..len, Y -w/2..w/2, Z 0..h, then turned to lie
|
||||
// along the segment and moved onto its start.
|
||||
let (sin, cos) = angle.sin_cos();
|
||||
let Some(bx) = solid_model::box_solid([len / 2.0, 0.0, height / 2.0], len, width, height)
|
||||
.and_then(|bx| {
|
||||
solid_model::placed(
|
||||
&bx,
|
||||
[cos, sin, 0.0],
|
||||
[-sin, cos, 0.0],
|
||||
[0.0, 0.0, 1.0],
|
||||
[a[0], a[1], 0.0],
|
||||
)
|
||||
})
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
acc = Some(match acc {
|
||||
None => bx,
|
||||
Some(prev) => solid_model::boolean(Bool::Union, &prev, &bx).unwrap_or(prev),
|
||||
});
|
||||
used += 1;
|
||||
}
|
||||
let Some(result) = acc else {
|
||||
let Some(result) = crate::scene::model::sweep_model::polysolid(&entity, width, height)
|
||||
else {
|
||||
self.command_line
|
||||
.push_error(crate::t!("POLYSOLID: the polyline has no usable segments.").as_ref());
|
||||
.push_error(crate::t!("POLYSOLID: the kernel could not offset the polyline.").as_ref());
|
||||
return Task::none();
|
||||
};
|
||||
if crate::scene::convert::acis_export::solid_to_sat(&result).is_none() {
|
||||
self.command_line
|
||||
.push_error(crate::t!("POLYSOLID: the result could not be encoded as ACIS.").as_ref());
|
||||
return Task::none();
|
||||
}
|
||||
self.push_undo_snapshot(i, "POLYSOLID");
|
||||
self.tabs[i].scene.erase_entities(&[handle]);
|
||||
let mut s3d = Solid3D::new();
|
||||
|
|
@ -329,11 +506,14 @@ impl super::OpenCADStudio {
|
|||
if !h.is_null() {
|
||||
self.tabs[i].scene.select_entity(h, false);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"POLYSOLID: built a wall solid from {used} segment(s) (width {width}, height {height})."
|
||||
).as_ref());
|
||||
if !h.is_null() {
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
self.command_line.push_output(
|
||||
crate::tf!("POLYSOLID: created a wall solid (width {width}, height {height}).")
|
||||
.as_ref(),
|
||||
);
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
|
|
@ -343,14 +523,7 @@ impl super::OpenCADStudio {
|
|||
/// loop on the way through; without that the copy lights black.
|
||||
pub(super) fn solid_mirror3d(&mut self, axis: usize) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let handles: Vec<Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|h| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.filter(|h| self.tabs[i].scene.solid_models.contains_key(h))
|
||||
.collect();
|
||||
let handles = self.selected_solid_handles();
|
||||
if handles.len() != 1 {
|
||||
self.command_line
|
||||
.push_error(crate::t!("3DMIRROR: select exactly one solid created this session.").as_ref());
|
||||
|
|
@ -395,14 +568,7 @@ impl super::OpenCADStudio {
|
|||
dst: [[f64; 3]; 3],
|
||||
) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let handles: Vec<Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|h| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.filter(|h| self.tabs[i].scene.solid_models.contains_key(h))
|
||||
.collect();
|
||||
let handles = self.selected_solid_handles();
|
||||
if handles.len() != 1 {
|
||||
self.command_line
|
||||
.push_error(crate::t!("3DALIGN: select exactly one solid created this session.").as_ref());
|
||||
|
|
@ -463,14 +629,7 @@ impl super::OpenCADStudio {
|
|||
use acadrust::Line;
|
||||
|
||||
let i = self.active_tab;
|
||||
let handles: Vec<Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|h| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.filter(|h| self.tabs[i].scene.solid_models.contains_key(h))
|
||||
.collect();
|
||||
let handles = self.selected_solid_handles();
|
||||
if handles.len() != 1 {
|
||||
self.command_line
|
||||
.push_error(crate::t!("SECTION: select exactly one solid created this session.").as_ref());
|
||||
|
|
@ -547,12 +706,12 @@ impl super::OpenCADStudio {
|
|||
self.tabs[i].scene.deselect_all();
|
||||
if !handle.is_null() {
|
||||
self.tabs[i].scene.select_entity(handle, false);
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"PYRAMID: created a {n}-sided pyramid (radius {radius}, height {height})."
|
||||
).as_ref());
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"PYRAMID: created a {n}-sided pyramid (radius {radius}, height {height})."
|
||||
).as_ref());
|
||||
Task::none()
|
||||
}
|
||||
|
||||
|
|
@ -570,15 +729,22 @@ impl super::OpenCADStudio {
|
|||
.selected_entities()
|
||||
.iter()
|
||||
.filter(|(h, _)| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.find_map(|(h, e)| match e {
|
||||
EntityType::LwPolyline(pl) => Some((
|
||||
.find_map(|(h, e)| {
|
||||
let EntityType::LwPolyline(_) = e else {
|
||||
return None;
|
||||
};
|
||||
let curve = crate::entities::curve::entity_curve(e)?;
|
||||
let cadkernel::geom2d::Curve::Polyline(polyline) = &curve.curve else {
|
||||
return None;
|
||||
};
|
||||
Some((
|
||||
*h,
|
||||
pl.vertices
|
||||
polyline
|
||||
.vertices
|
||||
.iter()
|
||||
.map(|v| [v.location.x, v.location.y, 0.0])
|
||||
.map(|vertex| curve.plane.point_at(vertex.position))
|
||||
.collect(),
|
||||
)),
|
||||
_ => None,
|
||||
))
|
||||
});
|
||||
let Some((handle, fit)) = found else {
|
||||
self.command_line
|
||||
|
|
@ -595,7 +761,7 @@ impl super::OpenCADStudio {
|
|||
let p = |k: usize| glam::DVec3::new(fit[k][0], fit[k][1], fit[k][2]);
|
||||
// Catmull-Rom → cubic Bézier control points: [P0, b1,b2,P1, b1,b2,P2, …].
|
||||
let mut ctrl: Vec<Vector3> = Vec::with_capacity(3 * m + 1);
|
||||
ctrl.push(Vector3::new(fit[0][0], fit[0][1], 0.0));
|
||||
ctrl.push(Vector3::new(fit[0][0], fit[0][1], fit[0][2]));
|
||||
for seg in 0..m {
|
||||
let p0 = p(seg);
|
||||
let p1 = p(seg + 1);
|
||||
|
|
@ -603,9 +769,9 @@ impl super::OpenCADStudio {
|
|||
let next = if seg + 2 <= m { p(seg + 2) } else { p1 };
|
||||
let b1 = p0 + (p1 - prev) / 6.0;
|
||||
let b2 = p1 - (next - p0) / 6.0;
|
||||
ctrl.push(Vector3::new(b1.x, b1.y, 0.0));
|
||||
ctrl.push(Vector3::new(b2.x, b2.y, 0.0));
|
||||
ctrl.push(Vector3::new(p1.x, p1.y, 0.0));
|
||||
ctrl.push(Vector3::new(b1.x, b1.y, b1.z));
|
||||
ctrl.push(Vector3::new(b2.x, b2.y, b2.z));
|
||||
ctrl.push(Vector3::new(p1.x, p1.y, p1.z));
|
||||
}
|
||||
// Clamped piecewise-Bézier knots (degree 3): len == ctrl.len()+degree+1.
|
||||
let mut knots: Vec<f64> = vec![0.0; 4];
|
||||
|
|
@ -617,7 +783,10 @@ impl super::OpenCADStudio {
|
|||
spl.degree = 3;
|
||||
spl.control_points = ctrl;
|
||||
spl.knots = knots;
|
||||
spl.fit_points = fit.iter().map(|q| Vector3::new(q[0], q[1], 0.0)).collect();
|
||||
spl.fit_points = fit
|
||||
.iter()
|
||||
.map(|q| Vector3::new(q[0], q[1], q[2]))
|
||||
.collect();
|
||||
// flags.rational defaults to false (non-rational) — exactly what we want.
|
||||
self.push_undo_snapshot(i, "SPLINEFIT");
|
||||
self.tabs[i].scene.erase_entities(&[handle]);
|
||||
|
|
@ -636,14 +805,7 @@ impl super::OpenCADStudio {
|
|||
use acadrust::types::Vector3;
|
||||
use acadrust::Line;
|
||||
let i = self.active_tab;
|
||||
let handles: Vec<Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|h| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.filter(|h| self.tabs[i].scene.solid_models.contains_key(h))
|
||||
.collect();
|
||||
let handles = self.selected_solid_handles();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
.push_error(crate::t!("FLATSHOT: select a solid created this session.").as_ref());
|
||||
|
|
@ -677,14 +839,7 @@ impl super::OpenCADStudio {
|
|||
use acadrust::entities::{Surface, SurfaceKind, Wire as AWire};
|
||||
use acadrust::types::Vector3;
|
||||
let i = self.active_tab;
|
||||
let handles: Vec<Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|h| !self.tabs[i].scene.is_layer_locked(*h))
|
||||
.filter(|h| self.tabs[i].scene.solid_models.contains_key(h))
|
||||
.collect();
|
||||
let handles = self.selected_solid_handles();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
.push_error(crate::t!("CONVTOSURFACE: select a solid created this session.").as_ref());
|
||||
|
|
|
|||
|
|
@ -121,6 +121,13 @@ impl OpenCADStudio {
|
|||
acadrust::EntityType::LwPolyline(polyline) => Some(polyline.vertices.len()),
|
||||
acadrust::EntityType::Polyline2D(polyline) => Some(polyline.vertices.len()),
|
||||
acadrust::EntityType::Leader(leader) => Some(leader.vertices.len()),
|
||||
acadrust::EntityType::Spline(spline) => {
|
||||
Some(if crate::entities::spline::shows_fit_points(spline) {
|
||||
spline.fit_points.len()
|
||||
} else {
|
||||
crate::entities::spline::control_vertex_count(spline)
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
vertex_count.map_or(prop_vertex, |count| prop_vertex.min(count.saturating_sub(1)))
|
||||
|
|
@ -830,7 +837,7 @@ impl OpenCADStudio {
|
|||
geom.props.push(crate::scene::model::object::Property {
|
||||
label: t!("Frozen Layers").into_owned(),
|
||||
field: "frozen_layers",
|
||||
value: crate::scene::model::object::PropValue::EditText(
|
||||
value: crate::scene::model::object::PropValue::PlainText(
|
||||
frozen_names.join(", "),
|
||||
),
|
||||
});
|
||||
|
|
@ -896,15 +903,17 @@ impl OpenCADStudio {
|
|||
.filter(|n| !n.is_empty())
|
||||
.collect();
|
||||
if !dim_style_names.is_empty() {
|
||||
// Current style is already shown as EditText in the geom section;
|
||||
// Current style is already shown as text in the geom section;
|
||||
// replace/upgrade it to a Choice if we have a list.
|
||||
if let Some(geom) = sections.last_mut() {
|
||||
// Find and replace the style_name EditText with a Choice.
|
||||
// Replace the style name with a choice.
|
||||
if let Some(prop) =
|
||||
geom.props.iter_mut().find(|p| p.field == "style_name")
|
||||
{
|
||||
let current = match &prop.value {
|
||||
crate::scene::model::object::PropValue::EditText(s) => s.clone(),
|
||||
crate::scene::model::object::PropValue::PlainText(s) => {
|
||||
s.clone()
|
||||
}
|
||||
_ => String::new(),
|
||||
};
|
||||
prop.value = crate::scene::model::object::PropValue::Choice {
|
||||
|
|
@ -1037,7 +1046,7 @@ impl OpenCADStudio {
|
|||
.find(|p| p.field == "dimension_style")
|
||||
{
|
||||
let cur = match &p.value {
|
||||
crate::scene::model::object::PropValue::EditText(s) => {
|
||||
crate::scene::model::object::PropValue::PlainText(s) => {
|
||||
s.clone()
|
||||
}
|
||||
_ => ld.dimension_style.clone(),
|
||||
|
|
@ -1215,7 +1224,8 @@ impl OpenCADStudio {
|
|||
acadrust::EntityType::Text(_)
|
||||
| acadrust::EntityType::MText(_)
|
||||
| acadrust::EntityType::Insert(_)
|
||||
| acadrust::EntityType::Leader(_) => Some(("annotative", None)),
|
||||
| acadrust::EntityType::Leader(_)
|
||||
| acadrust::EntityType::Hatch(_) => Some(("annotative", None)),
|
||||
acadrust::EntityType::MultiLeader(_) => {
|
||||
Some(("enable_annotation_scale", None))
|
||||
}
|
||||
|
|
@ -1258,7 +1268,8 @@ impl OpenCADStudio {
|
|||
},
|
||||
),
|
||||
acadrust::EntityType::Text(_)
|
||||
| acadrust::EntityType::Insert(_) => set_row_value(
|
||||
| acadrust::EntityType::Insert(_)
|
||||
| acadrust::EntityType::Hatch(_) => set_row_value(
|
||||
&mut sections,
|
||||
"annotative",
|
||||
crate::scene::model::object::PropValue::BoolToggle {
|
||||
|
|
@ -1833,6 +1844,41 @@ impl OpenCADStudio {
|
|||
&mut entity,
|
||||
);
|
||||
|
||||
// Smart centre objects carry their own drawing-level creation style.
|
||||
// Apply it after the generic ribbon style so ordinary LINE entities
|
||||
// keep the existing path while centre lines honour their settings.
|
||||
let center_line = acadrust::entities::CenterLineAssociation::read(
|
||||
&entity.common().extended_data,
|
||||
).is_some();
|
||||
let center_mark = acadrust::entities::CenterMarkAssociation::read(
|
||||
&entity.common().extended_data,
|
||||
).is_some();
|
||||
if center_line || center_mark {
|
||||
let settings = self.tabs[i].scene.centerline_settings();
|
||||
if !settings.layer.eq_ignore_ascii_case("Current") {
|
||||
entity.common_mut().layer = settings.layer;
|
||||
}
|
||||
if !settings.linetype.eq_ignore_ascii_case("Current") {
|
||||
entity.common_mut().linetype = settings.linetype;
|
||||
}
|
||||
entity.common_mut().linetype_scale = settings.linetype_scale;
|
||||
let application = if center_mark {
|
||||
acadrust::entities::CENTERMARK_XDATA_APPLICATION
|
||||
} else {
|
||||
acadrust::entities::CENTERLINE_XDATA_APPLICATION
|
||||
};
|
||||
if !self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.app_ids
|
||||
.contains(application)
|
||||
{
|
||||
let mut app = acadrust::tables::AppId::new(application);
|
||||
app.handle = self.tabs[i].scene.document.allocate_handle();
|
||||
let _ = self.tabs[i].scene.document.app_ids.add(app);
|
||||
}
|
||||
}
|
||||
|
||||
let text_style_annotative = match &entity {
|
||||
acadrust::EntityType::Text(text) => {
|
||||
crate::scene::annotative::text_style_is_annotative(
|
||||
|
|
@ -1972,6 +2018,7 @@ fn make_sections_read_only(
|
|||
let text = match &property.value {
|
||||
PropValue::ReadOnly(value)
|
||||
| PropValue::EditText(value)
|
||||
| PropValue::PlainText(value)
|
||||
| PropValue::LayerChoice(value)
|
||||
| PropValue::LinetypeChoice(value)
|
||||
| PropValue::HatchPatternChoice(value) => value.clone(),
|
||||
|
|
@ -2045,6 +2092,23 @@ pub(super) fn aggregate_sections(
|
|||
for sections in all_sections {
|
||||
result = merge_sections(&result, §ions);
|
||||
}
|
||||
// Sum the filled area while individual Area rows may still vary.
|
||||
if selected.len() > 1
|
||||
&& selected
|
||||
.iter()
|
||||
.all(|(_, entity)| matches!(entity, acadrust::EntityType::Hatch(_)))
|
||||
{
|
||||
let total = selected
|
||||
.iter()
|
||||
.filter_map(|(_, entity)| match entity {
|
||||
acadrust::EntityType::Hatch(hatch) => {
|
||||
Some(crate::entities::hatch::boundary_area(hatch))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.sum::<f64>();
|
||||
set_row(&mut result, "cumulative_area", format!("{total:.4}"));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
|
|
@ -2152,6 +2216,9 @@ fn merge_prop_value(
|
|||
(PropValue::EditText(_), PropValue::EditText(_)) => {
|
||||
PropValue::EditText(VARIES_LABEL.into())
|
||||
}
|
||||
(PropValue::PlainText(_), PropValue::PlainText(_)) => {
|
||||
PropValue::PlainText(VARIES_LABEL.into())
|
||||
}
|
||||
(PropValue::ReadOnly(_), PropValue::ReadOnly(_)) => {
|
||||
PropValue::ReadOnly(VARIES_LABEL.into())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use crate::scene::pick::grip::{
|
|||
};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::plugin::v4_support;
|
||||
use crate::scene::model::object::GripApply;
|
||||
use crate::scene::model::object::{GripApply, PropValue};
|
||||
use crate::scene::{
|
||||
self, hover_id, CubeRegion, Scene, VIEWCUBE_DRAW_PX, VIEWCUBE_PAD, VIEWCUBE_PX,
|
||||
};
|
||||
|
|
@ -1322,6 +1322,27 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
}
|
||||
}
|
||||
// One-shot action — apply immediately.
|
||||
let unchanged = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(popup.handle)
|
||||
.is_some_and(|entity| match (item.action, entity) {
|
||||
(GripMenuAction::ShowFit, acadrust::EntityType::Spline(spline)) => {
|
||||
!spline.cv_frame_visible
|
||||
&& crate::entities::spline::uses_fit_method(spline)
|
||||
}
|
||||
(
|
||||
GripMenuAction::ShowControlVertices,
|
||||
acadrust::EntityType::Spline(spline),
|
||||
) => {
|
||||
spline.cv_frame_visible
|
||||
|| !crate::entities::spline::uses_fit_method(spline)
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
if unchanged {
|
||||
return Task::none();
|
||||
}
|
||||
self.push_undo_snapshot(i, item.label);
|
||||
// For Add Leader, the new arrow becomes the last grip; remember
|
||||
// its id so we can grab it for placement right after.
|
||||
|
|
@ -1705,6 +1726,9 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
entry.gpu,
|
||||
crate::scene::model::hatch_model::HatchPattern::Solid
|
||||
);
|
||||
dxf.pattern_type =
|
||||
acadrust::entities::HatchPatternType::Predefined;
|
||||
dxf.gradient_color.enabled = false;
|
||||
}
|
||||
if let Some(model) = self.tabs[i].scene.hatches.get_mut(&handle) {
|
||||
model.pattern = entry.gpu.clone();
|
||||
|
|
@ -1728,6 +1752,91 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
let handles = self.property_target_handles(i);
|
||||
|
||||
if !handles.is_empty() {
|
||||
if matches!(
|
||||
field,
|
||||
"spline_method"
|
||||
| "knot_param"
|
||||
| "cv_frame"
|
||||
| "fill_type"
|
||||
| "gradient_type"
|
||||
| "style"
|
||||
| "pattern_type_label"
|
||||
) {
|
||||
let unchanged = handles.iter().all(|handle| {
|
||||
match self.tabs[i].scene.document.get_entity(*handle) {
|
||||
Some(acadrust::EntityType::Spline(spline)) => match field {
|
||||
"spline_method" => {
|
||||
value
|
||||
== if crate::entities::spline::shows_fit_points(spline) {
|
||||
"Fit"
|
||||
} else {
|
||||
"Control Vertices"
|
||||
}
|
||||
}
|
||||
"knot_param" => {
|
||||
let current = match spline.knot_parameterization {
|
||||
0 => "Chord",
|
||||
1 => "Square Root",
|
||||
2 => "Uniform",
|
||||
_ => "Custom",
|
||||
};
|
||||
value == current
|
||||
}
|
||||
"cv_frame" => {
|
||||
value
|
||||
== if spline.cv_frame_visible {
|
||||
"Show"
|
||||
} else {
|
||||
"Hide"
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
Some(acadrust::EntityType::Hatch(hatch)) => match field {
|
||||
"fill_type" => {
|
||||
value
|
||||
== if hatch.gradient_color.is_single_color {
|
||||
"One color"
|
||||
} else {
|
||||
"Two color"
|
||||
}
|
||||
}
|
||||
"gradient_type" => {
|
||||
let (kind, inverted) =
|
||||
crate::scene::model::hatch_model::GradientKind::from_name(
|
||||
&hatch.gradient_color.name,
|
||||
);
|
||||
value == kind.choice_label(inverted)
|
||||
}
|
||||
"style" => {
|
||||
value
|
||||
== match hatch.style {
|
||||
acadrust::entities::HatchStyleType::Normal => "Normal",
|
||||
acadrust::entities::HatchStyleType::Outer => "Outer",
|
||||
acadrust::entities::HatchStyleType::Ignore => "Ignore",
|
||||
}
|
||||
}
|
||||
"pattern_type_label" => {
|
||||
value
|
||||
== match hatch.pattern_type {
|
||||
acadrust::entities::HatchPatternType::Predefined => {
|
||||
"Predefined"
|
||||
}
|
||||
acadrust::entities::HatchPatternType::UserDefined => {
|
||||
"User Defined"
|
||||
}
|
||||
acadrust::entities::HatchPatternType::Custom => "Custom",
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
if unchanged {
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
|
||||
if field == "vscale_std" {
|
||||
|
|
@ -2086,6 +2195,20 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
crate::command::WorkingPlane::default()
|
||||
};
|
||||
for &handle in &handles {
|
||||
let mline_style = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.and_then(|entity| match entity {
|
||||
acadrust::EntityType::MLine(mline) => {
|
||||
crate::entities::mline::resolved_mline_style(
|
||||
mline,
|
||||
&self.tabs[i].scene.document,
|
||||
)
|
||||
.cloned()
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_geom_prop_in_working_plane(
|
||||
|
|
@ -2094,11 +2217,26 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
&value,
|
||||
plane,
|
||||
);
|
||||
if matches!(field, "ml_justification" | "ml_scale") {
|
||||
if let (
|
||||
acadrust::EntityType::MLine(mline),
|
||||
Some(style),
|
||||
) = (entity, mline_style.as_ref())
|
||||
{
|
||||
crate::modules::draw::draw::mline::sync_mline_element_parameters(
|
||||
mline, style,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
self.tabs[i].dirty = true;
|
||||
if field == "spline_method" {
|
||||
self.tabs[i].properties.prop_vertex = 0;
|
||||
self.tabs[i].properties.prop_vertex_indicator_active = false;
|
||||
}
|
||||
self.refresh_properties();
|
||||
}
|
||||
Task::none()
|
||||
|
|
@ -2109,18 +2247,56 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
self.tabs[i].properties.active_field = None;
|
||||
let handles = self.property_target_handles(i);
|
||||
if !handles.is_empty() {
|
||||
let evaluates_expression = self.tabs[i]
|
||||
.properties
|
||||
.sections
|
||||
.iter()
|
||||
.flat_map(|section| section.props.iter())
|
||||
.find(|property| property.field == field)
|
||||
.is_some_and(|property| {
|
||||
matches!(property.value, PropValue::EditText(_))
|
||||
});
|
||||
if let Some(raw_val) = self.tabs[i]
|
||||
.properties
|
||||
.edit_buf
|
||||
.remove(&crate::ui::properties::FieldKey::Geom(field))
|
||||
{
|
||||
// Block names are free-form text — a name like "10-5"
|
||||
// must not be arithmetic-evaluated.
|
||||
let val = if field == "block" {
|
||||
raw_val
|
||||
} else {
|
||||
let val = if evaluates_expression {
|
||||
crate::app::expr_eval::eval_to_string(&raw_val)
|
||||
} else {
|
||||
raw_val
|
||||
};
|
||||
if matches!(field, "current_fit_point" | "current_control_point") {
|
||||
let count = handles
|
||||
.iter()
|
||||
.filter_map(|handle| {
|
||||
let entity = self.tabs[i].scene.document.get_entity(*handle)?;
|
||||
match (field, entity) {
|
||||
(
|
||||
"current_fit_point",
|
||||
acadrust::EntityType::Spline(spline),
|
||||
) => Some(spline.fit_points.len()),
|
||||
(
|
||||
"current_control_point",
|
||||
acadrust::EntityType::Spline(spline),
|
||||
) => Some(
|
||||
crate::entities::spline::control_vertex_count(spline),
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.min()
|
||||
.unwrap_or(0);
|
||||
if let Ok(requested) = val.trim().parse::<usize>() {
|
||||
if count > 0 {
|
||||
let next = requested.clamp(1, count) - 1;
|
||||
self.tabs[i].properties.prop_vertex = next;
|
||||
self.tabs[i].properties.prop_vertex_indicator_active = true;
|
||||
}
|
||||
}
|
||||
self.refresh_properties();
|
||||
return Task::none();
|
||||
}
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
if field == "block" {
|
||||
// Name row on a block reference: an existing name
|
||||
|
|
@ -2248,6 +2424,20 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
)
|
||||
.is_none()
|
||||
{
|
||||
let mline_style = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.and_then(|entity| match entity {
|
||||
acadrust::EntityType::MLine(mline) => {
|
||||
crate::entities::mline::resolved_mline_style(
|
||||
mline,
|
||||
&self.tabs[i].scene.document,
|
||||
)
|
||||
.cloned()
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
if let Some(entity) = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
|
|
@ -2259,6 +2449,17 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
&val,
|
||||
plane,
|
||||
);
|
||||
if field == "ml_scale" {
|
||||
if let (
|
||||
acadrust::EntityType::MLine(mline),
|
||||
Some(style),
|
||||
) = (entity, mline_style.as_ref())
|
||||
{
|
||||
crate::modules::draw::draw::mline::sync_mline_element_parameters(
|
||||
mline, style,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ fn native_paths_match(left: &std::path::Path, right: &std::path::Path) -> bool {
|
|||
}
|
||||
|
||||
type LayoutPlotParams = (
|
||||
std::sync::Arc<Vec<crate::scene::WireModel>>,
|
||||
std::sync::Arc<Vec<crate::io::pdf_export::PlotWire>>,
|
||||
Vec<crate::scene::model::hatch_model::HatchModel>,
|
||||
Vec<crate::scene::model::hatch_model::HatchModel>,
|
||||
crate::io::pdf_export::PlotGroupSplits,
|
||||
|
|
@ -107,7 +107,7 @@ type LayoutPlotParams = (
|
|||
);
|
||||
|
||||
type ClippedPlotParams = (
|
||||
Vec<crate::scene::WireModel>,
|
||||
Vec<crate::io::pdf_export::PlotWire>,
|
||||
Vec<crate::scene::model::hatch_model::HatchModel>,
|
||||
Vec<crate::scene::model::hatch_model::HatchModel>,
|
||||
crate::io::pdf_export::PlotGroupSplits,
|
||||
|
|
@ -121,7 +121,7 @@ type ClippedPlotParams = (
|
|||
);
|
||||
|
||||
fn plot_content_extents(
|
||||
wires: &[crate::scene::WireModel],
|
||||
wires: &[crate::io::pdf_export::PlotWire],
|
||||
hatches: &[crate::scene::model::hatch_model::HatchModel],
|
||||
wipeouts: &[crate::scene::model::hatch_model::HatchModel],
|
||||
) -> Option<(f64, f64, f64, f64)> {
|
||||
|
|
@ -163,14 +163,26 @@ fn plot_scene_content(
|
|||
paper_space_last: bool,
|
||||
render_mode_override: Option<acadrust::entities::ViewportRenderMode>,
|
||||
) -> (
|
||||
std::sync::Arc<Vec<crate::scene::WireModel>>,
|
||||
std::sync::Arc<Vec<crate::io::pdf_export::PlotWire>>,
|
||||
Vec<crate::scene::model::hatch_model::HatchModel>,
|
||||
Vec<crate::scene::model::hatch_model::HatchModel>,
|
||||
crate::io::pdf_export::PlotGroupSplits,
|
||||
) {
|
||||
let (paper_wires, model_wires) = scene.plot_wire_groups(render_mode_override);
|
||||
let paper_hatches = scene.paper_canvas_hatches().as_ref().clone();
|
||||
let paper_wipeouts = scene.paper_canvas_wipeouts().as_ref().clone();
|
||||
let (mut paper_wires, mut model_wires) = scene.plot_wire_groups(render_mode_override);
|
||||
paper_wires.retain(|wire| wire.plot_visible);
|
||||
model_wires.retain(|wire| wire.plot_visible);
|
||||
let with_depth = |wires: Vec<crate::scene::WireModel>| {
|
||||
let depths = scene.plot_wire_depths(&wires);
|
||||
wires
|
||||
.into_iter()
|
||||
.zip(depths)
|
||||
.map(|(wire, draw_depth)| crate::io::pdf_export::PlotWire { wire, draw_depth })
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let paper_wires = with_depth(paper_wires);
|
||||
let model_wires = with_depth(model_wires);
|
||||
let paper_hatches = scene.paper_plot_hatches().as_ref().clone();
|
||||
let paper_wipeouts = scene.paper_plot_wipeouts().as_ref().clone();
|
||||
if scene.current_layout == "Model" {
|
||||
let splits = crate::io::pdf_export::PlotGroupSplits {
|
||||
wires: paper_wires.len(),
|
||||
|
|
@ -184,8 +196,13 @@ fn plot_scene_content(
|
|||
splits,
|
||||
);
|
||||
}
|
||||
let (model_pattern_wires, model_hatches, model_wipeouts) =
|
||||
let (mut model_pattern_wires, model_hatches, model_wipeouts) =
|
||||
scene.viewport_plot_fills();
|
||||
model_pattern_wires.retain(|(wire, _)| wire.plot_visible);
|
||||
let model_pattern_wires = model_pattern_wires
|
||||
.into_iter()
|
||||
.map(|(wire, draw_depth)| crate::io::pdf_export::PlotWire { wire, draw_depth })
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (wires, hatches, wipeouts, splits) = if paper_space_last {
|
||||
let splits = crate::io::pdf_export::PlotGroupSplits {
|
||||
|
|
@ -220,36 +237,72 @@ fn plot_scene_content(
|
|||
}
|
||||
|
||||
impl OpenCADStudio {
|
||||
/// Before a save, give every cached solid that still has no ACIS
|
||||
/// geometry (EXTRUDE/REVOLVE/SWEEP/LOFT/boolean results) an exact modeler
|
||||
/// body derived from its B-rep, so the written DWG/DXF carries real
|
||||
/// 3-D geometry other CAD apps can open instead of an empty data stream.
|
||||
/// A body holding something the kernel has no ACIS record form for is
|
||||
/// left untouched rather than written out half-complete.
|
||||
fn sync_solid_models_to_acis(&mut self, i: usize) {
|
||||
/// Persist exact ACIS bodies and kernel-derived edge caches before saving.
|
||||
fn sync_solid_models_for_save(&mut self, i: usize) {
|
||||
use acadrust::EntityType;
|
||||
let scene = &mut self.tabs[i].scene;
|
||||
let targets: Vec<acadrust::Handle> = scene
|
||||
.solid_models
|
||||
.keys()
|
||||
.copied()
|
||||
.filter(|h| {
|
||||
matches!(
|
||||
scene.document.get_entity(*h),
|
||||
Some(EntityType::Solid3D(s)) if !s.acis_data.has_data()
|
||||
)
|
||||
let targets: Vec<(acadrust::Handle, bool, bool)> = scene
|
||||
.document
|
||||
.entities()
|
||||
.filter_map(|entity| {
|
||||
let EntityType::Solid3D(solid) = entity else {
|
||||
return None;
|
||||
};
|
||||
let h = solid.common.handle;
|
||||
let needs_acis = !solid.acis_data.has_data();
|
||||
let needs_wires = solid.wires.is_empty();
|
||||
(needs_acis || needs_wires).then_some((h, needs_acis, needs_wires))
|
||||
})
|
||||
.collect();
|
||||
for h in targets {
|
||||
// Build the SAT while borrowing solid_models; the returned document
|
||||
// is owned, so the borrow ends before we mutate the entity.
|
||||
let sat = scene
|
||||
.solid_models
|
||||
.get(&h)
|
||||
.and_then(crate::scene::convert::acis_export::planar_solid_to_sat);
|
||||
if let Some(sat) = sat {
|
||||
if let Some(EntityType::Solid3D(s)) = scene.document.get_entity_mut(h) {
|
||||
s.set_sat_document(&sat);
|
||||
for (h, needs_acis, needs_wires) in targets {
|
||||
let body = scene.solid_models.get(&h);
|
||||
let sat = needs_acis
|
||||
.then(|| {
|
||||
body.and_then(crate::scene::convert::acis_export::solid_to_sat)
|
||||
})
|
||||
.flatten();
|
||||
let wires = needs_wires.then(|| {
|
||||
if let Some(body) = body {
|
||||
return crate::scene::model::solid_model::edge_wires(body);
|
||||
}
|
||||
let Some(mesh) = scene.meshes.get(&h).or_else(|| scene.block_meshes.get(&h)) else {
|
||||
return Vec::new();
|
||||
};
|
||||
mesh.edge_verts
|
||||
.chunks_exact(2)
|
||||
.enumerate()
|
||||
.map(|(index, points)| {
|
||||
let first_low = mesh
|
||||
.edge_verts_low
|
||||
.get(index * 2)
|
||||
.copied()
|
||||
.unwrap_or([0.0; 3]);
|
||||
let second_low = mesh
|
||||
.edge_verts_low
|
||||
.get(index * 2 + 1)
|
||||
.copied()
|
||||
.unwrap_or([0.0; 3]);
|
||||
acadrust::entities::Wire::from_points(vec![
|
||||
acadrust::types::Vector3::new(
|
||||
points[0][0] as f64 + first_low[0] as f64,
|
||||
points[0][1] as f64 + first_low[1] as f64,
|
||||
points[0][2] as f64 + first_low[2] as f64,
|
||||
),
|
||||
acadrust::types::Vector3::new(
|
||||
points[1][0] as f64 + second_low[0] as f64,
|
||||
points[1][1] as f64 + second_low[1] as f64,
|
||||
points[1][2] as f64 + second_low[2] as f64,
|
||||
),
|
||||
])
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
if let Some(EntityType::Solid3D(solid)) = scene.document.get_entity_mut(h) {
|
||||
if let Some(sat) = sat {
|
||||
solid.set_sat_document(&sat);
|
||||
}
|
||||
if let Some(wires) = wires.filter(|wires| !wires.is_empty()) {
|
||||
solid.wires = wires;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1391,14 +1444,23 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
move |result| Message::ObjImportFinished(tab_id, path, result),)
|
||||
}
|
||||
|
||||
fn sync_view_state_for_save(&mut self, i: usize) {
|
||||
self.sync_vport_display(i);
|
||||
if self.tabs[i].active_block_edit.is_none() {
|
||||
self.tabs[i].scene.sync_camera_to_document();
|
||||
self.tabs[i].last_synced_camera_gen =
|
||||
self.tabs[i].scene.camera_generation;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(in crate::app) fn prepare_native_save(&mut self, i: usize) {
|
||||
self.sync_vport_display(i);
|
||||
self.sync_view_state_for_save(i);
|
||||
sync_annotation_scale_header(&mut self.tabs[i].scene);
|
||||
self.stamp_header_sysvars(i);
|
||||
self.tabs[i].scene.document.header.user_real1 =
|
||||
self.tabs[i].scene.annotation_scale as f64;
|
||||
self.sync_solid_models_to_acis(i);
|
||||
self.sync_solid_models_for_save(i);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
|
@ -1453,16 +1515,120 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn stamp_thumbnail(&mut self, i: usize, version: acadrust::DxfVersion) {
|
||||
let scene = &self.tabs[i].scene;
|
||||
let preview = crate::io::thumbnail::from_snapshot(
|
||||
&scene.entity_wires(),
|
||||
&scene.camera.borrow(),
|
||||
scene.bg_color,
|
||||
version >= acadrust::DxfVersion::AC1027,
|
||||
self.vp_size,
|
||||
);
|
||||
self.tabs[i].scene.document.preview = preview;
|
||||
pub(super) fn on_thumbnail_capture_frame(&mut self) -> Task<Message> {
|
||||
let Some(pending) = self.pending_web_thumbnail_save.take() else {
|
||||
return Task::none();
|
||||
};
|
||||
iced::window::latest()
|
||||
.then(|window| match window {
|
||||
Some(window) => iced::window::screenshot(window).map(Some),
|
||||
None => Task::done(None),
|
||||
})
|
||||
.map(move |screenshot| Message::WebSaveScreenshot {
|
||||
tab_id: pending.tab_id,
|
||||
filename: pending.filename.clone(),
|
||||
ext: pending.ext.clone(),
|
||||
version: pending.version,
|
||||
bounds: Some(pending.bounds),
|
||||
screenshot,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(super) fn on_thumbnail_capture_frame(&mut self) -> Task<Message> {
|
||||
let Some(pending) = self.pending_native_thumbnail_save.take() else {
|
||||
return Task::none();
|
||||
};
|
||||
let Some(i) = self.tabs.iter().position(|tab| tab.id == pending.tab_id) else {
|
||||
self.thumbnail_capture_clean = false;
|
||||
return Task::none();
|
||||
};
|
||||
self.queue_native_save(
|
||||
i,
|
||||
pending.path,
|
||||
pending.version,
|
||||
pending.purpose,
|
||||
pending.continuation,
|
||||
pending.set_current_path,
|
||||
pending.check_external_change,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(super) fn on_web_save_screenshot(
|
||||
&mut self,
|
||||
tab_id: u64,
|
||||
filename: String,
|
||||
ext: String,
|
||||
version: acadrust::DxfVersion,
|
||||
bounds: Option<iced::Rectangle>,
|
||||
screenshot: Option<iced::window::Screenshot>,
|
||||
) -> Task<Message> {
|
||||
self.thumbnail_capture_clean = false;
|
||||
let Some(i) = self.tabs.iter().position(|tab| tab.id == tab_id) else {
|
||||
return Task::none();
|
||||
};
|
||||
let preview = screenshot.as_ref().and_then(|screenshot| {
|
||||
bounds.and_then(|bounds| {
|
||||
crate::io::thumbnail::from_screenshot(
|
||||
screenshot,
|
||||
bounds,
|
||||
version >= acadrust::DxfVersion::AC1027,
|
||||
)
|
||||
})
|
||||
});
|
||||
if let Some(preview) = preview {
|
||||
self.tabs[i].scene.document.preview = Some(preview);
|
||||
}
|
||||
|
||||
let mut recent_task = Task::none();
|
||||
let saved = match crate::io::save_to_bytes(
|
||||
&self.tabs[i].scene.document,
|
||||
&ext,
|
||||
version,
|
||||
) {
|
||||
Ok(bytes) => {
|
||||
crate::sys::download_bytes(&filename, &bytes);
|
||||
let cache_name = std::path::Path::new(&filename)
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().into_owned())
|
||||
.unwrap_or_else(|| filename.clone());
|
||||
let path = std::path::PathBuf::from(cache_name);
|
||||
self.tabs[i].current_path = Some(path.clone());
|
||||
self.tabs[i].scene.document.version = version;
|
||||
self.tabs[i].dirty = false;
|
||||
self.tabs[i].recovery_save_as_required = false;
|
||||
recent_task = Task::perform(
|
||||
async move {
|
||||
crate::io::web_recent::store(&path.to_string_lossy(), &bytes)
|
||||
.await
|
||||
.map(|_| path)
|
||||
},
|
||||
Message::WebRecentStored,
|
||||
);
|
||||
self.command_line
|
||||
.push_output(crate::tf!("Saved: {filename}").as_ref());
|
||||
true
|
||||
}
|
||||
Err(error) => {
|
||||
self.command_line
|
||||
.push_error(crate::tf!("Save failed: {error}").as_ref());
|
||||
false
|
||||
}
|
||||
};
|
||||
if self.save_dialog_for_unsaved {
|
||||
if saved {
|
||||
if let Some(crate::app::PendingClose::Tab(index)) = self.pending_close.take() {
|
||||
let continuation = self.update(Message::TabClose(index));
|
||||
let rest = self.continue_tab_close_queue();
|
||||
return Task::batch([recent_task, continuation, rest]);
|
||||
}
|
||||
} else if self.pending_close.is_some() {
|
||||
let retry = self.open_unsaved_dialog_window();
|
||||
return Task::batch([recent_task, retry]);
|
||||
}
|
||||
}
|
||||
recent_task
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
|
@ -1477,11 +1643,27 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
check_external_change: bool,
|
||||
) -> Task<Message> {
|
||||
let tab_id = self.tabs[i].id;
|
||||
if self
|
||||
.pending_native_thumbnail_save
|
||||
.as_ref()
|
||||
.is_some_and(|pending| pending.tab_id == tab_id)
|
||||
{
|
||||
if purpose != crate::app::SavePurpose::Autosave {
|
||||
self.command_line
|
||||
.push_info(crate::t!("Save already running for this drawing.").as_ref());
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
let capture_ready =
|
||||
self.thumbnail_capture_clean && self.pending_native_thumbnail_save.is_none();
|
||||
if self.active_save_jobs.contains_key(&tab_id) {
|
||||
if purpose != crate::app::SavePurpose::Autosave {
|
||||
self.command_line
|
||||
.push_info(crate::t!("Save already running for this drawing.").as_ref());
|
||||
}
|
||||
if capture_ready {
|
||||
self.thumbnail_capture_clean = false;
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
let destination_is_current = self.tabs[i]
|
||||
|
|
@ -1499,6 +1681,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
self.active_tab = i;
|
||||
self.save_dialog_for_unsaved =
|
||||
continuation != crate::app::SaveContinuation::None;
|
||||
if capture_ready {
|
||||
self.thumbnail_capture_clean = false;
|
||||
}
|
||||
return self.open_save_dialog_window(i);
|
||||
}
|
||||
if purpose != crate::app::SavePurpose::Autosave
|
||||
|
|
@ -1523,6 +1708,33 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
});
|
||||
self.restore_failed_save_continuation(continuation, i);
|
||||
self.active_modal = Some(crate::app::ModalKind::FileInUse);
|
||||
if capture_ready {
|
||||
self.thumbnail_capture_clean = false;
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
if purpose != crate::app::SavePurpose::Autosave
|
||||
&& i == self.active_tab
|
||||
&& !self.thumbnail_capture_clean
|
||||
&& self.main_window.is_some()
|
||||
&& crate::ui::wrap_bar::dropdown_bounds(
|
||||
crate::app::view::VIEWPORT_CAPTURE_BOUNDS_ID,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
self.pending_native_thumbnail_save = Some(
|
||||
crate::app::PendingNativeThumbnailSave {
|
||||
tab_id,
|
||||
path,
|
||||
version,
|
||||
purpose,
|
||||
continuation,
|
||||
set_current_path,
|
||||
check_external_change,
|
||||
},
|
||||
);
|
||||
self.thumbnail_capture_clean = true;
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
|
|
@ -1549,6 +1761,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
});
|
||||
self.restore_failed_save_continuation(continuation, i);
|
||||
self.active_modal = Some(crate::app::ModalKind::FileInUse);
|
||||
if capture_ready {
|
||||
self.thumbnail_capture_clean = false;
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => {
|
||||
|
|
@ -1562,32 +1777,15 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
let epoch = self.tabs[i].scene.geometry_epoch;
|
||||
let revision = self.tabs[i].edit_revision;
|
||||
let camera_generation = self.tabs[i].scene.camera_generation;
|
||||
let thumbnail_key = (purpose != crate::app::SavePurpose::Autosave).then(|| {
|
||||
let scene = &self.tabs[i].scene;
|
||||
crate::app::ThumbnailCacheKey {
|
||||
epoch,
|
||||
camera_generation,
|
||||
bg_color: scene.bg_color.map(f32::to_bits),
|
||||
png: version >= acadrust::DxfVersion::AC1027,
|
||||
viewport: [self.vp_size.0.to_bits(), self.vp_size.1.to_bits()],
|
||||
}
|
||||
let thumbnail = (purpose != crate::app::SavePurpose::Autosave && i == self.active_tab)
|
||||
.then_some(version >= acadrust::DxfVersion::AC1027);
|
||||
let capture_bounds = thumbnail.and_then(|_| {
|
||||
crate::ui::wrap_bar::dropdown_bounds(
|
||||
crate::app::view::VIEWPORT_CAPTURE_BOUNDS_ID,
|
||||
)
|
||||
});
|
||||
let thumbnail = if thumbnail_key == self.tabs[i].thumbnail_cache_key {
|
||||
None
|
||||
} else {
|
||||
thumbnail_key.map(|key| {
|
||||
let scene = &self.tabs[i].scene;
|
||||
(
|
||||
scene.entity_wires(),
|
||||
scene.camera.borrow().clone(),
|
||||
scene.bg_color,
|
||||
key.png,
|
||||
self.vp_size,
|
||||
)
|
||||
})
|
||||
};
|
||||
let clone_started = iced::time::Instant::now();
|
||||
let mut snapshot = self.tabs[i].scene.document.clone();
|
||||
let snapshot = self.tabs[i].scene.document.clone();
|
||||
let clone_ms = clone_started.elapsed().as_secs_f64() * 1000.0;
|
||||
if crate::perf::enabled() {
|
||||
crate::perf_record!(
|
||||
|
|
@ -1633,6 +1831,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
let (expected_fingerprint, verify_reader) = match verification {
|
||||
Ok(verification) => verification,
|
||||
Err(error) => {
|
||||
if capture_ready {
|
||||
self.thumbnail_capture_clean = false;
|
||||
}
|
||||
return Task::perform(
|
||||
async move {
|
||||
crate::app::SaveOutcome {
|
||||
|
|
@ -1647,7 +1848,6 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
set_current_path,
|
||||
purpose,
|
||||
continuation,
|
||||
thumbnail_key,
|
||||
refreshed_preview: None,
|
||||
result: Err(error),
|
||||
}
|
||||
|
|
@ -1657,65 +1857,97 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
}
|
||||
};
|
||||
let worker_path = path.clone();
|
||||
|
||||
Task::perform(
|
||||
async move {
|
||||
let (result, refreshed_preview) = std::thread::spawn(move || {
|
||||
let mut refreshed_preview = None;
|
||||
if let Some((wires, camera, bg_color, png, viewport)) = thumbnail {
|
||||
let started = iced::time::Instant::now();
|
||||
snapshot.preview = crate::io::thumbnail::from_snapshot(
|
||||
&wires,
|
||||
&camera,
|
||||
bg_color,
|
||||
png,
|
||||
viewport,
|
||||
);
|
||||
if crate::perf::enabled() {
|
||||
crate::perf_record!(
|
||||
"[perf] save-thumbnail {:.1}ms wires={}",
|
||||
started.elapsed().as_secs_f64() * 1000.0,
|
||||
wires.len(),
|
||||
);
|
||||
let capture_window = thumbnail.and(capture_bounds).and(self.main_window);
|
||||
let mut work = Some((
|
||||
snapshot,
|
||||
thumbnail,
|
||||
capture_bounds,
|
||||
worker_path,
|
||||
expected_fingerprint,
|
||||
verify_reader,
|
||||
path,
|
||||
previous_autosave,
|
||||
));
|
||||
let mut run_save = move |screenshot: Option<iced::window::Screenshot>| {
|
||||
let (
|
||||
mut snapshot,
|
||||
thumbnail,
|
||||
capture_bounds,
|
||||
worker_path,
|
||||
expected_fingerprint,
|
||||
verify_reader,
|
||||
path,
|
||||
previous_autosave,
|
||||
) = work.take().expect("save capture produced more than one result");
|
||||
Task::perform(
|
||||
async move {
|
||||
let (result, refreshed_preview) = std::thread::spawn(move || {
|
||||
let mut refreshed_preview = None;
|
||||
if let Some(png) = thumbnail {
|
||||
let started = iced::time::Instant::now();
|
||||
if let Some(preview) = screenshot.as_ref().and_then(|screenshot| {
|
||||
capture_bounds.and_then(|bounds| {
|
||||
crate::io::thumbnail::from_screenshot(screenshot, bounds, png)
|
||||
})
|
||||
}) {
|
||||
snapshot.preview = Some(preview);
|
||||
refreshed_preview = Some(snapshot.preview.clone());
|
||||
}
|
||||
if crate::perf::enabled() {
|
||||
crate::perf_record!(
|
||||
"[perf] save-thumbnail {:.1}ms",
|
||||
started.elapsed().as_secs_f64() * 1000.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
refreshed_preview = Some(snapshot.preview.clone());
|
||||
}
|
||||
let result = crate::io::save_owned_as_version_atomic(
|
||||
snapshot,
|
||||
&worker_path,
|
||||
let result = crate::io::save_owned_as_version_atomic(
|
||||
snapshot,
|
||||
&worker_path,
|
||||
version,
|
||||
backup,
|
||||
expected_fingerprint,
|
||||
verify_reader,
|
||||
);
|
||||
(result, refreshed_preview)
|
||||
})
|
||||
.join()
|
||||
.unwrap_or_else(|_| {
|
||||
(
|
||||
Err(crate::io::SaveFailure::other("save worker panicked")),
|
||||
None,
|
||||
)
|
||||
});
|
||||
crate::app::SaveOutcome {
|
||||
job_id,
|
||||
tab_id,
|
||||
epoch,
|
||||
revision,
|
||||
camera_generation,
|
||||
path,
|
||||
version,
|
||||
backup,
|
||||
expected_fingerprint,
|
||||
verify_reader,
|
||||
);
|
||||
(result, refreshed_preview)
|
||||
})
|
||||
.join()
|
||||
.unwrap_or_else(|_| {
|
||||
(
|
||||
Err(crate::io::SaveFailure::other("save worker panicked")),
|
||||
None,
|
||||
)
|
||||
});
|
||||
crate::app::SaveOutcome {
|
||||
job_id,
|
||||
tab_id,
|
||||
epoch,
|
||||
revision,
|
||||
camera_generation,
|
||||
path,
|
||||
version,
|
||||
previous_autosave,
|
||||
set_current_path,
|
||||
purpose,
|
||||
continuation,
|
||||
thumbnail_key,
|
||||
refreshed_preview,
|
||||
result,
|
||||
}
|
||||
},
|
||||
Message::SaveFinished,
|
||||
)
|
||||
previous_autosave,
|
||||
set_current_path,
|
||||
purpose,
|
||||
continuation,
|
||||
refreshed_preview,
|
||||
result,
|
||||
}
|
||||
},
|
||||
Message::SaveFinished,
|
||||
)
|
||||
};
|
||||
match capture_window {
|
||||
Some(window) => iced::window::screenshot(window).map(Some).then(move |screenshot| {
|
||||
Task::batch([
|
||||
Task::done(Message::ThumbnailCaptureFinished),
|
||||
run_save(screenshot),
|
||||
])
|
||||
}),
|
||||
None => {
|
||||
self.thumbnail_capture_clean = false;
|
||||
run_save(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
|
@ -1819,7 +2051,6 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
if let Some(preview) = outcome.refreshed_preview {
|
||||
self.tabs[i].scene.document.preview = preview;
|
||||
}
|
||||
self.tabs[i].thumbnail_cache_key = outcome.thumbnail_key;
|
||||
}
|
||||
let mut tasks = Vec::new();
|
||||
match outcome.purpose {
|
||||
|
|
@ -1833,10 +2064,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
.as_deref()
|
||||
.is_none_or(|current| {
|
||||
!native_paths_match(current, &outcome.path)
|
||||
});
|
||||
});
|
||||
self.command_line
|
||||
.push_output(crate::tf!("Saved: {}", outcome.path.display()).as_ref());
|
||||
self.recent_thumbs.remove(&outcome.path);
|
||||
if let Some(previous) = outcome.previous_autosave {
|
||||
if previous != outcome.path {
|
||||
let _ = std::fs::remove_file(previous);
|
||||
|
|
@ -1848,8 +2078,8 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
if outcome.purpose == crate::app::SavePurpose::SaveAs {
|
||||
self.tabs[i].recovery_save_as_required = false;
|
||||
}
|
||||
tasks.push(self.push_recent(outcome.path.clone()));
|
||||
}
|
||||
tasks.push(self.push_recent(outcome.path.clone()));
|
||||
self.refresh_native_edit_guard_after_save(
|
||||
i,
|
||||
&outcome.path,
|
||||
|
|
@ -2047,7 +2277,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
// once after the destination/version is known.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
self.sync_vport_display(i);
|
||||
self.sync_view_state_for_save(i);
|
||||
self.stamp_header_sysvars(i);
|
||||
}
|
||||
// Native: save straight to the known path. Web has no path
|
||||
|
|
@ -2182,62 +2412,38 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let close = self.close_save_dialog_window();
|
||||
self.sync_view_state_for_save(i);
|
||||
sync_annotation_scale_header(&mut self.tabs[i].scene);
|
||||
self.stamp_header_sysvars(i);
|
||||
self.sync_solid_models_to_acis(i);
|
||||
self.stamp_thumbnail(i, version);
|
||||
let mut recent_task = Task::none();
|
||||
let saved = match crate::io::save_to_bytes(
|
||||
&self.tabs[i].scene.document,
|
||||
ext,
|
||||
version,
|
||||
) {
|
||||
Ok(bytes) => {
|
||||
crate::sys::download_bytes(&filename, &bytes);
|
||||
let cache_name = std::path::Path::new(&filename)
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().into_owned())
|
||||
.unwrap_or_else(|| filename.clone());
|
||||
let path = std::path::PathBuf::from(cache_name);
|
||||
self.tabs[i].current_path = Some(path.clone());
|
||||
self.tabs[i].scene.document.version = version;
|
||||
self.tabs[i].dirty = false;
|
||||
self.tabs[i].recovery_save_as_required = false;
|
||||
recent_task = Task::perform(
|
||||
async move {
|
||||
crate::io::web_recent::store(
|
||||
&path.to_string_lossy(),
|
||||
&bytes,
|
||||
)
|
||||
.await
|
||||
.map(|_| path)
|
||||
},
|
||||
Message::WebRecentStored,
|
||||
);
|
||||
self.command_line.push_output(crate::tf!("Saved: {filename}").as_ref());
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
self.command_line.push_error(crate::tf!("Save failed: {e}").as_ref());
|
||||
false
|
||||
}
|
||||
self.sync_solid_models_for_save(i);
|
||||
let tab_id = self.tabs[i].id;
|
||||
let bounds = crate::ui::wrap_bar::dropdown_bounds(
|
||||
crate::app::view::VIEWPORT_CAPTURE_BOUNDS_ID,
|
||||
);
|
||||
let Some(bounds) = bounds else {
|
||||
return Task::batch([
|
||||
close,
|
||||
Task::done(Message::WebSaveScreenshot {
|
||||
tab_id,
|
||||
filename,
|
||||
ext: ext.to_string(),
|
||||
version,
|
||||
bounds: None,
|
||||
screenshot: None,
|
||||
}),
|
||||
]);
|
||||
};
|
||||
// Continue a pending tab close.
|
||||
if self.save_dialog_for_unsaved {
|
||||
if saved {
|
||||
if let Some(crate::app::PendingClose::Tab(idx)) =
|
||||
self.pending_close.take()
|
||||
{
|
||||
let cont = self.update(Message::TabClose(idx));
|
||||
let rest = self.continue_tab_close_queue();
|
||||
return Task::batch([close, recent_task, cont, rest]);
|
||||
}
|
||||
} else if self.pending_close.is_some() {
|
||||
let retry = self.open_unsaved_dialog_window();
|
||||
return Task::batch([close, recent_task, retry]);
|
||||
}
|
||||
}
|
||||
Task::batch([close, recent_task])
|
||||
self.pending_web_thumbnail_save = Some(
|
||||
crate::app::PendingWebThumbnailSave {
|
||||
tab_id,
|
||||
filename,
|
||||
ext: ext.to_string(),
|
||||
version,
|
||||
bounds,
|
||||
},
|
||||
);
|
||||
self.thumbnail_capture_clean = true;
|
||||
close
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2348,6 +2554,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
{
|
||||
continue;
|
||||
}
|
||||
self.prepare_native_save(i);
|
||||
let version = self.tabs[i].scene.document.version;
|
||||
let target = self.autosave_target(i);
|
||||
tasks.push(self.queue_native_save(
|
||||
|
|
|
|||
|
|
@ -1559,20 +1559,37 @@ impl OpenCADStudio {
|
|||
))
|
||||
}
|
||||
|
||||
Message::CommandLineArrowProbe { direction } => {
|
||||
Message::CommandLineArrowProbe {
|
||||
direction,
|
||||
extend_selection,
|
||||
} => {
|
||||
iced::widget::operation::is_focused(iced::widget::Id::new(
|
||||
crate::ui::command_line::CMD_INPUT_ID,
|
||||
))
|
||||
.map(move |focused| Message::CommandLineArrowResolved {
|
||||
direction,
|
||||
focused,
|
||||
extend_selection,
|
||||
})
|
||||
}
|
||||
|
||||
Message::CommandLineArrowResolved { direction, focused } => {
|
||||
Message::CommandLineArrowResolved {
|
||||
direction,
|
||||
focused,
|
||||
extend_selection,
|
||||
} => {
|
||||
if !focused {
|
||||
return Task::none();
|
||||
}
|
||||
// The editor inherits arrows captured by the focused command line.
|
||||
if self.mtext_editor.is_some() {
|
||||
match direction {
|
||||
ArrowKey::Up => self.mtext_caret_move_vertical(1, extend_selection),
|
||||
ArrowKey::Down => self.mtext_caret_move_vertical(-1, extend_selection),
|
||||
ArrowKey::Left | ArrowKey::Right => {}
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
match direction {
|
||||
ArrowKey::Up => self.update(Message::CommandHistoryPrev),
|
||||
ArrowKey::Down => self.update(Message::CommandHistoryNext),
|
||||
|
|
@ -2410,6 +2427,58 @@ impl OpenCADStudio {
|
|||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::LayerTogglePlot(idx) => {
|
||||
let i = self.active_tab;
|
||||
|
||||
let plottable = self.tabs[i]
|
||||
.layers
|
||||
.layers
|
||||
.get(idx)
|
||||
.map(|layer| !layer.plottable);
|
||||
|
||||
let targets = self.layer_row_action_targets(i, idx);
|
||||
|
||||
if let Some(plottable) = plottable {
|
||||
if !targets.is_empty() {
|
||||
let undo = self.begin_layer_undo(i, "LAYER PLOT/NOPLOT", &targets);
|
||||
|
||||
for name in &targets {
|
||||
if let Some(layer) = self.tabs[i].scene.document.layers.get_mut(name) {
|
||||
layer.is_plottable = plottable;
|
||||
}
|
||||
|
||||
if let Some(layer) = self.tabs[i]
|
||||
.layers
|
||||
.layers
|
||||
.iter_mut()
|
||||
.find(|layer| &layer.name == name)
|
||||
{
|
||||
layer.plottable = plottable;
|
||||
}
|
||||
}
|
||||
|
||||
self.tabs[i].layers.refresh_sort();
|
||||
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.invalidate_layer_dependencies(&targets);
|
||||
|
||||
self.tabs[i].dirty = true;
|
||||
self.commit_layer_undo(i, undo);
|
||||
|
||||
self.command_line.push_output(
|
||||
crate::tf!(
|
||||
"{} layer(s) set to {}",
|
||||
targets.len(),
|
||||
if plottable { "Plot" } else { "No Plot" }
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Task::none()
|
||||
},
|
||||
|
||||
Message::LayerToggleVpFreeze(layer_idx, vp_col_idx) => {
|
||||
self.on_layer_toggle_vp_freeze(layer_idx, vp_col_idx)
|
||||
|
|
@ -4060,7 +4129,7 @@ impl OpenCADStudio {
|
|||
.push_error(crate::t!("DRAWORDER: select entities first.").as_ref());
|
||||
} else {
|
||||
use crate::command::CadCommand;
|
||||
let cmd = super::commands::DrawOrderRefCommand::new(to_move, above);
|
||||
let cmd = super::commands::DrawOrderCommand::for_reference_pick(to_move, above);
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
}
|
||||
|
|
@ -6251,9 +6320,33 @@ impl OpenCADStudio {
|
|||
|
||||
Message::AutoSave => self.on_autosave(),
|
||||
|
||||
Message::ThumbnailCaptureFrame => self.on_thumbnail_capture_frame(),
|
||||
|
||||
Message::ThumbnailCaptureFinished => {
|
||||
self.thumbnail_capture_clean = false;
|
||||
Task::none()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
Message::SaveFinished(outcome) => self.on_save_finished(outcome),
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
Message::WebSaveScreenshot {
|
||||
tab_id,
|
||||
filename,
|
||||
ext,
|
||||
version,
|
||||
bounds,
|
||||
screenshot,
|
||||
} => self.on_web_save_screenshot(
|
||||
tab_id,
|
||||
filename,
|
||||
ext,
|
||||
version,
|
||||
bounds,
|
||||
screenshot,
|
||||
),
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
Message::SaveFileInUseRetry => self.on_save_file_in_use_retry(),
|
||||
|
||||
|
|
|
|||
|
|
@ -253,17 +253,44 @@ impl OpenCADStudio {
|
|||
eye: glam::DVec3,
|
||||
bounds: iced::Rectangle,
|
||||
) -> Option<crate::snap::OtrackHit> {
|
||||
if snap.is_some_and(|hit| hit.snap_type != crate::snap::SnapType::Extension) {
|
||||
let intersection = snap.filter(|hit| {
|
||||
hit.snap_type == crate::snap::SnapType::Intersection
|
||||
});
|
||||
|
||||
// Ordinary object snaps suppress OTRACK. Extension and Intersection are
|
||||
// exceptions: both may lie on an active tracking vector.
|
||||
if snap.is_some_and(|hit| {
|
||||
!matches!(
|
||||
hit.snap_type,
|
||||
crate::snap::SnapType::Extension | crate::snap::SnapType::Intersection
|
||||
)
|
||||
}) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let required_crossing_ray = match snap {
|
||||
Some(extension) => Some((extension.extension_origin?, extension.extension_dir?)),
|
||||
None => None,
|
||||
Some(extension)
|
||||
if extension.snap_type == crate::snap::SnapType::Extension =>
|
||||
{
|
||||
Some((
|
||||
extension.extension_origin?,
|
||||
extension.extension_dir?,
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// When Intersection has already won OSNAP selection, test OTRACK at the
|
||||
// exact intersection rather than at the free cursor position.
|
||||
let track_cursor = intersection
|
||||
.map(|hit| hit.world)
|
||||
.unwrap_or(cursor);
|
||||
|
||||
let step = (self.polar_mode && drafting).then_some(self.polar_increment_deg);
|
||||
let (_, (ucs_x, ucs_y, _)) = self.drafting_grid_basis(tab);
|
||||
let hit = self.snapper.otrack_snap(
|
||||
cursor,
|
||||
|
||||
let mut hit = self.snapper.otrack_snap(
|
||||
track_cursor,
|
||||
view_rot,
|
||||
eye,
|
||||
bounds,
|
||||
|
|
@ -274,6 +301,28 @@ impl OpenCADStudio {
|
|||
ucs_x.as_dvec3(),
|
||||
ucs_y.as_dvec3(),
|
||||
)?;
|
||||
|
||||
if let Some(intersection) = intersection {
|
||||
// Only keep the vector when it really passes through the highlighted
|
||||
// intersection. This avoids showing an unrelated nearby tracking ray.
|
||||
let ndc = view_rot.project_point3((hit.aligned - eye).as_vec3());
|
||||
let aligned_screen = iced::Point::new(
|
||||
(ndc.x + 1.0) * 0.5 * bounds.width,
|
||||
(1.0 - ndc.y) * 0.5 * bounds.height,
|
||||
);
|
||||
|
||||
let dx = aligned_screen.x - intersection.screen.x;
|
||||
let dy = aligned_screen.y - intersection.screen.y;
|
||||
|
||||
if dx * dx + dy * dy > 4.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// OTRACK is only providing the visual/reference vector here.
|
||||
// Intersection remains the exact picked point.
|
||||
hit.aligned = intersection.world;
|
||||
}
|
||||
|
||||
Some(hit)
|
||||
}
|
||||
|
||||
|
|
@ -292,8 +341,9 @@ impl OpenCADStudio {
|
|||
.abs();
|
||||
if !self.point_size_relative && mag == 0.0 {
|
||||
let wpp = self.tabs[i].scene.world_per_pixel().unwrap_or(0.0);
|
||||
let viewport_height = self.tabs[i].scene.selection.borrow().vp_size.1;
|
||||
mag = if wpp > 0.0 {
|
||||
crate::entities::point::relative_world_size(0.0, wpp)
|
||||
crate::entities::point::relative_world_size(0.0, wpp, viewport_height)
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
|
@ -653,7 +703,9 @@ impl OpenCADStudio {
|
|||
screen: p,
|
||||
started: iced::time::Instant::now(),
|
||||
});
|
||||
self.grip_popup = None;
|
||||
if !self.grip_popup.as_ref().is_some_and(|popup| popup.pinned) {
|
||||
self.grip_popup = None;
|
||||
}
|
||||
} else if let Some(h) = self.grip_hover.as_mut() {
|
||||
h.screen = p;
|
||||
}
|
||||
|
|
@ -672,12 +724,17 @@ impl OpenCADStudio {
|
|||
use crate::entities::traits::EntityTypeOps;
|
||||
let items = e.grip_menu(grip_id);
|
||||
if !items.is_empty() {
|
||||
let selected = items
|
||||
.iter()
|
||||
.position(|item| item.label.starts_with('✓'))
|
||||
.unwrap_or(0);
|
||||
self.grip_popup = Some(crate::app::GripPopup {
|
||||
handle,
|
||||
grip_id,
|
||||
anchor: p,
|
||||
items,
|
||||
selected: 0,
|
||||
selected,
|
||||
pinned: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -685,7 +742,7 @@ impl OpenCADStudio {
|
|||
}
|
||||
None => {
|
||||
self.grip_hover = None;
|
||||
if let Some(popup) = &self.grip_popup {
|
||||
if let Some(popup) = self.grip_popup.as_ref().filter(|popup| !popup.pinned) {
|
||||
let dx = p.x - popup.anchor.x;
|
||||
let dy = p.y - popup.anchor.y;
|
||||
if (dx * dx + dy * dy).sqrt() > POPUP_DISMISS_PX {
|
||||
|
|
@ -752,6 +809,7 @@ impl OpenCADStudio {
|
|||
// render mode; the model-layout tab style is untouched.
|
||||
if self.tabs[i].scene.set_active_viewport_render_mode(mode) {
|
||||
self.tabs[i].scene.bump_geometry_no_blocks();
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::tf!("Viewport visual style: {label}").as_ref());
|
||||
return Task::none();
|
||||
|
|
@ -770,6 +828,7 @@ impl OpenCADStudio {
|
|||
// Re-upload face3d fills on the next frame — the render
|
||||
// pipeline keys its upload cache off `geometry_epoch`.
|
||||
self.tabs[i].scene.bump_geometry_no_blocks();
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::tf!("Visual style: {label}").as_ref());
|
||||
Task::none()
|
||||
|
|
@ -1210,7 +1269,14 @@ impl OpenCADStudio {
|
|||
// Keep the entity's original geometry available for self-OSNAP,
|
||||
// Extension and OTRACK while the live grip preview is being deformed.
|
||||
self.grip_snap_wires = snap.clone();
|
||||
|
||||
// The user explicitly picked this grip, so treat its original position as an
|
||||
// acquired tracking point immediately. This lets Extension/OTRACK use the
|
||||
// original edge direction on the very first movement instead of requiring the
|
||||
// cursor to return to the old grip position and dwell there first.
|
||||
self.snapper.acquire_grip_tracking_point(
|
||||
grip.origin_world,
|
||||
&self.grip_snap_wires,
|
||||
);
|
||||
self.grip_text_verts = snap
|
||||
.iter()
|
||||
.flat_map(|w| w.text_verts.iter().copied())
|
||||
|
|
@ -1749,6 +1815,49 @@ impl OpenCADStudio {
|
|||
self.axis_lock_dir = None;
|
||||
None
|
||||
};
|
||||
// An acquired OTRACK ray can cross real drawing geometry even before the
|
||||
// command has a first point. Probe the active tracking ray and let the normal
|
||||
// snap engine evaluate that ray against nearby geometry as a construction ray.
|
||||
//
|
||||
// This makes OTRACK × entity crossings real Intersection snaps, so the normal
|
||||
// dwell acquisition can subsequently turn the crossing into a tracking point.
|
||||
if axis_lock.is_none() && !is_window_corner {
|
||||
let tracking_probe = self.active_otrack_hit(
|
||||
i,
|
||||
cursor_world,
|
||||
None,
|
||||
self.last_point,
|
||||
true,
|
||||
view_rot,
|
||||
eye,
|
||||
bounds,
|
||||
);
|
||||
|
||||
if let Some(track) = tracking_probe {
|
||||
let (go, gr) = self.drafting_grid_basis(i);
|
||||
|
||||
let tracked_snap = self.snapper.snap(
|
||||
snap_cursor,
|
||||
p,
|
||||
&snap_candidates,
|
||||
view_rot,
|
||||
eye,
|
||||
bounds,
|
||||
go,
|
||||
gr,
|
||||
Some((track.base, track.base + track.dir)),
|
||||
);
|
||||
|
||||
// The second pass exists only to discover a crossing between the
|
||||
// active OTRACK ray and real geometry. Keep the original snap result
|
||||
// for every other kind of snap.
|
||||
if tracked_snap
|
||||
.is_some_and(|hit| hit.snap_type == crate::snap::SnapType::Intersection)
|
||||
{
|
||||
self.tabs[i].snap_result = tracked_snap;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.snapper.update_otrack_dwell(
|
||||
self.tabs[i].snap_result,
|
||||
&snap_candidates,
|
||||
|
|
@ -2131,9 +2240,12 @@ impl OpenCADStudio {
|
|||
let far_pos = base + dir * far;
|
||||
let far_neg = base - dir * far;
|
||||
let guide = crate::scene::WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
plot_visible: true,
|
||||
fill_is_3d: false,
|
||||
fill_is_2d_solid: false,
|
||||
render_instance: None,
|
||||
|
|
@ -2736,6 +2848,32 @@ impl OpenCADStudio {
|
|||
let Some(&handle) = self.tabs[i].selected_grip_handles.get(grip_index) else {
|
||||
return Task::none();
|
||||
};
|
||||
let grip_shape = self.tabs[i].selected_grips[grip_index].shape;
|
||||
if grip_shape == crate::scene::model::object::GripShape::Dropdown {
|
||||
use crate::entities::traits::EntityTypeOps;
|
||||
let items = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.map(|entity| entity.grip_menu(grip_id))
|
||||
.unwrap_or_default();
|
||||
if !items.is_empty() {
|
||||
let selected = items
|
||||
.iter()
|
||||
.position(|item| item.label.starts_with('✓'))
|
||||
.unwrap_or(0);
|
||||
self.grip_popup = Some(crate::app::GripPopup {
|
||||
handle,
|
||||
grip_id,
|
||||
anchor: p_full,
|
||||
items,
|
||||
selected,
|
||||
pinned: true,
|
||||
});
|
||||
}
|
||||
self.grip_hover = None;
|
||||
return Task::none();
|
||||
}
|
||||
// The visibility (lookup) grip opens a state
|
||||
// dropdown instead of starting a stretch drag.
|
||||
if grip_id == crate::app::visibility::VIS_GRIP_ID {
|
||||
|
|
@ -3022,7 +3160,7 @@ impl OpenCADStudio {
|
|||
.as_ref()
|
||||
.map(|c| c.needs_entity_pick())
|
||||
.unwrap_or(false);
|
||||
let snap_hit = if needs_entity_click {
|
||||
let mut snap_hit = if needs_entity_click {
|
||||
None
|
||||
} else if needs_tan {
|
||||
self.snapper.snap_tangent_only(
|
||||
|
|
@ -3064,6 +3202,43 @@ impl OpenCADStudio {
|
|||
construction_ray,
|
||||
)
|
||||
};
|
||||
// Mirror the cursor-move OTRACK × geometry intersection pass when the
|
||||
// point is actually clicked. The move path may already display the
|
||||
// Intersection marker, but click handling recomputes snapping from scratch.
|
||||
if !needs_entity_click && !needs_tan && !is_window_corner {
|
||||
let tracking_probe = self.active_otrack_hit(
|
||||
i,
|
||||
raw,
|
||||
None,
|
||||
self.last_point,
|
||||
true,
|
||||
view_rot,
|
||||
eye,
|
||||
bounds,
|
||||
);
|
||||
|
||||
if let Some(track) = tracking_probe {
|
||||
let (go, gr) = self.drafting_grid_basis(i);
|
||||
|
||||
let tracked_snap = self.snapper.snap(
|
||||
snap_cursor,
|
||||
p,
|
||||
&snap_candidates,
|
||||
view_rot,
|
||||
eye,
|
||||
bounds,
|
||||
go,
|
||||
gr,
|
||||
Some((track.base, track.base + track.dir)),
|
||||
);
|
||||
|
||||
if tracked_snap
|
||||
.is_some_and(|hit| hit.snap_type == crate::snap::SnapType::Intersection)
|
||||
{
|
||||
snap_hit = tracked_snap;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Snap runs in model space; the result is already model.
|
||||
let mut pt = snap_hit.map(|s| s.world).unwrap_or(raw);
|
||||
// When no UCS is active clamp to world XY; with a UCS the point is
|
||||
|
|
@ -3279,6 +3454,38 @@ impl OpenCADStudio {
|
|||
})?
|
||||
});
|
||||
if let Some(handle) = hit {
|
||||
let uses_surface_point = self.tabs[i]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.is_some_and(|command| command.entity_pick_uses_surface_point());
|
||||
let entity_pick_point = if uses_surface_point {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.solid_click_point_for(p, view_rot2, eye2, bounds, handle)
|
||||
.unwrap_or(pick_wcs)
|
||||
} else {
|
||||
pick_wcs
|
||||
};
|
||||
let entity_pick_direction = if uses_surface_point {
|
||||
if matches!(
|
||||
self.tabs[i].scene.document.get_entity(handle),
|
||||
Some(acadrust::EntityType::Solid3D(_))
|
||||
) {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.solid_planar_face_normal_at(handle, entity_pick_point)
|
||||
} else {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.and_then(crate::entities::curve::entity_curve)
|
||||
.and_then(|curve| curve.plane.normal())
|
||||
.map(glam::DVec3::from_array)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Some commands (e.g. SS_CATCHMENT) need the entity
|
||||
// body before `on_entity_pick` can advance.
|
||||
let inject_first = self.tabs[i]
|
||||
|
|
@ -3309,7 +3516,8 @@ impl OpenCADStudio {
|
|||
let result = self.tabs[i].active_cmd.as_mut().map(|c| {
|
||||
// Shift-swap state for TRIM/EXTEND (#336).
|
||||
c.set_shift(shift);
|
||||
c.on_entity_pick(handle, pick_wcs)
|
||||
c.set_entity_pick_direction(entity_pick_direction);
|
||||
c.on_entity_pick(handle, entity_pick_point)
|
||||
});
|
||||
// HATCHEDIT: after pick, inject hatch model data into the command.
|
||||
if self.tabs[i]
|
||||
|
|
@ -3319,13 +3527,28 @@ impl OpenCADStudio {
|
|||
.unwrap_or(false)
|
||||
{
|
||||
if let Some(model) = self.tabs[i].scene.hatches.get(&handle).cloned() {
|
||||
let entity = self.tabs[i].scene.document.get_entity(handle);
|
||||
let annotative = entity.is_some_and(|entity| {
|
||||
crate::scene::annotative::is_annotative(
|
||||
&self.tabs[i].scene.document,
|
||||
entity,
|
||||
)
|
||||
});
|
||||
let (scale, angle) = match entity {
|
||||
Some(acadrust::EntityType::Hatch(hatch)) => (
|
||||
hatch.pattern_scale as f32,
|
||||
hatch.pattern_angle.to_degrees() as f32,
|
||||
),
|
||||
_ => (model.scale, model.angle_offset.to_degrees()),
|
||||
};
|
||||
use crate::command::CadCommand;
|
||||
use crate::modules::draw::draw::hatchedit::HatcheditCommand;
|
||||
let cmd: Box<dyn CadCommand> = Box::new(HatcheditCommand::with_handle(
|
||||
handle,
|
||||
model.name.clone(),
|
||||
model.scale,
|
||||
model.angle_offset,
|
||||
scale,
|
||||
angle,
|
||||
annotative,
|
||||
));
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(cmd);
|
||||
|
|
@ -3821,8 +4044,7 @@ impl OpenCADStudio {
|
|||
)
|
||||
})
|
||||
.or_else(|| {
|
||||
// Block-internal hatch: resolve to the
|
||||
// parent Insert (AutoCAD behaviour).
|
||||
// Block-internal hatch: resolve to the parent Insert.
|
||||
scene::pick::hit_test::click_hit_insert_hatch(
|
||||
p,
|
||||
self.tabs[i].scene.insert_hatches_for_click().as_ref(),
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ use viewcube::{viewcube_nav_controls, viewcube_ucs_picker, UCS_PICKER_W};
|
|||
// the `view::` path as before the split.
|
||||
pub(in crate::app) use overlay::{MTEXT_TEXT_ID, TEXT_INLINE_ID};
|
||||
|
||||
pub(in crate::app) const VIEWPORT_CAPTURE_BOUNDS_ID: &str = "viewport-capture-bounds";
|
||||
|
||||
const VIEWCUBE_HIT_SIZE: f32 = VIEWCUBE_REGION_PX;
|
||||
/// The desk shown around the sheet, as the widget wants it. One definition,
|
||||
/// shared with the renderer that clears the sheet viewport to the same thing.
|
||||
|
|
@ -184,6 +186,7 @@ impl OpenCADStudio {
|
|||
pub fn view_main(&self) -> Element<'_, Message> {
|
||||
let i = self.active_tab;
|
||||
let tab = &self.tabs[i];
|
||||
let thumbnail_capture_clean = self.thumbnail_capture_clean;
|
||||
let theme_text = self.active_theme.palette().background.base.text;
|
||||
let viewcube_text_color = [
|
||||
theme_text.r,
|
||||
|
|
@ -229,8 +232,10 @@ impl OpenCADStudio {
|
|||
let (vw, vh) = tab.scene.selection.borrow().vp_size;
|
||||
tab.scene.active_model_tile_bounds(vw, vh).width
|
||||
};
|
||||
let viewcube_visible =
|
||||
self.show_viewcube && !tab.is_start && viewcube_has_room(render_bar_w, active_vp_w);
|
||||
let viewcube_visible = self.show_viewcube
|
||||
&& !thumbnail_capture_clean
|
||||
&& !tab.is_start
|
||||
&& viewcube_has_room(render_bar_w, active_vp_w);
|
||||
// Start tab: render welcome page in place of the viewport.
|
||||
// Surrounding chrome (tab bar, status bar) stays; the welcome widget
|
||||
// returned here also flags the rest of `view` to skip drawing-only
|
||||
|
|
@ -258,6 +263,7 @@ impl OpenCADStudio {
|
|||
shader(ViewportPane::model(
|
||||
&tab.scene,
|
||||
viewcube_visible,
|
||||
!thumbnail_capture_clean,
|
||||
viewport_render_mode,
|
||||
viewcube_text_color,
|
||||
))
|
||||
|
|
@ -276,6 +282,7 @@ impl OpenCADStudio {
|
|||
// mouse_areas' hover state and drops their move events).
|
||||
let scene = &tab.scene;
|
||||
let show_viewcube = viewcube_visible;
|
||||
let show_interaction = !thumbnail_capture_clean;
|
||||
let render_mode = viewport_render_mode;
|
||||
let size_probe: Element<'_, Message> = responsive(move |size| {
|
||||
{
|
||||
|
|
@ -293,6 +300,7 @@ impl OpenCADStudio {
|
|||
shader(ViewportPane::for_pane(
|
||||
scene,
|
||||
show_viewcube,
|
||||
show_interaction,
|
||||
render_mode,
|
||||
idx,
|
||||
viewcube_text_color,
|
||||
|
|
@ -414,22 +422,20 @@ impl OpenCADStudio {
|
|||
None => tab.scene.active_model_tile_bounds(vw, vh),
|
||||
};
|
||||
let sel_h = tab.selected_handle;
|
||||
// The Current Vertex the Properties panel is focused on:
|
||||
// mark that grip hot so the navigated vertex is visible in
|
||||
// the drawing. Only for a single selected polyline, whose
|
||||
// vertex grips are ids 0..n. (Properties vertex stepper)
|
||||
// Mark the Properties panel's current point grip hot.
|
||||
let current_vertex_grip: Option<usize> = tab
|
||||
.properties
|
||||
.prop_vertex_indicator_active
|
||||
.then(|| sel_h)
|
||||
.flatten()
|
||||
.and_then(|h| {
|
||||
matches!(
|
||||
tab.scene.document.get_entity(h),
|
||||
Some(acadrust::EntityType::LwPolyline(_))
|
||||
let indexed = match tab.scene.document.get_entity(h) {
|
||||
Some(acadrust::EntityType::LwPolyline(_))
|
||||
| Some(acadrust::EntityType::Polyline2D(_))
|
||||
)
|
||||
.then_some(tab.properties.prop_vertex)
|
||||
| Some(acadrust::EntityType::Spline(_)) => true,
|
||||
_ => false,
|
||||
};
|
||||
indexed.then_some(tab.properties.prop_vertex)
|
||||
});
|
||||
// In-viewport grips are model-space; project them with the
|
||||
// viewport camera so they sit on the wire the GPU draws.
|
||||
|
|
@ -495,6 +501,30 @@ impl OpenCADStudio {
|
|||
} else {
|
||||
vec![]
|
||||
};
|
||||
let control_polygon = tab.selected_handle.and_then(|handle| {
|
||||
let spline = match tab.scene.document.get_entity(handle) {
|
||||
Some(acadrust::EntityType::Spline(spline)) if spline.cv_frame_visible => spline,
|
||||
_ => return None,
|
||||
};
|
||||
if tab
|
||||
.selected_grip_handles
|
||||
.iter()
|
||||
.any(|owner| *owner != handle)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let points: Vec<_> = grips
|
||||
.iter()
|
||||
.filter(|grip| {
|
||||
grip.shape == crate::scene::model::object::GripShape::Circle
|
||||
})
|
||||
.map(|grip| grip.pos)
|
||||
.collect();
|
||||
(points.len() >= 2).then_some((
|
||||
points,
|
||||
spline.flags.closed || spline.flags.periodic,
|
||||
))
|
||||
});
|
||||
let grip_clip = if grips.is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
|
@ -688,6 +718,7 @@ impl OpenCADStudio {
|
|||
snap_ext_base,
|
||||
snap_ext_base2,
|
||||
grips,
|
||||
control_polygon,
|
||||
grip_clip,
|
||||
ucs_icons,
|
||||
ost_points,
|
||||
|
|
@ -845,13 +876,24 @@ impl OpenCADStudio {
|
|||
.height(Fill)]
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
} else if is_paper {
|
||||
// Paper layout: the GPU shader renders everything — the desk is the
|
||||
// container background, the white sheet + paper entities + borders
|
||||
// come from the full-canvas top-locked "sheet" viewport, and the
|
||||
// floating content viewports overlay it (same path as model space).
|
||||
} else if thumbnail_capture_clean {
|
||||
let capture_bg = if is_paper { PAPER_SPACE_BACKGROUND } else { bg_color };
|
||||
stack![
|
||||
container(grid_overlay)
|
||||
container(Space::new())
|
||||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(capture_bg)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Fill)
|
||||
.height(Fill),
|
||||
viewport_3d,
|
||||
]
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
} else if is_paper {
|
||||
// Keep the grid above the opaque GPU sheet and below interaction UI.
|
||||
stack![
|
||||
container(Space::new())
|
||||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PAPER_SPACE_BACKGROUND)),
|
||||
..Default::default()
|
||||
|
|
@ -859,6 +901,7 @@ impl OpenCADStudio {
|
|||
.width(Fill)
|
||||
.height(Fill),
|
||||
viewport_3d,
|
||||
grid_overlay,
|
||||
selection_overlay,
|
||||
viewport_mouse,
|
||||
]
|
||||
|
|
@ -866,7 +909,7 @@ impl OpenCADStudio {
|
|||
.height(Fill)
|
||||
} else {
|
||||
stack![
|
||||
container(grid_overlay)
|
||||
container(Space::new())
|
||||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(bg_color)),
|
||||
..Default::default()
|
||||
|
|
@ -874,12 +917,14 @@ impl OpenCADStudio {
|
|||
.width(Fill)
|
||||
.height(Fill),
|
||||
viewport_3d,
|
||||
grid_overlay,
|
||||
selection_overlay,
|
||||
]
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
};
|
||||
|
||||
if !thumbnail_capture_clean {
|
||||
// Per-pane input pane_grid goes ABOVE the crosshair overlay so it
|
||||
// receives mouse events (the overlay's `Hidden` cursor would otherwise
|
||||
// starve any layer beneath it). The controls bar is pushed on top of it.
|
||||
|
|
@ -1093,26 +1138,28 @@ impl OpenCADStudio {
|
|||
}
|
||||
}
|
||||
|
||||
// Multi-functional grip popup (Phase 2). One bordered container
|
||||
// wraps a column of borderless item buttons so the popup reads
|
||||
// as a single widget instead of stacked tiles.
|
||||
if let Some(popup) = self.grip_popup.as_ref() {
|
||||
if !tab.is_start {
|
||||
// Size the row to the widest label so the selection
|
||||
// highlight fills the whole row instead of just the
|
||||
// text glyphs. ~7 px per character at size 12 + the
|
||||
// horizontal padding (10 + 10).
|
||||
let max_len = popup
|
||||
let labels: Vec<String> = popup
|
||||
.items
|
||||
.iter()
|
||||
.map(|i| i.label.chars().count())
|
||||
.map(|item| {
|
||||
let (mark, raw) = item
|
||||
.label
|
||||
.strip_prefix("✓ ")
|
||||
.map_or(("", item.label), |raw| ("✓ ", raw));
|
||||
format!("{mark}{}", crate::i18n::translate(raw))
|
||||
})
|
||||
.collect();
|
||||
let max_len = labels
|
||||
.iter()
|
||||
.map(|label| label.chars().count())
|
||||
.max()
|
||||
.unwrap_or(8) as f32;
|
||||
let row_w = max_len * 7.0 + 24.0;
|
||||
let mut col = column![].spacing(0).width(iced::Length::Fixed(row_w));
|
||||
for (idx, item) in popup.items.iter().enumerate() {
|
||||
for (idx, label) in labels.into_iter().enumerate() {
|
||||
let is_sel = idx == popup.selected;
|
||||
let label = item.label;
|
||||
let btn = button(text(label).size(12))
|
||||
.on_press(Message::GripMenuPick(idx))
|
||||
.padding([3, 10])
|
||||
|
|
@ -1457,6 +1504,7 @@ impl OpenCADStudio {
|
|||
viewport_stack = viewport_stack.push(text_inline_overlay(ed, canvas));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Docked side panels (Properties, block palette, future palettes) live
|
||||
// in an ordered vertical stack on the left/right edge of the drawing
|
||||
|
|
@ -1511,7 +1559,13 @@ impl OpenCADStudio {
|
|||
if let Some(e) = left_edge {
|
||||
parts.push(e);
|
||||
}
|
||||
parts.push(viewport_stack.into());
|
||||
parts.push(
|
||||
crate::ui::wrap_bar::PosReport::new(
|
||||
VIEWPORT_CAPTURE_BOUNDS_ID,
|
||||
viewport_stack,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
if let Some(e) = right_edge {
|
||||
parts.push(e);
|
||||
}
|
||||
|
|
@ -1688,7 +1742,9 @@ impl OpenCADStudio {
|
|||
&self.history_content,
|
||||
self.win_size.1,
|
||||
);
|
||||
let center_stack: Element<'_, Message> = if tab.is_start {
|
||||
let center_stack: Element<'_, Message> = if thumbnail_capture_clean {
|
||||
workspace
|
||||
} else if tab.is_start {
|
||||
column![
|
||||
workspace,
|
||||
iced::widget::container(command_line)
|
||||
|
|
@ -2072,6 +2128,11 @@ impl OpenCADStudio {
|
|||
} else {
|
||||
Subscription::none()
|
||||
};
|
||||
let thumbnail_capture = if self.thumbnail_capture_clean {
|
||||
window::frames().map(|_| Message::ThumbnailCaptureFrame)
|
||||
} else {
|
||||
Subscription::none()
|
||||
};
|
||||
// Blink the MText preview caret while the editor is open.
|
||||
let caret_blink = if self.mtext_editor.is_some() {
|
||||
iced::time::every(std::time::Duration::from_millis(530))
|
||||
|
|
@ -2130,6 +2191,7 @@ impl OpenCADStudio {
|
|||
grip_dwell,
|
||||
hover_dwell,
|
||||
nav_settle,
|
||||
thumbnail_capture,
|
||||
caret_blink,
|
||||
web_fonts,
|
||||
autosave,
|
||||
|
|
@ -2242,6 +2304,7 @@ impl OpenCADStudio {
|
|||
{
|
||||
return Some(Message::CommandLineArrowProbe {
|
||||
direction: arrow?,
|
||||
extend_selection: modifiers.shift(),
|
||||
});
|
||||
}
|
||||
// A focused web text field needs the browser clipboard;
|
||||
|
|
|
|||
|
|
@ -421,6 +421,7 @@ impl OpenCADStudio {
|
|||
540,
|
||||
|flow| {
|
||||
crate::ui::style::plotstyle::view_window(
|
||||
&self.tabs[self.active_tab].scene.document,
|
||||
self.active_plot_style.as_ref(),
|
||||
self.plotstyle_panel_aci,
|
||||
&self.ps_color_buf,
|
||||
|
|
|
|||
148
src/command.rs
|
|
@ -11,6 +11,22 @@ use crate::scene::Scene;
|
|||
use acadrust::{EntityType, Handle};
|
||||
use glam::DVec3;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum HatchEditOperation {
|
||||
Update {
|
||||
origin: Option<(f64, f64)>,
|
||||
disassociate: bool,
|
||||
style: Option<acadrust::entities::HatchStyleType>,
|
||||
annotative: Option<bool>,
|
||||
},
|
||||
RecreateBoundary,
|
||||
Separate,
|
||||
AddBoundaries(Vec<Handle>),
|
||||
RemoveBoundaries(Vec<Handle>),
|
||||
DrawOrderFront,
|
||||
DrawOrderBack,
|
||||
}
|
||||
|
||||
// ── Working plane ─────────────────────────────────────────────────────────
|
||||
|
||||
/// Full-precision coordinate frame used by interactive commands.
|
||||
|
|
@ -526,6 +542,34 @@ impl KeywordCommand {
|
|||
}
|
||||
}
|
||||
|
||||
fn match_cmd_option<'a>(
|
||||
options: &'a [(&'static str, &'static str, Option<&'static str>)],
|
||||
text: &str,
|
||||
) -> Option<&'a (&'static str, &'static str, Option<&'static str>)> {
|
||||
let t = text.trim();
|
||||
let up = t.to_uppercase();
|
||||
if up.is_empty() {
|
||||
return None;
|
||||
}
|
||||
// 1. Exact match on keyword or label (case-insensitive)
|
||||
if let Some(opt) = options.iter().find(|(label, k, _)| {
|
||||
k.eq_ignore_ascii_case(&up) || label.eq_ignore_ascii_case(t)
|
||||
}) {
|
||||
return Some(opt);
|
||||
}
|
||||
// 2. Unambiguous prefix match on keyword or label (e.g. "A" -> "ABOVE", "L" -> "LEFT")
|
||||
let matches: Vec<_> = options
|
||||
.iter()
|
||||
.filter(|(label, k, _)| {
|
||||
k.to_uppercase().starts_with(&up) || label.to_uppercase().starts_with(&up)
|
||||
})
|
||||
.collect();
|
||||
if matches.len() == 1 {
|
||||
return Some(matches[0]);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl CadCommand for KeywordCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
self.name
|
||||
|
|
@ -565,10 +609,7 @@ impl CadCommand for KeywordCommand {
|
|||
// Consumed inputs that keep prompting return `Some(NeedPoint)` —
|
||||
// `None` would hand the same text to the command a second time.
|
||||
None => {
|
||||
let up = t.to_uppercase();
|
||||
let Some((_, keyword, value_prompt)) = self.options.iter().find(|(label, k, _)| {
|
||||
k.eq_ignore_ascii_case(&up) || label.eq_ignore_ascii_case(t)
|
||||
})
|
||||
let Some((_, keyword, value_prompt)) = match_cmd_option(&self.options, t)
|
||||
else {
|
||||
// Unknown verb — keep prompting rather than dispatch garbage.
|
||||
return Some(CmdResult::NeedPoint);
|
||||
|
|
@ -767,11 +808,18 @@ impl CadCommand for SelectThenKeywordCommand {
|
|||
return None;
|
||||
}
|
||||
match self.pending {
|
||||
Some((keyword, _)) => Some(CmdResult::Dispatch(format!("{} {keyword} {t}", self.name))),
|
||||
Some((keyword, _)) => {
|
||||
if self.selected.is_empty() {
|
||||
Some(CmdResult::Dispatch(format!("{} {keyword} {t}", self.name)))
|
||||
} else {
|
||||
Some(CmdResult::Relaunch(
|
||||
format!("{} {keyword} {t}", self.name),
|
||||
std::mem::take(&mut self.selected),
|
||||
))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let up = t.to_uppercase();
|
||||
let Some((_, keyword, value_prompt)) =
|
||||
self.options.iter().find(|(_, k, _)| k.eq_ignore_ascii_case(&up))
|
||||
let Some((_, keyword, value_prompt)) = match_cmd_option(&self.options, t)
|
||||
else {
|
||||
// Unknown verb — consumed, keep prompting (`None` would
|
||||
// feed the same text to the command a second time).
|
||||
|
|
@ -782,7 +830,16 @@ impl CadCommand for SelectThenKeywordCommand {
|
|||
self.pending = Some((keyword, vp));
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
None => Some(CmdResult::Dispatch(format!("{} {keyword}", self.name))),
|
||||
None => {
|
||||
if self.selected.is_empty() {
|
||||
Some(CmdResult::Dispatch(format!("{} {keyword}", self.name)))
|
||||
} else {
|
||||
Some(CmdResult::Relaunch(
|
||||
format!("{} {keyword}", self.name),
|
||||
std::mem::take(&mut self.selected),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1197,6 +1254,11 @@ pub enum CmdResult {
|
|||
boundaries: Vec<EntityType>,
|
||||
entity_style: Option<(acadrust::types::Color, acadrust::types::Transparency)>,
|
||||
},
|
||||
/// Commit independently editable hatch entities for every selected region.
|
||||
CommitHatches {
|
||||
hatches: Vec<HatchModel>,
|
||||
entity_style: Option<(acadrust::types::Color, acadrust::types::Transparency)>,
|
||||
},
|
||||
/// Copy selected entities with multiple transforms (e.g. rectangular array); end command.
|
||||
BatchCopy(Vec<Handle>, Vec<EntityTransform>),
|
||||
/// Erase `handle` and replace with new entities; command stays active.
|
||||
|
|
@ -1206,6 +1268,12 @@ pub enum CmdResult {
|
|||
ReplaceMany(Vec<(Handle, Vec<EntityType>)>, Vec<EntityType>),
|
||||
/// Replace several entities as one undo step while keeping the command active.
|
||||
ReplaceManyContinue(Vec<(Handle, Vec<EntityType>)>),
|
||||
/// Attach one smart centre mark to a newly selected circular source.
|
||||
ReassociateCenterMark {
|
||||
target: Handle,
|
||||
source: Handle,
|
||||
point: DVec3,
|
||||
},
|
||||
/// Cancel: discard any preview and end the command.
|
||||
Cancel,
|
||||
/// Cancel because the active drawing space changed. Cleanup is identical
|
||||
|
|
@ -1297,7 +1365,11 @@ pub enum CmdResult {
|
|||
boundary_handle: Handle,
|
||||
},
|
||||
/// Create a wipeout from an existing closed polyline in the active space.
|
||||
WipeoutFromPolyline(Handle),
|
||||
/// `erase_source` controls whether the source boundary is consumed.
|
||||
WipeoutFromPolyline {
|
||||
handle: Handle,
|
||||
erase_source: bool,
|
||||
},
|
||||
/// Temporarily switch between paper and Model while MVIEW defines a new
|
||||
/// model-space window, keeping the command active.
|
||||
MviewSwitchLayout(String),
|
||||
|
|
@ -1334,6 +1406,7 @@ pub enum CmdResult {
|
|||
name: String,
|
||||
scale: f32,
|
||||
angle: f32,
|
||||
operation: HatchEditOperation,
|
||||
},
|
||||
/// STRETCH crossing-window selection. The command can accumulate several
|
||||
/// independent crossing windows before Enter ends the selection stage.
|
||||
|
|
@ -1352,17 +1425,20 @@ pub enum CmdResult {
|
|||
/// Translation vector applied once to every selected point.
|
||||
delta: DVec3,
|
||||
},
|
||||
/// Create a Solid3D placeholder entity + associated MeshModel.
|
||||
/// `mesh_fn` is called with the entity's handle string to build the mesh.
|
||||
CommitSolid3D {
|
||||
mesh_fn: Box<dyn FnOnce(String) -> Option<crate::scene::model::mesh_model::MeshModel> + Send>,
|
||||
},
|
||||
/// Extrude the profile entity `handle` by `height` along Z.
|
||||
/// Extrude the profile entity `handle` along its plane normal.
|
||||
ExtrudeEntity {
|
||||
handle: Handle,
|
||||
height: f64,
|
||||
color: [f32; 4],
|
||||
},
|
||||
/// Pull a closed profile or a planar solid face by a signed distance.
|
||||
PresspullEntity {
|
||||
handle: Handle,
|
||||
pick: DVec3,
|
||||
distance: f64,
|
||||
drag: Option<DVec3>,
|
||||
color: [f32; 4],
|
||||
},
|
||||
/// Revolve the profile entity `handle` around the given axis by `angle_deg`.
|
||||
RevolveEntity {
|
||||
handle: Handle,
|
||||
|
|
@ -1382,6 +1458,17 @@ pub enum CmdResult {
|
|||
handles: Vec<Handle>,
|
||||
color: [f32; 4],
|
||||
},
|
||||
/// Round or bevel the straight edge nearest `pick` on a solid.
|
||||
SolidEdgeBlend {
|
||||
handle: Handle,
|
||||
pick: DVec3,
|
||||
value: f64,
|
||||
fillet: bool,
|
||||
},
|
||||
SolidSubtract {
|
||||
bases: Vec<Handle>,
|
||||
cutters: Vec<Handle>,
|
||||
},
|
||||
/// INSERT landed on a block that has AttributeDefinitions.
|
||||
/// The host should look up the attdefs for `block_name` from the document
|
||||
/// and call `attreq_set_attdefs()` on the command, then loop on text input.
|
||||
|
|
@ -1694,6 +1781,14 @@ pub trait CadCommand: Send {
|
|||
false
|
||||
}
|
||||
|
||||
/// Supply the mesh surface hit instead of the working-plane projection.
|
||||
fn entity_pick_uses_surface_point(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Supply the picked surface or profile direction when available.
|
||||
fn set_entity_pick_direction(&mut self, _direction: Option<DVec3>) {}
|
||||
|
||||
/// Render the entity under the cursor through the normal rollover
|
||||
/// highlight while this command is waiting for an entity pick.
|
||||
fn entity_pick_highlights_hover(&self) -> bool {
|
||||
|
|
@ -1818,6 +1913,16 @@ pub trait CadCommand: Send {
|
|||
false
|
||||
}
|
||||
|
||||
/// Current drawing-persisted SKETCH settings.
|
||||
fn sketch_settings(&self) -> Option<(i16, f64, f64)> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Current drawing-persisted multiline creation settings.
|
||||
fn mline_settings(&self) -> Option<(f64, i16, String, Option<Handle>)> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns `true` when the active text prompt expects free-form prose
|
||||
/// that can legitimately contain whitespace (the body of a TEXT /
|
||||
/// MTEXT / DDEDIT entity, an attribute default value, etc.). For
|
||||
|
|
@ -1903,10 +2008,13 @@ pub trait CadCommand: Send {
|
|||
None
|
||||
}
|
||||
|
||||
/// Inject the block's attribute definitions for ATTREQ attr-filling after
|
||||
/// the INSERT point is picked. Carries the full definitions so the created
|
||||
/// attributes inherit their geometry, not just tag / prompt / default (#255).
|
||||
fn attreq_set_attdefs(&mut self, _attdefs: Vec<acadrust::entities::AttributeDefinition>) {}
|
||||
/// Inject block attribute definitions after the INSERT point is picked.
|
||||
fn attreq_set_attdefs(
|
||||
&mut self,
|
||||
_attdefs: Vec<acadrust::entities::AttributeDefinition>,
|
||||
) -> Option<acadrust::EntityType> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the INSERT entity built so far (pending attr fill) if this is an
|
||||
/// ATTREQ-aware INSERT command waiting for attdef injection.
|
||||
|
|
|
|||
|
|
@ -22,13 +22,8 @@ fn to_render(arc: &Arc) -> RenderEntity {
|
|||
let ea = arc.end_angle;
|
||||
let normal = (arc.normal.x, arc.normal.y, arc.normal.z);
|
||||
|
||||
// Compute OCS basis vectors for this entity's normal.
|
||||
let (ax, ay) = crate::scene::view::transform::ocs_axes(normal);
|
||||
|
||||
// Arc centre in WCS.
|
||||
let (cwx, cwy, cwz) = crate::scene::view::transform::ocs_point_to_wcs((cx, cy, cz), normal);
|
||||
|
||||
// Arc points in WCS: centre_wcs + r*cos(a)*Ax + r*sin(a)*Ay
|
||||
let arc_pt = |a: f64| {
|
||||
let (c, s) = (a.cos(), a.sin());
|
||||
[
|
||||
|
|
@ -43,9 +38,13 @@ fn to_render(arc: &Arc) -> RenderEntity {
|
|||
// curves) deliberately emit no midpoint; see #34.
|
||||
let curve = crate::entities::curve::arc_curve(arc);
|
||||
let snap = crate::entities::curve::snap_from(&curve);
|
||||
let tangent = TangentGeom::Circle {
|
||||
center: [cwx as f32, cwy as f32, cwz as f32],
|
||||
radius: r as f32,
|
||||
let tangent = TangentGeom::Arc {
|
||||
center: [cwx, cwy, cwz],
|
||||
axis_x: [ax.0, ax.1, ax.2],
|
||||
axis_y: [ay.0, ay.1, ay.2],
|
||||
radius: r,
|
||||
start_angle: sa,
|
||||
end_angle: ea,
|
||||
};
|
||||
|
||||
if arc.thickness.abs() > 1e-10 {
|
||||
|
|
@ -98,56 +97,14 @@ fn to_render(arc: &Arc) -> RenderEntity {
|
|||
}
|
||||
|
||||
fn control_points(arc: &Arc) -> [glam::DVec3; 3] {
|
||||
let center = glam::DVec3::new(arc.center.x, arc.center.y, arc.center.z);
|
||||
let sweep = (arc.end_angle - arc.start_angle).rem_euclid(TAU);
|
||||
let point = |angle: f64| {
|
||||
center
|
||||
+ glam::DVec3::new(
|
||||
arc.radius * angle.cos(),
|
||||
arc.radius * angle.sin(),
|
||||
0.0,
|
||||
)
|
||||
};
|
||||
let curve = crate::entities::curve::arc_curve(arc);
|
||||
[
|
||||
point(arc.start_angle),
|
||||
point(arc.start_angle + sweep * 0.5),
|
||||
point(arc.end_angle),
|
||||
glam::DVec3::from_array(curve.point_at(0.0)),
|
||||
glam::DVec3::from_array(curve.point_at(0.5)),
|
||||
glam::DVec3::from_array(curve.point_at(1.0)),
|
||||
]
|
||||
}
|
||||
|
||||
fn circumcircle(
|
||||
a: glam::DVec3,
|
||||
b: glam::DVec3,
|
||||
c: glam::DVec3,
|
||||
) -> Option<(glam::DVec3, f64)> {
|
||||
// Work relative to the first point so large drawing coordinates do not
|
||||
// lose the small differences that define the circle.
|
||||
let ab = b - a;
|
||||
let ac = c - a;
|
||||
let bc = c - b;
|
||||
let scale2 = ab
|
||||
.length_squared()
|
||||
.max(ac.length_squared())
|
||||
.max(bc.length_squared());
|
||||
if scale2 < 1.0e-18 {
|
||||
return None;
|
||||
}
|
||||
let det = 2.0 * (ab.x * ac.y - ab.y * ac.x);
|
||||
if det.abs() <= scale2 * 1.0e-12 {
|
||||
return None;
|
||||
}
|
||||
let ab2 = ab.x * ab.x + ab.y * ab.y;
|
||||
let ac2 = ac.x * ac.x + ac.y * ac.y;
|
||||
let center = a
|
||||
+ glam::DVec3::new(
|
||||
(ab2 * ac.y - ac2 * ab.y) / det,
|
||||
(ab.x * ac2 - ac.x * ab2) / det,
|
||||
0.0,
|
||||
);
|
||||
let radius = center.distance(a);
|
||||
radius.is_finite().then_some((center, radius))
|
||||
}
|
||||
|
||||
pub(crate) fn refit_grips(
|
||||
arc: &mut Arc,
|
||||
original: &Arc,
|
||||
|
|
@ -169,36 +126,35 @@ pub(crate) fn refit_grips(
|
|||
return false;
|
||||
}
|
||||
|
||||
let Some((center, radius)) = circumcircle(points[0], points[1], points[2]) else {
|
||||
let plane = crate::entities::curve::arc_curve(original).plane;
|
||||
let Some(a) = plane.project(points[0].to_array()) else {
|
||||
return false;
|
||||
};
|
||||
if radius <= 1.0e-9 {
|
||||
let Some(b) = plane.project(points[1].to_array()) else {
|
||||
return false;
|
||||
}
|
||||
|
||||
let start = (points[0].y - center.y).atan2(points[0].x - center.x);
|
||||
let middle = (points[1].y - center.y).atan2(points[1].x - center.x);
|
||||
let end = (points[2].y - center.y).atan2(points[2].x - center.x);
|
||||
let sweep = (end - start).rem_euclid(TAU);
|
||||
let middle_sweep = (middle - start).rem_euclid(TAU);
|
||||
// Crossing the two fixed points makes the three-point definition
|
||||
// degenerate before it reverses. Keep the last valid preview instead of
|
||||
// swapping the start and end grip identities under the cursor.
|
||||
if sweep <= 1.0e-9 || middle_sweep > sweep + 1.0e-9 {
|
||||
};
|
||||
let Some(c) = plane.project(points[2].to_array()) else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let Some(fit) = cadkernel::geom2d::arc_through_points(a, b, c) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
arc.center.x = center.x;
|
||||
arc.center.y = center.y;
|
||||
arc.center.x = fit.centre[0];
|
||||
arc.center.y = fit.centre[1];
|
||||
arc.center.z = original.center.z;
|
||||
arc.radius = radius;
|
||||
arc.start_angle = start;
|
||||
arc.end_angle = end;
|
||||
arc.radius = fit.radius;
|
||||
arc.start_angle = fit.start_angle;
|
||||
arc.end_angle = fit.end_angle;
|
||||
true
|
||||
}
|
||||
|
||||
fn grips(arc: &Arc) -> Vec<GripDef> {
|
||||
let ctr = glam::DVec3::new(arc.center.x, arc.center.y, arc.center.z);
|
||||
let (x, y, z) = crate::scene::view::transform::ocs_point_to_wcs(
|
||||
(arc.center.x, arc.center.y, arc.center.z),
|
||||
(arc.normal.x, arc.normal.y, arc.normal.z),
|
||||
);
|
||||
let ctr = glam::DVec3::new(x, y, z);
|
||||
let [start, middle, end] = control_points(arc);
|
||||
vec![
|
||||
center_grip(0, ctr),
|
||||
|
|
@ -215,7 +171,11 @@ fn properties(arc: &Arc) -> Vec<PropSection> {
|
|||
let sweep = (ea - sa).rem_euclid(TAU);
|
||||
let total_angle = sweep.to_degrees();
|
||||
let arc_length = r * sweep;
|
||||
let area = 0.5 * r * r * sweep;
|
||||
let area = crate::entities::curve::arc_curve(arc)
|
||||
.curve
|
||||
.chord_closed_area()
|
||||
.unwrap_or(0.0)
|
||||
.abs();
|
||||
|
||||
let normal = (arc.normal.x, arc.normal.y, arc.normal.z);
|
||||
let (ax, ay) = crate::scene::view::transform::ocs_axes(normal);
|
||||
|
|
@ -240,9 +200,9 @@ fn properties(arc: &Arc) -> Vec<PropSection> {
|
|||
ro(t!("Start X").as_ref(), "start_x", format!("{sx:.4}")),
|
||||
ro(t!("Start Y").as_ref(), "start_y", format!("{sy:.4}")),
|
||||
ro(t!("Start Z").as_ref(), "start_z", format!("{sz:.4}")),
|
||||
edit(t!("Center X").as_ref(), "center_x", arc.center.x),
|
||||
edit(t!("Center Y").as_ref(), "center_y", arc.center.y),
|
||||
edit(t!("Center Z").as_ref(), "center_z", arc.center.z),
|
||||
edit(t!("Center X").as_ref(), "center_x", cwx),
|
||||
edit(t!("Center Y").as_ref(), "center_y", cwy),
|
||||
edit(t!("Center Z").as_ref(), "center_z", cwz),
|
||||
ro(t!("End X").as_ref(), "end_x", format!("{ex:.4}")),
|
||||
ro(t!("End Y").as_ref(), "end_y", format!("{ey:.4}")),
|
||||
ro(t!("End Z").as_ref(), "end_z", format!("{ez:.4}")),
|
||||
|
|
@ -264,9 +224,23 @@ fn apply_geom_prop(arc: &mut Arc, field: &str, value: &str) {
|
|||
return;
|
||||
};
|
||||
match field {
|
||||
"center_x" => arc.center.x = v,
|
||||
"center_y" => arc.center.y = v,
|
||||
"center_z" => arc.center.z = v,
|
||||
"center_x" | "center_y" | "center_z" => {
|
||||
let normal = (arc.normal.x, arc.normal.y, arc.normal.z);
|
||||
let (mut x, mut y, mut z) = crate::scene::view::transform::ocs_point_to_wcs(
|
||||
(arc.center.x, arc.center.y, arc.center.z),
|
||||
normal,
|
||||
);
|
||||
match field {
|
||||
"center_x" => x = v,
|
||||
"center_y" => y = v,
|
||||
"center_z" => z = v,
|
||||
_ => {}
|
||||
}
|
||||
let (ox, oy, oz) = crate::scene::view::transform::wcs_point_to_ocs((x, y, z), normal);
|
||||
arc.center.x = ox;
|
||||
arc.center.y = oy;
|
||||
arc.center.z = oz;
|
||||
}
|
||||
"radius" if v > 0.0 => arc.radius = v,
|
||||
"start_angle" => arc.start_angle = v.to_radians(),
|
||||
"end_angle" => arc.end_angle = v.to_radians(),
|
||||
|
|
@ -277,14 +251,22 @@ fn apply_geom_prop(arc: &mut Arc, field: &str, value: &str) {
|
|||
fn apply_grip(arc: &mut Arc, grip_id: usize, apply: GripApply) {
|
||||
match (grip_id, apply) {
|
||||
(0, GripApply::Translate(d)) => {
|
||||
arc.center.x += d.x;
|
||||
arc.center.y += d.y;
|
||||
arc.center.z += d.z;
|
||||
let (x, y, z) = crate::scene::view::transform::wcs_point_to_ocs(
|
||||
(d.x, d.y, d.z),
|
||||
(arc.normal.x, arc.normal.y, arc.normal.z),
|
||||
);
|
||||
arc.center.x += x;
|
||||
arc.center.y += y;
|
||||
arc.center.z += z;
|
||||
}
|
||||
(0, GripApply::Absolute(p)) => {
|
||||
arc.center.x = p[0];
|
||||
arc.center.y = p[1];
|
||||
arc.center.z = p[2];
|
||||
let (x, y, z) = crate::scene::view::transform::wcs_point_to_ocs(
|
||||
(p[0], p[1], p[2]),
|
||||
(arc.normal.x, arc.normal.y, arc.normal.z),
|
||||
);
|
||||
arc.center.x = x;
|
||||
arc.center.y = y;
|
||||
arc.center.z = z;
|
||||
}
|
||||
(1..=3, GripApply::Absolute(p)) => {
|
||||
let original = arc.clone();
|
||||
|
|
@ -386,7 +368,11 @@ impl crate::entities::traits::Grippable for Arc {
|
|||
if !matches!(action, A::Lengthen) || self.radius <= 1.0e-9 {
|
||||
return None;
|
||||
}
|
||||
let cursor_angle = (point.y - self.center.y).atan2(point.x - self.center.x);
|
||||
let (x, y, _) = crate::scene::view::transform::wcs_point_to_ocs(
|
||||
(point.x, point.y, point.z),
|
||||
(self.normal.x, self.normal.y, self.normal.z),
|
||||
);
|
||||
let cursor_angle = (y - self.center.y).atan2(x - self.center.x);
|
||||
let current_sweep = (self.end_angle - self.start_angle).rem_euclid(TAU);
|
||||
let desired_sweep = match grip_id {
|
||||
1 => (self.end_angle - cursor_angle).rem_euclid(TAU),
|
||||
|
|
@ -412,7 +398,9 @@ impl crate::entities::traits::Grippable for Arc {
|
|||
// Hold start_angle, derive new end_angle from arc length
|
||||
// = r * Δθ.
|
||||
let new_span = value / self.radius;
|
||||
self.end_angle = self.start_angle + new_span;
|
||||
if new_span < TAU - 1.0e-9 {
|
||||
self.end_angle = self.start_angle + new_span;
|
||||
}
|
||||
}
|
||||
A::Lengthen => {
|
||||
// Extend either end by `value` arc-length units along
|
||||
|
|
@ -422,6 +410,11 @@ impl crate::entities::traits::Grippable for Arc {
|
|||
return;
|
||||
}
|
||||
let dtheta = value / self.radius;
|
||||
let current = (self.end_angle - self.start_angle).rem_euclid(TAU);
|
||||
let next = current + dtheta;
|
||||
if next <= 1.0e-9 || next >= TAU - 1.0e-9 {
|
||||
return;
|
||||
}
|
||||
match grip_id {
|
||||
1 => self.start_angle -= dtheta,
|
||||
2 => self.end_angle += dtheta,
|
||||
|
|
@ -450,26 +443,18 @@ impl crate::entities::traits::Transformable for Arc {
|
|||
|
||||
impl crate::entities::traits::MassPropsCalc for acadrust::entities::Arc {
|
||||
fn mass_props(&self) -> crate::entities::traits::MassProps {
|
||||
use std::f64::consts::TAU;
|
||||
let r = self.radius;
|
||||
let span = {
|
||||
let s = (self.end_angle - self.start_angle).rem_euclid(TAU);
|
||||
if s < 1e-6 {
|
||||
TAU
|
||||
} else {
|
||||
s
|
||||
}
|
||||
};
|
||||
// Sector area (pie slice)
|
||||
let area = 0.5 * r * r * span;
|
||||
let arc_len = r * span;
|
||||
// Centroid of arc (chord midpoint direction)
|
||||
let mid_rad = self.start_angle + span / 2.0;
|
||||
let curve = crate::entities::curve::arc_curve(self);
|
||||
let area = curve.curve.chord_closed_area().unwrap_or(0.0).abs();
|
||||
let centroid = curve
|
||||
.curve
|
||||
.chord_closed_centroid()
|
||||
.map(|point| curve.plane.point_at(point))
|
||||
.unwrap_or_else(|| curve.point_at(0.5));
|
||||
crate::entities::traits::MassProps {
|
||||
area,
|
||||
perimeter: arc_len,
|
||||
cx: self.center.x + r * mid_rad.cos(),
|
||||
cy: self.center.y + r * mid_rad.sin(),
|
||||
perimeter: curve.length(),
|
||||
cx: centroid[0],
|
||||
cy: centroid[1],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -417,17 +417,17 @@ impl PropertyEditable for AttributeDefinition {
|
|||
Property {
|
||||
label: t!("Tag").into_owned(),
|
||||
field: "att_tag",
|
||||
value: PropValue::EditText(self.tag.clone()),
|
||||
value: PropValue::PlainText(self.tag.clone()),
|
||||
},
|
||||
Property {
|
||||
label: t!("Prompt").into_owned(),
|
||||
field: "att_prompt",
|
||||
value: PropValue::EditText(self.prompt.clone()),
|
||||
value: PropValue::PlainText(self.prompt.clone()),
|
||||
},
|
||||
Property {
|
||||
label: t!("Value").into_owned(),
|
||||
field: "att_default",
|
||||
value: PropValue::EditText(self.default_value.clone()),
|
||||
value: PropValue::PlainText(self.default_value.clone()),
|
||||
},
|
||||
Property {
|
||||
label: t!("Style").into_owned(),
|
||||
|
|
@ -698,13 +698,13 @@ impl PropertyEditable for AttributeEntity {
|
|||
Property {
|
||||
label: t!("Tag").into_owned(),
|
||||
field: "atte_tag",
|
||||
value: PropValue::EditText(self.tag.clone()),
|
||||
value: PropValue::PlainText(self.tag.clone()),
|
||||
},
|
||||
ro(t!("Prompt").as_ref(), "atte_prompt", String::new()),
|
||||
Property {
|
||||
label: t!("Value").into_owned(),
|
||||
field: "atte_val",
|
||||
value: PropValue::EditText(self.value.clone()),
|
||||
value: PropValue::PlainText(self.value.clone()),
|
||||
},
|
||||
Property {
|
||||
label: t!("Style").into_owned(),
|
||||
|
|
|
|||
|
|
@ -20,14 +20,15 @@ fn to_render(circle: &Circle) -> RenderEntity {
|
|||
let (ax, ay) = crate::scene::view::transform::ocs_axes(normal);
|
||||
let (cwx, cwy, cwz) = crate::scene::view::transform::ocs_point_to_wcs((cx, cy, cz), normal);
|
||||
|
||||
let rf = r as f32;
|
||||
// Centre and quadrants come from the entity's own curve, so the same
|
||||
// definition answers here, in the tessellation and in a trim.
|
||||
let curve = crate::entities::curve::circle_curve(circle);
|
||||
let snap_pts = crate::entities::curve::snap_from(&curve).snap_pts;
|
||||
let tangent = TangentGeom::Circle {
|
||||
center: [cwx as f32, cwy as f32, cwz as f32],
|
||||
radius: rf,
|
||||
let tangent = TangentGeom::PlanarCircle {
|
||||
center: [cwx, cwy, cwz],
|
||||
axis_x: [ax.0, ax.1, ax.2],
|
||||
axis_y: [ay.0, ay.1, ay.2],
|
||||
radius: r,
|
||||
};
|
||||
|
||||
if circle.thickness.abs() > 1e-10 {
|
||||
|
|
@ -74,14 +75,7 @@ fn to_render(circle: &Circle) -> RenderEntity {
|
|||
};
|
||||
}
|
||||
|
||||
// Sampled from the entity's own curve rather than as a
|
||||
// `circle_arc` (arc-through-three-points). At large WCS coordinates
|
||||
// (e.g. −1.2M UTM) the three-point fit cancels catastrophically — the
|
||||
// circle comes back with a ~3% radius wobble and uneven segment lengths,
|
||||
// which then throws off a dashed linetype's dash spacing. Direct
|
||||
// evaluation only adds a small ±r term to the centre, so it stays
|
||||
// precise; the `Lines` path RTE-splits the absolute-f64 points into the
|
||||
// double-single the shader reconstructs.
|
||||
// Keep tessellation on the entity curve for large-coordinate precision.
|
||||
let pts = crate::entities::curve::curve_points(&curve);
|
||||
|
||||
RenderEntity {
|
||||
|
|
@ -95,26 +89,31 @@ fn to_render(circle: &Circle) -> RenderEntity {
|
|||
}
|
||||
|
||||
fn grips(circle: &Circle) -> Vec<GripDef> {
|
||||
let ctr = glam::DVec3::new(circle.center.x, circle.center.y, circle.center.z);
|
||||
let center = circle.center_wcs();
|
||||
let (axis_x, axis_y) = circle.axes_wcs();
|
||||
let ctr = glam::DVec3::new(center.x, center.y, center.z);
|
||||
let axis_x = glam::DVec3::new(axis_x.x, axis_x.y, axis_x.z);
|
||||
let axis_y = glam::DVec3::new(axis_y.x, axis_y.y, axis_y.z);
|
||||
let r = circle.radius;
|
||||
vec![
|
||||
center_grip(0, ctr),
|
||||
square_grip(1, ctr + glam::DVec3::new(r, 0.0, 0.0)),
|
||||
square_grip(2, ctr + glam::DVec3::new(0.0, r, 0.0)),
|
||||
square_grip(3, ctr - glam::DVec3::new(r, 0.0, 0.0)),
|
||||
square_grip(4, ctr - glam::DVec3::new(0.0, r, 0.0)),
|
||||
square_grip(1, ctr + axis_x * r),
|
||||
square_grip(2, ctr + axis_y * r),
|
||||
square_grip(3, ctr - axis_x * r),
|
||||
square_grip(4, ctr - axis_y * r),
|
||||
]
|
||||
}
|
||||
|
||||
fn properties(circle: &Circle) -> Vec<PropSection> {
|
||||
use std::f64::consts::PI;
|
||||
let r = circle.radius;
|
||||
let center = circle.center_wcs();
|
||||
vec![PropSection {
|
||||
title: t!("Geometry").into_owned(),
|
||||
props: vec![
|
||||
edit(t!("Center X").as_ref(), "center_x", circle.center.x),
|
||||
edit(t!("Center Y").as_ref(), "center_y", circle.center.y),
|
||||
edit(t!("Center Z").as_ref(), "center_z", circle.center.z),
|
||||
edit(t!("Center X").as_ref(), "center_x", center.x),
|
||||
edit(t!("Center Y").as_ref(), "center_y", center.y),
|
||||
edit(t!("Center Z").as_ref(), "center_z", center.z),
|
||||
edit(t!("Radius").as_ref(), "radius", r),
|
||||
edit(t!("Diameter").as_ref(), "diameter", r * 2.0),
|
||||
edit(t!("Circumference").as_ref(), "circumference", 2.0 * PI * r),
|
||||
|
|
@ -132,9 +131,22 @@ fn apply_geom_prop(circle: &mut Circle, field: &str, value: &str) {
|
|||
return;
|
||||
};
|
||||
match field {
|
||||
"center_x" => circle.center.x = v,
|
||||
"center_y" => circle.center.y = v,
|
||||
"center_z" => circle.center.z = v,
|
||||
"center_x" | "center_y" | "center_z" => {
|
||||
let normal = (circle.normal.x, circle.normal.y, circle.normal.z);
|
||||
let center = circle.center_wcs();
|
||||
let (mut x, mut y, mut z) = (center.x, center.y, center.z);
|
||||
match field {
|
||||
"center_x" => x = v,
|
||||
"center_y" => y = v,
|
||||
"center_z" => z = v,
|
||||
_ => {}
|
||||
}
|
||||
let (ox, oy, oz) =
|
||||
crate::scene::view::transform::wcs_point_to_ocs((x, y, z), normal);
|
||||
circle.center.x = ox;
|
||||
circle.center.y = oy;
|
||||
circle.center.z = oz;
|
||||
}
|
||||
"radius" if v > 0.0 => circle.radius = v,
|
||||
"diameter" if v > 0.0 => circle.radius = v / 2.0,
|
||||
"circumference" if v > 0.0 => circle.radius = v / (2.0 * PI),
|
||||
|
|
@ -146,19 +158,31 @@ fn apply_geom_prop(circle: &mut Circle, field: &str, value: &str) {
|
|||
fn apply_grip(circle: &mut Circle, grip_id: usize, apply: GripApply) {
|
||||
match (grip_id, apply) {
|
||||
(0, GripApply::Absolute(p)) => {
|
||||
circle.center.x = p.x as f64;
|
||||
circle.center.y = p.y as f64;
|
||||
circle.center.z = p.z as f64;
|
||||
let (x, y, z) = crate::scene::view::transform::wcs_point_to_ocs(
|
||||
(p.x, p.y, p.z),
|
||||
(circle.normal.x, circle.normal.y, circle.normal.z),
|
||||
);
|
||||
circle.center.x = x;
|
||||
circle.center.y = y;
|
||||
circle.center.z = z;
|
||||
}
|
||||
(0, GripApply::Translate(d)) => {
|
||||
circle.center.x += d.x as f64;
|
||||
circle.center.y += d.y as f64;
|
||||
circle.center.z += d.z as f64;
|
||||
let (x, y, z) = crate::scene::view::transform::wcs_point_to_ocs(
|
||||
(d.x, d.y, d.z),
|
||||
(circle.normal.x, circle.normal.y, circle.normal.z),
|
||||
);
|
||||
circle.center.x += x;
|
||||
circle.center.y += y;
|
||||
circle.center.z += z;
|
||||
}
|
||||
(1..=4, GripApply::Absolute(p)) => {
|
||||
let dx = p.x - circle.center.x;
|
||||
let dy = p.y - circle.center.y;
|
||||
circle.radius = (dx * dx + dy * dy).sqrt();
|
||||
let plane = crate::entities::curve::circle_curve(circle).plane;
|
||||
if let Some(point) = plane.project([p.x, p.y, p.z]) {
|
||||
let radius = (point[0] - circle.center.x).hypot(point[1] - circle.center.y);
|
||||
if radius > 1.0e-9 {
|
||||
circle.radius = radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -187,11 +211,12 @@ impl crate::entities::traits::MassPropsCalc for Circle {
|
|||
fn mass_props(&self) -> crate::entities::traits::MassProps {
|
||||
use std::f64::consts::{PI, TAU};
|
||||
let r = self.radius;
|
||||
let center = self.center_wcs();
|
||||
crate::entities::traits::MassProps {
|
||||
area: PI * r * r,
|
||||
perimeter: TAU * r,
|
||||
cx: self.center.x,
|
||||
cy: self.center.y,
|
||||
cx: center.x,
|
||||
cy: center.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,31 +104,31 @@ pub fn format_length(value: f64) -> String {
|
|||
match ctx.lunits {
|
||||
1 => format!("{:.*e}", prec, value),
|
||||
3 => {
|
||||
// Engineering: ft-inches, decimal inches.
|
||||
// Round before splitting so twelve inches carries into feet.
|
||||
let sign = if value < 0.0 { "-" } else { "" };
|
||||
let abs = value.abs();
|
||||
let feet = (abs / 12.0).trunc();
|
||||
let rem = abs - feet * 12.0;
|
||||
let scale = 10f64.powi(prec.min(15) as i32);
|
||||
let total = (abs * scale).round();
|
||||
let per_foot = 12.0 * scale;
|
||||
let feet = (total / per_foot).trunc();
|
||||
let rem = (total - feet * per_foot) / scale;
|
||||
format!("{}{:.0}'-{:.*}\"", sign, feet, prec, rem)
|
||||
}
|
||||
4 | 5 => {
|
||||
// Architectural / Fractional — n + fraction with 1/2^p denom (1
|
||||
// unit = 1 inch). Use 6 as a moderate denominator power so the
|
||||
// result reads like 1/64".
|
||||
// Architectural and fractional formats use 1/64-inch resolution.
|
||||
let sign = if value < 0.0 { "-" } else { "" };
|
||||
let abs = value.abs();
|
||||
let (feet, in_rem) = if ctx.lunits == 4 {
|
||||
let f = (abs / 12.0).trunc();
|
||||
(Some(f as i64), abs - f * 12.0)
|
||||
let denom = 64.0;
|
||||
let ticks = (abs * denom).round();
|
||||
let (feet, rest) = if ctx.lunits == 4 {
|
||||
let per_foot = 12.0 * denom;
|
||||
(Some((ticks / per_foot).floor()), ticks.rem_euclid(per_foot))
|
||||
} else {
|
||||
(None, abs)
|
||||
(None, ticks)
|
||||
};
|
||||
let whole = in_rem.trunc();
|
||||
let frac = in_rem - whole;
|
||||
let denom = 64u64;
|
||||
let numer = (frac * denom as f64).round() as i64;
|
||||
let mut n = numer as u64;
|
||||
let mut d = denom;
|
||||
let whole = (rest / denom).floor();
|
||||
let mut n = (rest - whole * denom).round() as u64;
|
||||
let mut d = denom as u64;
|
||||
while d > 1 && n % 2 == 0 && d % 2 == 0 {
|
||||
n /= 2;
|
||||
d /= 2;
|
||||
|
|
@ -140,7 +140,7 @@ pub fn format_length(value: f64) -> String {
|
|||
};
|
||||
let unit_suffix = if ctx.lunits == 4 { "\"" } else { "" };
|
||||
match feet {
|
||||
Some(f) => format!("{}{}'-{:.0}{}{}", sign, f, whole, frac_str, unit_suffix),
|
||||
Some(f) => format!("{}{:.0}'-{:.0}{}{}", sign, f, whole, frac_str, unit_suffix),
|
||||
None => format!("{}{:.0}{}", sign, whole, frac_str),
|
||||
}
|
||||
}
|
||||
|
|
@ -148,6 +148,18 @@ pub fn format_length(value: f64) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Format an area with the drawing's linear precision. Area stays a decimal
|
||||
/// scalar even when linear distances use architectural or fractional notation.
|
||||
pub fn format_area(value: f64) -> String {
|
||||
let ctx = unit_context();
|
||||
let precision = ctx.luprec.max(0).min(15) as usize;
|
||||
if ctx.lunits == 1 {
|
||||
format!("{:.*e}", precision, value)
|
||||
} else {
|
||||
format!("{:.*}", precision, value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Format an angle (input in radians) using AUNITS / AUPREC.
|
||||
pub fn format_angle(value_rad: f64) -> String {
|
||||
let ctx = unit_context();
|
||||
|
|
@ -169,10 +181,15 @@ pub fn format_angle(value_rad: f64) -> String {
|
|||
fn dms(degrees: f64, prec: usize) -> String {
|
||||
let sign = if degrees < 0.0 { "-" } else { "" };
|
||||
let a = degrees.abs();
|
||||
let d = a.floor();
|
||||
let m_full = (a - d) * 60.0;
|
||||
let m = m_full.floor();
|
||||
let s = (m_full - m) * 60.0;
|
||||
// Round before splitting so seconds carry into minutes and degrees.
|
||||
let scale = 10f64.powi(prec.min(15) as i32);
|
||||
let per_minute = 60.0 * scale;
|
||||
let per_degree = 60.0 * per_minute;
|
||||
let ticks = (a * per_degree).round();
|
||||
let d = (ticks / per_degree).trunc();
|
||||
let rest = ticks - d * per_degree;
|
||||
let m = (rest / per_minute).trunc();
|
||||
let s = (rest - m * per_minute) / scale;
|
||||
format!("{}{:.0}d{:.0}'{:.*}\"", sign, d, m, prec, s)
|
||||
}
|
||||
|
||||
|
|
@ -183,20 +200,26 @@ fn dms(degrees: f64, prec: usize) -> String {
|
|||
/// the single letter, which is also what keeps a 90° angle from reading as the
|
||||
/// contradictory `N 90d0'0" E`.
|
||||
fn surveyor(value_rad: f64, prec: usize) -> String {
|
||||
let deg = value_rad.to_degrees().rem_euclid(360.0);
|
||||
let near = |target: f64| (deg - target).abs() < 1e-9;
|
||||
if near(0.0) {
|
||||
// Quantize before choosing a cardinal or quadrant.
|
||||
let scale = 3600.0 * 10f64.powi(prec.min(15) as i32);
|
||||
let turn = 360.0 * scale;
|
||||
let ticks = (value_rad.to_degrees().rem_euclid(360.0) * scale)
|
||||
.round()
|
||||
.rem_euclid(turn);
|
||||
let at = |degrees: f64| (ticks - degrees * scale).abs() < 0.5;
|
||||
if at(0.0) {
|
||||
return "E".into();
|
||||
}
|
||||
if near(90.0) {
|
||||
if at(90.0) {
|
||||
return "N".into();
|
||||
}
|
||||
if near(180.0) {
|
||||
if at(180.0) {
|
||||
return "W".into();
|
||||
}
|
||||
if near(270.0) {
|
||||
if at(270.0) {
|
||||
return "S".into();
|
||||
}
|
||||
let deg = ticks / scale;
|
||||
// Measured from the nearer pole, toward the side the angle falls on.
|
||||
let (pole, bearing, side) = if deg < 90.0 {
|
||||
("N", 90.0 - deg, "E")
|
||||
|
|
@ -409,6 +432,11 @@ pub fn parse_direction(text: &str) -> Option<f64> {
|
|||
Some(relative + ctx.angbase)
|
||||
}
|
||||
|
||||
/// Read a direction typed at a command prompt, accepting a decimal comma.
|
||||
pub fn parse_typed_direction(text: &str) -> Option<f64> {
|
||||
parse_direction(&text.replace(',', "."))
|
||||
}
|
||||
|
||||
/// Two interior triangles covering a quad (flat list, 6 vertices) — the
|
||||
/// click-anywhere pick surface for frame-like entities (image, OLE frame,
|
||||
/// underlay, wipeout). Corners in ring order.
|
||||
|
|
@ -453,6 +481,30 @@ pub fn circle_grip(id: usize, world: glam::DVec3) -> GripDef {
|
|||
}
|
||||
}
|
||||
|
||||
/// Round grip that edits one vertex.
|
||||
pub fn round_grip(id: usize, world: glam::DVec3) -> GripDef {
|
||||
GripDef {
|
||||
id,
|
||||
world,
|
||||
is_midpoint: false,
|
||||
shape: GripShape::Circle,
|
||||
dir: None,
|
||||
axis: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Screen-offset down-arrow that opens a choice menu for an entity.
|
||||
pub fn dropdown_grip(id: usize, world: glam::DVec3) -> GripDef {
|
||||
GripDef {
|
||||
id,
|
||||
world,
|
||||
is_midpoint: false,
|
||||
shape: GripShape::Dropdown,
|
||||
dir: None,
|
||||
axis: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Mid-segment stretch grip oriented along `dir` (the segment's in-plane
|
||||
/// world-XY direction). Drawn as a small rectangle elongated along the
|
||||
/// segment so the affordance reads as "stretch perpendicular".
|
||||
|
|
@ -462,7 +514,7 @@ pub fn rectangle_grip(id: usize, world: glam::DVec3, dir: [f32; 2]) -> GripDef {
|
|||
world,
|
||||
is_midpoint: true,
|
||||
shape: GripShape::Rectangle,
|
||||
dir: Some(dir),
|
||||
dir: Some(glam::DVec3::new(dir[0] as f64, dir[1] as f64, 0.0)),
|
||||
axis: None,
|
||||
}
|
||||
}
|
||||
|
|
@ -479,6 +531,18 @@ pub fn triangle_grip(id: usize, world: glam::DVec3) -> GripDef {
|
|||
}
|
||||
}
|
||||
|
||||
/// Triangle grip pointing along a world-space direction.
|
||||
pub fn oriented_triangle_grip(id: usize, world: glam::DVec3, dir: glam::DVec3) -> GripDef {
|
||||
GripDef {
|
||||
id,
|
||||
world,
|
||||
is_midpoint: false,
|
||||
shape: GripShape::Triangle,
|
||||
dir: Some(dir),
|
||||
axis: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Editable ANGLE row: displays via AUNITS/AUPREC. Angle rows used the
|
||||
/// LINEAR formatter, so LUNITS=Architectural showed a block rotation as
|
||||
/// feet-and-inches and the string wouldn't parse back (#297). Value in
|
||||
|
|
@ -499,6 +563,16 @@ pub fn edit_prop(label: &str, field: &'static str, value: f64) -> Property {
|
|||
}
|
||||
}
|
||||
|
||||
/// Editable dimensionless value independent of drawing units.
|
||||
pub fn edit_scalar_prop(label: &str, field: &'static str, value: f64) -> Property {
|
||||
let value = if value == 0.0 { 0.0 } else { value };
|
||||
Property {
|
||||
label: label.into(),
|
||||
field,
|
||||
value: PropValue::EditText(value.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ro_prop(label: &str, field: &'static str, value: impl Into<String>) -> Property {
|
||||
Property {
|
||||
label: label.into(),
|
||||
|
|
@ -538,8 +612,13 @@ pub fn stepper_prop(
|
|||
|
||||
pub fn parse_f64(value: &str) -> Option<f64> {
|
||||
let t = value.trim();
|
||||
// Angle rows display via AUNITS (#297) — accept those formats back.
|
||||
t.parse::<f64>().ok().or_else(|| parse_angle_deg(t))
|
||||
// Length rows display via LUNITS and angle rows via AUNITS. Accept both
|
||||
// representations back so a value shown by Properties can always be
|
||||
// committed unchanged.
|
||||
t.parse::<f64>()
|
||||
.ok()
|
||||
.or_else(|| parse_length(t))
|
||||
.or_else(|| parse_angle_deg(t))
|
||||
}
|
||||
|
||||
/// Parse an angle string the panel displayed via AUNITS back to DEGREES:
|
||||
|
|
@ -791,3 +870,124 @@ pub(crate) fn polyline_segment_fill(
|
|||
Some(boundary)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod length_format_tests {
|
||||
use super::*;
|
||||
|
||||
fn with_units(lunits: i16, luprec: i16, value: f64) -> String {
|
||||
let mut ctx = unit_context();
|
||||
ctx.lunits = lunits;
|
||||
ctx.luprec = luprec;
|
||||
set_unit_context(ctx);
|
||||
format_length(value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fractional_carries_a_rounded_up_fraction() {
|
||||
assert_eq!(with_units(5, 4, 5.99), "5 63/64");
|
||||
assert_eq!(with_units(5, 4, 5.995), "6");
|
||||
assert_eq!(with_units(5, 4, 0.995), "1");
|
||||
assert_eq!(with_units(5, 4, 11.999), "12");
|
||||
assert_eq!(with_units(5, 4, -5.995), "-6");
|
||||
assert_eq!(with_units(5, 4, 0.5), "0 1/2");
|
||||
assert_eq!(with_units(5, 4, 9.25), "9 1/4");
|
||||
assert_eq!(with_units(5, 4, 3.0e17), "300000000000000000");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn architectural_carries_into_feet() {
|
||||
assert_eq!(with_units(4, 4, 11.99), "0'-11 63/64\"");
|
||||
assert_eq!(with_units(4, 4, 11.999), "1'-0\"");
|
||||
assert_eq!(with_units(4, 4, 23.999), "2'-0\"");
|
||||
assert_eq!(with_units(4, 4, 66.5), "5'-6 1/2\"");
|
||||
assert_eq!(with_units(4, 4, -11.999), "-1'-0\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn engineering_carries_into_feet() {
|
||||
assert_eq!(with_units(3, 1, 11.94), "0'-11.9\"");
|
||||
assert_eq!(with_units(3, 1, 11.99), "1'-0.0\"");
|
||||
assert_eq!(with_units(3, 1, 23.99), "2'-0.0\"");
|
||||
assert_eq!(with_units(3, 2, 11.999), "1'-0.00\"");
|
||||
assert_eq!(with_units(3, 1, 66.5), "5'-6.5\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formatted_lengths_read_back() {
|
||||
for lunits in [3, 4, 5] {
|
||||
for value in [0.995f64, 5.995, 11.999, 23.999, 66.5, 9.25] {
|
||||
let shown = with_units(lunits, if lunits == 3 { 4 } else { 4 }, value);
|
||||
let read = parse_length(&shown)
|
||||
.unwrap_or_else(|| panic!("lunits {lunits} wrote {shown:?}, which does not read back"));
|
||||
assert!(
|
||||
(read - value).abs() < 0.02,
|
||||
"lunits {lunits}: {value} wrote {shown:?}, read back as {read}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod angle_format_tests {
|
||||
use super::*;
|
||||
|
||||
fn shown(aunits: i16, auprec: i16, degrees: f64) -> String {
|
||||
let mut ctx = unit_context();
|
||||
ctx.aunits = aunits;
|
||||
ctx.auprec = auprec;
|
||||
set_unit_context(ctx);
|
||||
format_angle(degrees.to_radians())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degrees_minutes_seconds_carry() {
|
||||
assert_eq!(shown(1, 0, 45.9999), "46d0'0\"");
|
||||
assert_eq!(shown(1, 0, 89.99999), "90d0'0\"");
|
||||
assert_eq!(shown(1, 0, 0.99999), "1d0'0\"");
|
||||
assert_eq!(shown(1, 0, 45.5), "45d30'0\"");
|
||||
assert_eq!(shown(1, 2, 45.9999), "45d59'59.64\"");
|
||||
assert_eq!(shown(1, 2, 45.5), "45d30'0.00\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn surveyor_bearings_stay_inside_a_quadrant() {
|
||||
assert_eq!(shown(4, 0, 359.99999), "E");
|
||||
assert_eq!(shown(4, 0, 89.99999), "N");
|
||||
assert_eq!(shown(4, 0, 45.0), "N 45d0'0\" E");
|
||||
assert_eq!(shown(4, 0, 180.0), "W");
|
||||
assert_ne!(shown(4, 8, 0.0000000005), "E");
|
||||
for prec in [0, 2] {
|
||||
for deg in [0.99999f64, 45.9999, 89.99999, 179.99999, 269.99999, 359.99999] {
|
||||
let text = shown(4, prec, deg);
|
||||
assert!(
|
||||
!text.contains("90d"),
|
||||
"prec {prec}: {deg} wrote {text:?}, a quarter turn inside a quadrant"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formatted_angles_read_back() {
|
||||
for aunits in [0, 1, 4] {
|
||||
for prec in [0, 2] {
|
||||
for deg in [0.99999f64, 45.5, 45.9999, 89.99999, 200.25, 359.99999] {
|
||||
let text = shown(aunits, prec, deg);
|
||||
let read = parse_angle(&text).unwrap_or_else(|| {
|
||||
panic!("aunits {aunits} prec {prec} wrote {text:?}, which does not read back")
|
||||
});
|
||||
let delta = (read.to_degrees().rem_euclid(360.0) - deg.rem_euclid(360.0))
|
||||
.rem_euclid(360.0);
|
||||
let delta = delta.min(360.0 - delta);
|
||||
assert!(
|
||||
delta < 1.0,
|
||||
"aunits {aunits} prec {prec}: {deg} wrote {text:?}, read back as {}",
|
||||
read.to_degrees()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ use acadrust::entities::{
|
|||
Arc as ArcEnt, Circle as CircleEnt, Ellipse as EllipseEnt, LwPolyline as LwPolylineEnt,
|
||||
Polyline2D, Spline as SplineEnt,
|
||||
};
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use cadkernel::geom2d::{
|
||||
characteristic_points, Arc, Circle, Curve, Ellipse, EllipseArc, Line, Polyline, PolylineVertex,
|
||||
Ray, SnapKind, Transform, XLine,
|
||||
};
|
||||
use cadkernel::space::{PlanarCurve, Plane, Vec3};
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use cadkernel::space::{are_coplanar, coplanarity_tolerance, PlanarCurve, Plane, Vec3};
|
||||
|
||||
use crate::modules::draw::modify::spline_ops::spline_to_nurbs_on;
|
||||
use crate::scene::model::wire_model::SnapHint;
|
||||
|
|
@ -287,27 +287,61 @@ pub fn polyline2d_curve(polyline: &Polyline2D) -> Option<PlanarCurve> {
|
|||
/// plane the normal describes. A spline that genuinely wanders in space gets
|
||||
/// `None`, which is honest: flattening it to XY would move it.
|
||||
pub fn spline_curve(spline: &SplineEnt) -> Option<PlanarCurve> {
|
||||
let points: Vec<Vector3> = spline
|
||||
.control_points
|
||||
.iter()
|
||||
.chain(spline.fit_points.iter())
|
||||
.copied()
|
||||
.collect();
|
||||
let fit_method = crate::entities::spline::uses_fit_method(spline);
|
||||
let source = if fit_method {
|
||||
&spline.fit_points
|
||||
} else {
|
||||
&spline.control_points
|
||||
};
|
||||
let points: Vec<Vector3> = source.to_vec();
|
||||
let first = points.first()?;
|
||||
if !spline_is_planar(spline) {
|
||||
return None;
|
||||
}
|
||||
let normal = normalized(spline.normal);
|
||||
let elevation = Vec3::from(xyz(*first)).dot(Vec3::from(xyz(normal)));
|
||||
let plane = ocs_plane(normal, elevation);
|
||||
|
||||
let tolerance = PLANARITY_TOLERANCE * scale_of(&points);
|
||||
let point_arrays: Vec<[f64; 3]> = points.iter().copied().map(xyz).collect();
|
||||
let tolerance = coplanarity_tolerance(&point_arrays);
|
||||
if !points.iter().all(|p| plane.contains(xyz(*p), tolerance)) {
|
||||
return None;
|
||||
}
|
||||
if fit_method && !spline.flags.periodic {
|
||||
let plane_normal = Vec3::from(plane.normal()?);
|
||||
for tangent in [spline.begin_tangent, spline.end_tangent] {
|
||||
let tangent = Vec3::from(xyz(tangent));
|
||||
if tangent.length_squared() > 1e-18
|
||||
&& tangent.dot(plane_normal).abs()
|
||||
> PLANARITY_TOLERANCE * tangent.length().max(1.0)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(PlanarCurve::new(
|
||||
plane,
|
||||
Curve::Nurbs(spline_to_nurbs_on(spline, &plane)?),
|
||||
))
|
||||
}
|
||||
|
||||
/// Whether the defining points and active tangents share a plane.
|
||||
pub fn spline_is_planar(spline: &SplineEnt) -> bool {
|
||||
let fit_method = crate::entities::spline::uses_fit_method(spline);
|
||||
let points = if fit_method {
|
||||
&spline.fit_points
|
||||
} else {
|
||||
&spline.control_points
|
||||
};
|
||||
let points: Vec<[f64; 3]> = points.iter().copied().map(xyz).collect();
|
||||
let directions: Vec<[f64; 3]> = if fit_method && !spline.flags.periodic {
|
||||
vec![xyz(spline.begin_tangent), xyz(spline.end_tangent)]
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
are_coplanar(&points, &directions)
|
||||
}
|
||||
|
||||
/// The entity's curve in world XY coordinates.
|
||||
///
|
||||
/// The editing commands — TRIM, EXTEND, FILLET, OFFSET — work in plan view,
|
||||
|
|
@ -352,6 +386,42 @@ pub fn entity_curve_xy(entity: &EntityType) -> Option<Curve> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Re-expresses an axis-aligned LWPOLYLINE in world XY coordinates.
|
||||
pub fn lwpolyline_world_xy(polyline: &LwPolylineEnt) -> Option<LwPolylineEnt> {
|
||||
let normal = normalized(polyline.normal);
|
||||
if normal.x.abs() > 1e-12 || normal.y.abs() > 1e-12 || normal.z.abs() <= 1e-12 {
|
||||
return None;
|
||||
}
|
||||
let Curve::Polyline(curve) = entity_curve_xy(&EntityType::LwPolyline(polyline.clone()))?
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
if curve.vertices.len() != polyline.vertices.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut world = polyline.clone();
|
||||
for (vertex, kernel) in world.vertices.iter_mut().zip(curve.vertices) {
|
||||
vertex.location.x = kernel.position[0];
|
||||
vertex.location.y = kernel.position[1];
|
||||
vertex.bulge = kernel.bulge;
|
||||
}
|
||||
world.elevation = ocs_plane(polyline.normal, polyline.elevation).origin[2];
|
||||
world.thickness *= normal.z;
|
||||
world.normal = Vector3::new(0.0, 0.0, 1.0);
|
||||
Some(world)
|
||||
}
|
||||
|
||||
/// Normalizes only LWPOLYLINE entities; other types pass through unchanged.
|
||||
pub fn entity_with_lwpolyline_world_xy(entity: &EntityType) -> EntityType {
|
||||
match entity {
|
||||
EntityType::LwPolyline(polyline) => lwpolyline_world_xy(polyline)
|
||||
.map(EntityType::LwPolyline)
|
||||
.unwrap_or_else(|| entity.clone()),
|
||||
_ => entity.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// World-space wire points sampled by the kernel's angular policy.
|
||||
pub fn curve_points(curve: &PlanarCurve) -> Vec<[f64; 3]> {
|
||||
curve.tessellate_angle(cadkernel::tessellation::DEFAULT_ANGLE)
|
||||
|
|
|
|||
|
|
@ -19,19 +19,19 @@ fn base_props(base: &DimensionBase) -> Vec<crate::scene::model::object::Property
|
|||
crate::scene::model::object::Property {
|
||||
label: t!("Text").into_owned(),
|
||||
field: "text",
|
||||
value: crate::scene::model::object::PropValue::EditText(base.text.clone()),
|
||||
value: crate::scene::model::object::PropValue::PlainText(base.text.clone()),
|
||||
},
|
||||
crate::scene::model::object::Property {
|
||||
label: t!("User Text").into_owned(),
|
||||
field: "user_text",
|
||||
value: crate::scene::model::object::PropValue::EditText(
|
||||
value: crate::scene::model::object::PropValue::PlainText(
|
||||
base.user_text.clone().unwrap_or_default(),
|
||||
),
|
||||
},
|
||||
crate::scene::model::object::Property {
|
||||
label: t!("Style").into_owned(),
|
||||
field: "style_name",
|
||||
value: crate::scene::model::object::PropValue::EditText(base.style_name.clone()),
|
||||
value: crate::scene::model::object::PropValue::PlainText(base.style_name.clone()),
|
||||
},
|
||||
edit(t!("Text X").as_ref(), "text_x", base.text_middle_point.x),
|
||||
edit(t!("Text Y").as_ref(), "text_y", base.text_middle_point.y),
|
||||
|
|
@ -2066,9 +2066,12 @@ fn tessellate_dimension_inner(
|
|||
let (ext1, ext2) = split_ext_lines(&geom.ext_lines);
|
||||
if !ext1.is_empty() {
|
||||
wires.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
plot_visible: true,
|
||||
fill_is_3d: false,
|
||||
fill_is_2d_solid: false,
|
||||
render_instance: None,
|
||||
|
|
@ -2097,9 +2100,12 @@ fn tessellate_dimension_inner(
|
|||
}
|
||||
if !ext2.is_empty() {
|
||||
wires.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
plot_visible: true,
|
||||
fill_is_3d: false,
|
||||
fill_is_2d_solid: false,
|
||||
render_instance: None,
|
||||
|
|
@ -2128,9 +2134,12 @@ fn tessellate_dimension_inner(
|
|||
}
|
||||
} else {
|
||||
wires.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
plot_visible: true,
|
||||
fill_is_3d: false,
|
||||
fill_is_2d_solid: false,
|
||||
render_instance: None,
|
||||
|
|
@ -2160,9 +2169,12 @@ fn tessellate_dimension_inner(
|
|||
}
|
||||
|
||||
wires.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
plot_visible: true,
|
||||
fill_is_3d: false,
|
||||
fill_is_2d_solid: false,
|
||||
render_instance: None,
|
||||
|
|
@ -2208,9 +2220,12 @@ fn tessellate_dimension_inner(
|
|||
aci_to_rgba(&c)
|
||||
};
|
||||
wires.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
plot_visible: true,
|
||||
fill_is_3d: false,
|
||||
fill_is_2d_solid: false,
|
||||
render_instance: None,
|
||||
|
|
@ -4129,4 +4144,3 @@ mod arch_format_tests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
use acadrust::entities::Ellipse;
|
||||
use cadkernel::geom2d::{
|
||||
Curve as KernelCurve, EllipseArc as KernelEllipseArc, Tolerance, Vec2 as KernelVec2,
|
||||
};
|
||||
use cadkernel::space::PlanarCurve;
|
||||
|
||||
use crate::entities::curve::CurveSnap;
|
||||
use crate::t;
|
||||
|
||||
use crate::command::EntityTransform;
|
||||
use crate::entities::common::{
|
||||
center_grip, edit_prop as edit, parse_f64, ro_prop as ro, square_grip,
|
||||
center_grip, edit_prop as edit, oriented_triangle_grip, parse_f64, ro_prop as ro,
|
||||
square_grip,
|
||||
};
|
||||
use crate::entities::traits::RenderConvertible;
|
||||
use crate::scene::convert::acad_to_render::{RenderEntity, RenderObject};
|
||||
|
|
@ -58,33 +63,56 @@ fn to_render(ell: &Ellipse) -> RenderEntity {
|
|||
}
|
||||
}
|
||||
|
||||
/// A curve through every one of `points` in order.
|
||||
///
|
||||
/// Degree one, so the control polygon *is* the curve — the same thing the
|
||||
/// hand-built sampling produced, without restating how a B-spline is put
|
||||
/// together at each site.
|
||||
fn ellipse_curves(ell: &Ellipse) -> Option<(PlanarCurve, PlanarCurve)> {
|
||||
let arc = crate::entities::curve::ellipse_curve(ell)?;
|
||||
let KernelCurve::Ellipse(piece) = &arc.curve else {
|
||||
return None;
|
||||
};
|
||||
let full = PlanarCurve::new(
|
||||
arc.plane,
|
||||
KernelCurve::Ellipse(KernelEllipseArc::full(piece.ellipse)),
|
||||
);
|
||||
Some((arc, full))
|
||||
}
|
||||
|
||||
fn is_closed(ell: &Ellipse) -> bool {
|
||||
crate::entities::curve::ellipse_curve(ell).is_some_and(|curve| curve.curve.is_closed())
|
||||
}
|
||||
|
||||
fn parameter_at_point(ell: &Ellipse, point: glam::DVec3) -> Option<f64> {
|
||||
let (_, full) = ellipse_curves(ell)?;
|
||||
Some(
|
||||
(full.parameter_at(point.to_array())? * std::f64::consts::TAU)
|
||||
.rem_euclid(std::f64::consts::TAU),
|
||||
)
|
||||
}
|
||||
|
||||
fn grips(ell: &Ellipse) -> Vec<GripDef> {
|
||||
let ctr = glam::DVec3::new(ell.center.x, ell.center.y, ell.center.z);
|
||||
let maj = glam::DVec3::new(
|
||||
ell.center.x + ell.major_axis.x,
|
||||
ell.center.y + ell.major_axis.y,
|
||||
ell.center.z + ell.major_axis.z,
|
||||
);
|
||||
let major_xy =
|
||||
((ell.major_axis.x * ell.major_axis.x + ell.major_axis.y * ell.major_axis.y) as f64).sqrt();
|
||||
let (px, py) = if major_xy > 1e-10 {
|
||||
let s = ell.major_axis_length() * ell.minor_axis_ratio / major_xy;
|
||||
(-ell.major_axis.y * s, ell.major_axis.x * s)
|
||||
} else {
|
||||
(0.0, ell.major_axis_length() * ell.minor_axis_ratio)
|
||||
let fallback = glam::DVec3::new(ell.center.x, ell.center.y, ell.center.z);
|
||||
let Some((arc, full)) = ellipse_curves(ell) else {
|
||||
return vec![center_grip(0, fallback)];
|
||||
};
|
||||
let min = glam::DVec3::new(ell.center.x + px, ell.center.y + py, ell.center.z);
|
||||
vec![
|
||||
let KernelCurve::Ellipse(piece) = &full.curve else {
|
||||
return vec![center_grip(0, fallback)];
|
||||
};
|
||||
let ctr = glam::DVec3::from_array(full.plane.point_at(piece.ellipse.centre));
|
||||
let axes = [0.0, 0.25, 0.5, 0.75].map(|t| glam::DVec3::from_array(full.point_at(t)));
|
||||
let mut grips = vec![
|
||||
center_grip(0, ctr),
|
||||
square_grip(1, maj),
|
||||
square_grip(2, min),
|
||||
]
|
||||
square_grip(1, axes[0]),
|
||||
square_grip(2, axes[1]),
|
||||
square_grip(3, axes[2]),
|
||||
square_grip(4, axes[3]),
|
||||
];
|
||||
if !arc.curve.is_closed() {
|
||||
let start = glam::DVec3::from_array(arc.point_at(0.0));
|
||||
let end = glam::DVec3::from_array(arc.point_at(1.0));
|
||||
let start_outward = -glam::DVec3::from_array(arc.tangent_at(0.0));
|
||||
let end_outward = glam::DVec3::from_array(arc.tangent_at(1.0));
|
||||
grips.push(oriented_triangle_grip(5, start, start_outward));
|
||||
grips.push(oriented_triangle_grip(6, end, end_outward));
|
||||
}
|
||||
grips
|
||||
}
|
||||
|
||||
fn properties(ell: &Ellipse) -> Vec<PropSection> {
|
||||
|
|
@ -114,9 +142,7 @@ fn properties(ell: &Ellipse) -> Vec<PropSection> {
|
|||
let minor_vec = v * r_minor;
|
||||
|
||||
let start_angle = ell.start_parameter.to_degrees().rem_euclid(360.0);
|
||||
let end_angle = if crate::entities::curve::ellipse_curve(ell)
|
||||
.is_some_and(|curve| curve.curve.is_closed())
|
||||
{
|
||||
let end_angle = if is_closed(ell) {
|
||||
360.0
|
||||
} else {
|
||||
ell.end_parameter.to_degrees().rem_euclid(360.0)
|
||||
|
|
@ -188,6 +214,73 @@ fn apply_geom_prop(ell: &mut Ellipse, field: &str, value: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_axis_grip(ell: &mut Ellipse, grip_id: usize, point: glam::DVec3) {
|
||||
let Some((_, full)) = ellipse_curves(ell) else {
|
||||
return;
|
||||
};
|
||||
let KernelCurve::Ellipse(piece) = &full.curve else {
|
||||
return;
|
||||
};
|
||||
let Some(projected) = full.plane.project(point.to_array()) else {
|
||||
return;
|
||||
};
|
||||
let center = KernelVec2::from(piece.ellipse.centre);
|
||||
let mut dragged = KernelVec2::from(projected) - center;
|
||||
let tolerance = Tolerance::default().linear();
|
||||
|
||||
if matches!(grip_id, 1 | 3) {
|
||||
if grip_id == 3 {
|
||||
dragged = -dragged;
|
||||
}
|
||||
if dragged.length() <= tolerance {
|
||||
return;
|
||||
}
|
||||
let axis = full.plane.vector_at(dragged.to_array());
|
||||
ell.major_axis.x = axis[0];
|
||||
ell.major_axis.y = axis[1];
|
||||
ell.major_axis.z = axis[2];
|
||||
return;
|
||||
}
|
||||
|
||||
if grip_id == 4 {
|
||||
dragged = -dragged;
|
||||
}
|
||||
let major_dir = KernelVec2::from(piece.ellipse.major_axis);
|
||||
let minor_dir = major_dir.perpendicular();
|
||||
let major_len = piece.ellipse.major_radius.abs();
|
||||
let minor_len = piece.ellipse.minor_radius.abs();
|
||||
if major_len <= tolerance || minor_len <= tolerance {
|
||||
return;
|
||||
}
|
||||
|
||||
let along_major = dragged.dot(major_dir).abs();
|
||||
let along_minor = dragged.dot(minor_dir).abs();
|
||||
let (drag_dir, dragged_len, fixed_dir, fixed_len) = if along_minor >= along_major {
|
||||
(minor_dir, along_minor, major_dir, major_len)
|
||||
} else {
|
||||
(major_dir, along_major, minor_dir, minor_len)
|
||||
};
|
||||
if fixed_len <= tolerance {
|
||||
return;
|
||||
}
|
||||
let (axis, ratio) = if dragged_len >= fixed_len {
|
||||
(
|
||||
drag_dir * dragged_len,
|
||||
(fixed_len / dragged_len).clamp(0.001, 1.0),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
fixed_dir * fixed_len,
|
||||
(dragged_len / fixed_len).clamp(0.001, 1.0),
|
||||
)
|
||||
};
|
||||
let axis = full.plane.vector_at(axis.to_array());
|
||||
ell.major_axis.x = axis[0];
|
||||
ell.major_axis.y = axis[1];
|
||||
ell.major_axis.z = axis[2];
|
||||
ell.minor_axis_ratio = ratio;
|
||||
}
|
||||
|
||||
fn apply_grip(ell: &mut Ellipse, grip_id: usize, apply: GripApply) {
|
||||
match (grip_id, apply) {
|
||||
(0, GripApply::Translate(d)) => {
|
||||
|
|
@ -200,54 +293,15 @@ fn apply_grip(ell: &mut Ellipse, grip_id: usize, apply: GripApply) {
|
|||
ell.center.y = p.y as f64;
|
||||
ell.center.z = p.z as f64;
|
||||
}
|
||||
(1, GripApply::Absolute(p)) => {
|
||||
ell.major_axis.x = p.x as f64 - ell.center.x;
|
||||
ell.major_axis.y = p.y as f64 - ell.center.y;
|
||||
ell.major_axis.z = p.z as f64 - ell.center.z;
|
||||
}
|
||||
(2, GripApply::Absolute(p)) => {
|
||||
// Minor-axis grip. The two axes are always perpendicular, so this
|
||||
// grip stretches one of them along its fixed direction while the
|
||||
// other stays put. Work in the ellipse's view plane (XY).
|
||||
let mx = ell.major_axis.x;
|
||||
let my = ell.major_axis.y;
|
||||
let major_len = (mx * mx + my * my).sqrt();
|
||||
if major_len <= 1e-10 {
|
||||
(1 | 2 | 3 | 4, GripApply::Absolute(p)) => apply_axis_grip(ell, grip_id, p),
|
||||
(5 | 6, GripApply::Absolute(p)) => {
|
||||
let Some(parameter) = parameter_at_point(ell, p) else {
|
||||
return;
|
||||
}
|
||||
let major_unit = (mx / major_len, my / major_len);
|
||||
let minor_unit = (-major_unit.1, major_unit.0);
|
||||
let minor_len = major_len * ell.minor_axis_ratio;
|
||||
|
||||
let dx = p.x as f64 - ell.center.x;
|
||||
let dy = p.y as f64 - ell.center.y;
|
||||
// Which stored axis is actually under the cursor? Below a circle it
|
||||
// is the minor direction; once the drag pushes the minor past the
|
||||
// major and the axes swap, the grabbed endpoint sits on the major
|
||||
// direction. Decide by cursor alignment — NOT by recomputing a perp
|
||||
// from the just-swapped major every frame — so a continuous drag
|
||||
// stays stable instead of flipping the major 90° each move (which
|
||||
// made both axes balloon once the minor reached the major). The
|
||||
// perpendicular (non-dragged) axis keeps its length; the longer axis
|
||||
// is stored as the major so the ratio stays <= 1 per the file format.
|
||||
let along_major = (dx * major_unit.0 + dy * major_unit.1).abs();
|
||||
let along_minor = (dx * minor_unit.0 + dy * minor_unit.1).abs();
|
||||
let (drag_dir, dragged_len, fixed_dir, fixed_len) = if along_minor >= along_major {
|
||||
(minor_unit, along_minor, major_unit, major_len)
|
||||
} else {
|
||||
(major_unit, along_major, minor_unit, minor_len)
|
||||
};
|
||||
if fixed_len <= 1e-10 {
|
||||
return;
|
||||
}
|
||||
if dragged_len >= fixed_len {
|
||||
ell.major_axis.x = drag_dir.0 * dragged_len;
|
||||
ell.major_axis.y = drag_dir.1 * dragged_len;
|
||||
ell.minor_axis_ratio = (fixed_len / dragged_len).clamp(0.001, 1.0);
|
||||
if grip_id == 5 {
|
||||
ell.start_parameter = parameter;
|
||||
} else {
|
||||
ell.major_axis.x = fixed_dir.0 * fixed_len;
|
||||
ell.major_axis.y = fixed_dir.1 * fixed_len;
|
||||
ell.minor_axis_ratio = (dragged_len / fixed_len).clamp(0.001, 1.0);
|
||||
ell.end_parameter = parameter;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ fn text_prop(label: &str, field: &'static str, value: &str) -> Property {
|
|||
Property {
|
||||
label: label.into(),
|
||||
field,
|
||||
value: PropValue::EditText(value.to_string()),
|
||||
value: PropValue::PlainText(value.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -301,6 +301,11 @@ fn geo_marker_lines(data: &GeoPositionMarkerData) -> Vec<[f64; 3]> {
|
|||
fn point_cloud_lines(data: &PointCloudData) -> Vec<[f64; 3]> {
|
||||
let mut points = Vec::new();
|
||||
push_box(&mut points, data.extents_min, data.extents_max);
|
||||
points
|
||||
}
|
||||
|
||||
fn point_cloud_clip_lines(data: &PointCloudData) -> Vec<[f64; 3]> {
|
||||
let mut points = Vec::new();
|
||||
if data.show_clipping {
|
||||
for clip in &data.clippings {
|
||||
if clip.vertices.len() < 2 {
|
||||
|
|
@ -323,6 +328,11 @@ fn point_cloud_lines(data: &PointCloudData) -> Vec<[f64; 3]> {
|
|||
fn point_cloud_ex_lines(data: &PointCloudExData) -> Vec<[f64; 3]> {
|
||||
let mut points = Vec::new();
|
||||
push_box(&mut points, data.extents_min, data.extents_max);
|
||||
points
|
||||
}
|
||||
|
||||
fn point_cloud_ex_clip_lines(data: &PointCloudExData) -> Vec<[f64; 3]> {
|
||||
let mut points = Vec::new();
|
||||
if data.show_cropping {
|
||||
for crop in &data.croppings {
|
||||
if crop.points.len() < 2 {
|
||||
|
|
@ -339,6 +349,14 @@ fn point_cloud_ex_lines(data: &PointCloudExData) -> Vec<[f64; 3]> {
|
|||
points
|
||||
}
|
||||
|
||||
pub(crate) fn point_cloud_frame_lines(entity: &ExtendedEntity) -> Option<Vec<[f64; 3]>> {
|
||||
match &entity.data {
|
||||
ExtendedEntityData::PointCloud(data) => Some(point_cloud_clip_lines(data)),
|
||||
ExtendedEntityData::PointCloudEx(data) => Some(point_cloud_ex_clip_lines(data)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn camera_lines(document: &acadrust::CadDocument, view_handle: Handle) -> Vec<[f64; 3]> {
|
||||
let Some(view) = document.views.iter().find(|view| view.handle == view_handle) else {
|
||||
return Vec::new();
|
||||
|
|
|
|||
|
|
@ -14,41 +14,40 @@ use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Pr
|
|||
use crate::scene::convert::tess_util::FallbackGeometry;
|
||||
use crate::scene::model::wire_model::SnapHint;
|
||||
|
||||
/// The area the hatch's boundary paths enclose.
|
||||
///
|
||||
/// Summed edge by edge through the kernel, which measures what each edge
|
||||
/// actually encloses rather than what a polygon through some of its points
|
||||
/// would. The version this replaced pushed an arc's *centre* into the ring
|
||||
/// and a spline's control points — neither of which is on the boundary — so
|
||||
/// the number it produced was not the area of anything.
|
||||
///
|
||||
/// Outer paths and their holes both contribute; the sign of a loop says
|
||||
/// which it is, so the magnitude of the sum is the region's own area.
|
||||
fn boundary_area(h: &Hatch) -> f64 {
|
||||
let mut area = 0.0;
|
||||
/// The area enclosed by the hatch boundary paths.
|
||||
pub(crate) fn boundary_area(h: &Hatch) -> f64 {
|
||||
let mut path_areas = Vec::new();
|
||||
let mut rings = Vec::new();
|
||||
for path in &h.paths {
|
||||
let mut path_area = 0.0;
|
||||
let mut ends: Vec<[f64; 2]> = Vec::new();
|
||||
for edge in &path.edges {
|
||||
let Some(curve) = edge_curve(edge) else {
|
||||
continue;
|
||||
};
|
||||
path_area += curve.enclosed_area();
|
||||
ends.push(curve.point_at(0.0));
|
||||
ends.push(curve.point_at(1.0));
|
||||
let directions = crate::scene::hatch_path_directions(path);
|
||||
let curves = path.edges.iter().filter_map(edge_curve);
|
||||
for (curve, direction) in curves.zip(directions) {
|
||||
path_area += direction * curve.enclosed_area();
|
||||
}
|
||||
// Edges are stored as separate pieces, so the chain has to be closed
|
||||
// by the chord from the last end back to the first — the same closing
|
||||
// an open polyline gets.
|
||||
if let (Some(first), Some(last)) = (ends.first(), ends.last()) {
|
||||
path_area += 0.5 * (last[0] * first[1] - first[0] * last[1]);
|
||||
}
|
||||
area += path_area.abs();
|
||||
path_areas.push(path_area.abs());
|
||||
rings.push(crate::scene::hatch_path_ring(path).unwrap_or_default());
|
||||
}
|
||||
area
|
||||
let depths = cadkernel::geom2d::ring_nesting_depths(&rings);
|
||||
path_areas
|
||||
.into_iter()
|
||||
.zip(depths)
|
||||
.filter_map(|(area, depth)| match h.style {
|
||||
acadrust::entities::HatchStyleType::Normal => {
|
||||
Some(if depth % 2 == 0 { area } else { -area })
|
||||
}
|
||||
acadrust::entities::HatchStyleType::Outer if depth <= 1 => {
|
||||
Some(if depth == 0 { area } else { -area })
|
||||
}
|
||||
acadrust::entities::HatchStyleType::Outer => None,
|
||||
acadrust::entities::HatchStyleType::Ignore if depth == 0 => Some(area),
|
||||
acadrust::entities::HatchStyleType::Ignore => None,
|
||||
})
|
||||
.sum::<f64>()
|
||||
.abs()
|
||||
}
|
||||
|
||||
/// A hatch boundary edge as a kernel curve, in the hatch's own OCS.
|
||||
/// A hatch boundary edge as a kernel curve in the hatch OCS.
|
||||
pub(crate) fn edge_curve(edge: &BoundaryEdge) -> Option<KernelCurve> {
|
||||
Some(match edge {
|
||||
BoundaryEdge::Line(l) => KernelCurve::Line(KernelLine {
|
||||
|
|
@ -323,6 +322,33 @@ pub fn clear_background_color(h: &mut Hatch) {
|
|||
}
|
||||
}
|
||||
|
||||
fn associative_property(h: &Hatch) -> Property {
|
||||
if h
|
||||
.paths
|
||||
.iter()
|
||||
.any(|path| !path.boundary_handles.is_empty())
|
||||
{
|
||||
Property {
|
||||
label: t!("Associative").into_owned(),
|
||||
field: "associative",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "associative",
|
||||
value: h.is_associative,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
ro(
|
||||
t!("Associative").as_ref(),
|
||||
"associative",
|
||||
if h.is_associative {
|
||||
t!("Yes").into_owned()
|
||||
} else {
|
||||
t!("No").into_owned()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn properties(h: &Hatch) -> Vec<PropSection> {
|
||||
let pattern_type = match h.pattern_type {
|
||||
acadrust::entities::HatchPatternType::Predefined => "Predefined",
|
||||
|
|
@ -345,59 +371,66 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
|
|||
if g.enabled {
|
||||
// ── Gradient fill ──────────────────────────────────────────────────
|
||||
let grad_type = if g.is_single_color { "One color" } else { "Two color" };
|
||||
let centered = if g.shift.abs() < 1e-9 { "Yes" } else { "No" };
|
||||
let mut sections = vec![
|
||||
let (kind, inverted) =
|
||||
crate::scene::model::hatch_model::GradientKind::from_name(&g.name);
|
||||
let mut pattern_props = vec![
|
||||
ro(t!("Type").as_ref(), "fill_kind", t!("Gradient").into_owned()),
|
||||
Property {
|
||||
label: t!("Color mode").into_owned(),
|
||||
field: "fill_type",
|
||||
value: PropValue::Choice {
|
||||
selected: grad_type.to_string(),
|
||||
options: vec!["One color".into(), "Two color".into()],
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: t!("Gradient type").into_owned(),
|
||||
field: "gradient_type",
|
||||
value: PropValue::Choice {
|
||||
selected: kind.choice_label(inverted).to_string(),
|
||||
options: crate::scene::model::hatch_model::GradientKind::CHOICES
|
||||
.iter()
|
||||
.map(|(kind, inverted)| kind.choice_label(*inverted).to_string())
|
||||
.collect(),
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: t!("Color 1").into_owned(),
|
||||
field: "gradient_color_1",
|
||||
value: PropValue::ColorChoice(grad_c1),
|
||||
},
|
||||
];
|
||||
if g.is_single_color {
|
||||
pattern_props.push(edit(
|
||||
t!("Tint/Shade").as_ref(),
|
||||
"gradient_tint",
|
||||
g.color_tint.clamp(0.0, 1.0),
|
||||
));
|
||||
} else {
|
||||
pattern_props.push(Property {
|
||||
label: t!("Color 2").into_owned(),
|
||||
field: "gradient_color_2",
|
||||
value: PropValue::ColorChoice(grad_c2),
|
||||
});
|
||||
}
|
||||
pattern_props.push(edit_angle(
|
||||
t!("Angle").as_ref(),
|
||||
"pattern_angle",
|
||||
g.angle.to_degrees(),
|
||||
));
|
||||
pattern_props.push(Property {
|
||||
label: t!("Centered").into_owned(),
|
||||
field: "gradient_centered",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "gradient_centered",
|
||||
value: g.shift < 0.5,
|
||||
},
|
||||
});
|
||||
|
||||
return vec![
|
||||
PropSection {
|
||||
title: t!("Pattern").into_owned(),
|
||||
props: vec![
|
||||
Property {
|
||||
label: t!("Type").into_owned(),
|
||||
field: "fill_type",
|
||||
value: PropValue::Choice {
|
||||
selected: grad_type.to_string(),
|
||||
options: vec!["Two color".into(), "One color".into()],
|
||||
},
|
||||
},
|
||||
{
|
||||
let (kind, invert) =
|
||||
crate::scene::model::hatch_model::GradientKind::from_name(&g.name);
|
||||
let _ = invert;
|
||||
Property {
|
||||
label: t!("Gradient type").into_owned(),
|
||||
field: "gradient_type",
|
||||
value: PropValue::Choice {
|
||||
selected: kind.label().to_string(),
|
||||
options: crate::scene::model::hatch_model::GradientKind::ALL
|
||||
.iter()
|
||||
.map(|k| k.label().to_string())
|
||||
.collect(),
|
||||
},
|
||||
}
|
||||
},
|
||||
Property {
|
||||
label: t!("Invert").into_owned(),
|
||||
field: "gradient_invert",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "gradient_invert",
|
||||
value: crate::scene::model::hatch_model::GradientKind::from_name(
|
||||
&g.name,
|
||||
)
|
||||
.1,
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: t!("Color 1").into_owned(),
|
||||
field: "gradient_color_1",
|
||||
value: PropValue::ColorChoice(grad_c1),
|
||||
},
|
||||
Property {
|
||||
label: t!("Color 2").into_owned(),
|
||||
field: "gradient_color_2",
|
||||
value: PropValue::ColorChoice(grad_c2),
|
||||
},
|
||||
edit_angle(t!("Angle").as_ref(), "pattern_angle", g.angle.to_degrees()),
|
||||
ro(t!("Centered").as_ref(), "gradient_centered", centered),
|
||||
],
|
||||
props: pattern_props,
|
||||
},
|
||||
PropSection {
|
||||
title: t!("Geometry").into_owned(),
|
||||
|
|
@ -410,59 +443,42 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
|
|||
PropSection {
|
||||
title: t!("Misc").into_owned(),
|
||||
props: vec![
|
||||
ro(t!("Associative").as_ref(),
|
||||
"associative",
|
||||
if h.is_associative { "Yes" } else { "No" },
|
||||
),
|
||||
associative_property(h),
|
||||
ro(t!("Annotative").as_ref(), "annotative", String::new()),
|
||||
ro(t!("Island detection style").as_ref(), "style", style),
|
||||
Property {
|
||||
label: t!("Background").into_owned(),
|
||||
field: "bg_enabled",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "bg_enabled",
|
||||
value: bg_on,
|
||||
label: t!("Island detection style").into_owned(),
|
||||
field: "style",
|
||||
value: PropValue::Choice {
|
||||
selected: style.to_string(),
|
||||
options: vec!["Normal".into(), "Outer".into(), "Ignore".into()],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
if bg_on {
|
||||
if let Some(sec) = sections.last_mut() {
|
||||
sec.props.push(Property {
|
||||
label: t!("Background color").into_owned(),
|
||||
field: "background_color",
|
||||
value: PropValue::ColorChoice(bg_col),
|
||||
});
|
||||
}
|
||||
}
|
||||
return sections;
|
||||
}
|
||||
|
||||
|
||||
// ── Hatch (pattern / solid) ────────────────────────────────────────────
|
||||
// "Type" = pattern definition source (Predefined / User Defined / Custom).
|
||||
let type_row = Property {
|
||||
label: t!("Type").into_owned(),
|
||||
field: "pattern_type_label",
|
||||
value: PropValue::Choice {
|
||||
selected: pattern_type.to_string(),
|
||||
options: vec!["Predefined".into(), "User Defined".into(), "Custom".into()],
|
||||
},
|
||||
// Show only controls used by the selected fill type.
|
||||
let type_row = if h.is_solid {
|
||||
ro(t!("Type").as_ref(), "fill_kind", t!("Solid").into_owned())
|
||||
} else {
|
||||
Property {
|
||||
label: t!("Type").into_owned(),
|
||||
field: "pattern_type_label",
|
||||
value: PropValue::Choice {
|
||||
selected: pattern_type.to_string(),
|
||||
options: vec!["Predefined".into(), "User Defined".into(), "Custom".into()],
|
||||
},
|
||||
}
|
||||
};
|
||||
let pattern_name_row = Property {
|
||||
label: t!("Pattern name").into_owned(),
|
||||
field: "pattern_name",
|
||||
value: PropValue::HatchPatternChoice(h.pattern.name.clone()),
|
||||
};
|
||||
let associative_row = Property {
|
||||
label: t!("Associative").into_owned(),
|
||||
field: "associative",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "associative",
|
||||
value: h.is_associative,
|
||||
},
|
||||
};
|
||||
let associative_row = associative_property(h);
|
||||
let double_row = Property {
|
||||
label: t!("Double").into_owned(),
|
||||
field: "double",
|
||||
|
|
@ -479,47 +495,51 @@ fn properties(h: &Hatch) -> Vec<PropSection> {
|
|||
options: vec!["Normal".into(), "Outer".into(), "Ignore".into()],
|
||||
},
|
||||
};
|
||||
let spacing_row = edit(t!("Spacing").as_ref(),
|
||||
"spacing",
|
||||
h.pattern
|
||||
.lines
|
||||
.first()
|
||||
.map(|l| l.offset.length())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
// Pattern tiling origin: the base point the pattern lines are anchored to.
|
||||
let (origin_x, origin_y) = h
|
||||
.pattern
|
||||
.lines
|
||||
.first()
|
||||
.map(|l| (l.base_point.x, l.base_point.y))
|
||||
.unwrap_or((0.0, 0.0));
|
||||
let spacing_row = edit(t!("Spacing").as_ref(), "spacing", h.pattern_scale);
|
||||
|
||||
let mut pattern_props = vec![type_row, pattern_name_row];
|
||||
pattern_props.push(ro(t!("Annotative").as_ref(), "annotative", String::new()));
|
||||
if !h.is_solid {
|
||||
pattern_props.push(edit_angle(
|
||||
t!("Angle").as_ref(),
|
||||
"pattern_angle",
|
||||
h.pattern_angle.to_degrees(),
|
||||
));
|
||||
if matches!(
|
||||
h.pattern_type,
|
||||
acadrust::entities::HatchPatternType::UserDefined
|
||||
) {
|
||||
pattern_props.push(spacing_row);
|
||||
pattern_props.push(double_row);
|
||||
} else {
|
||||
pattern_props.push(edit(t!("Scale").as_ref(), "pattern_scale", h.pattern_scale));
|
||||
// Origin edits are relative offsets.
|
||||
pattern_props.push(edit(t!("Origin X").as_ref(), "origin_x", 0.0));
|
||||
pattern_props.push(edit(t!("Origin Y").as_ref(), "origin_y", 0.0));
|
||||
if h.pattern.name.to_ascii_uppercase().starts_with("ISO") {
|
||||
pattern_props.push(edit(
|
||||
t!("ISO pen width").as_ref(),
|
||||
"iso_pen_width",
|
||||
h.pattern_scale,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
pattern_props.push(associative_row);
|
||||
pattern_props.push(island_row);
|
||||
pattern_props.push(Property {
|
||||
label: t!("Background").into_owned(),
|
||||
field: "bg_enabled",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "bg_enabled",
|
||||
value: bg_on,
|
||||
},
|
||||
});
|
||||
|
||||
let mut sections = vec![
|
||||
PropSection {
|
||||
title: t!("Pattern").into_owned(),
|
||||
props: vec![
|
||||
type_row,
|
||||
pattern_name_row,
|
||||
ro(t!("Annotative").as_ref(), "annotative", String::new()),
|
||||
edit_angle(t!("Angle").as_ref(), "pattern_angle", h.pattern_angle.to_degrees()),
|
||||
edit(t!("Scale").as_ref(), "pattern_scale", h.pattern_scale),
|
||||
edit(t!("Origin X").as_ref(), "origin_x", origin_x),
|
||||
edit(t!("Origin Y").as_ref(), "origin_y", origin_y),
|
||||
spacing_row,
|
||||
ro(t!("ISO pen width").as_ref(), "iso_pen_width", String::new()),
|
||||
double_row,
|
||||
associative_row,
|
||||
island_row,
|
||||
Property {
|
||||
label: t!("Background").into_owned(),
|
||||
field: "bg_enabled",
|
||||
value: PropValue::BoolToggle {
|
||||
field: "bg_enabled",
|
||||
value: bg_on,
|
||||
},
|
||||
},
|
||||
],
|
||||
props: pattern_props,
|
||||
},
|
||||
PropSection {
|
||||
title: t!("Geometry").into_owned(),
|
||||
|
|
@ -567,20 +587,63 @@ fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
|
|||
return;
|
||||
}
|
||||
"associative" => {
|
||||
h.is_associative = if value == "toggle" {
|
||||
let requested = if value == "toggle" {
|
||||
!h.is_associative
|
||||
} else {
|
||||
value == "true"
|
||||
};
|
||||
// An associative flag without source handles is inert and cannot
|
||||
// update from boundary edits. Only enable it when a real
|
||||
// relationship is available; disabling retains the handles so the
|
||||
// user can turn it back on later.
|
||||
if !requested
|
||||
|| h.paths
|
||||
.iter()
|
||||
.any(|path| !path.boundary_handles.is_empty())
|
||||
{
|
||||
h.is_associative = requested;
|
||||
}
|
||||
return;
|
||||
}
|
||||
"pattern_type_label" => {
|
||||
h.pattern_type = match value {
|
||||
let requested = match value {
|
||||
"Predefined" => HatchPatternType::Predefined,
|
||||
"User Defined" => HatchPatternType::UserDefined,
|
||||
"Custom" => HatchPatternType::Custom,
|
||||
_ => h.pattern_type,
|
||||
};
|
||||
if requested != h.pattern_type {
|
||||
let old_origin = h.pattern.lines.first().map(|line| line.base_point);
|
||||
h.pattern_type = requested;
|
||||
h.is_solid = false;
|
||||
match requested {
|
||||
HatchPatternType::UserDefined => {
|
||||
// Rebuild user-defined geometry from its parameters.
|
||||
h.pattern = acadrust::entities::HatchPattern::new("_USER");
|
||||
}
|
||||
HatchPatternType::Predefined => {
|
||||
if let Some(entry) =
|
||||
crate::scene::model::hatch_patterns::find("ANSI31")
|
||||
{
|
||||
let mut pattern =
|
||||
crate::scene::model::hatch_patterns::build_dxf_pattern(entry);
|
||||
scale_pattern_geometry(&mut pattern, h.pattern_scale);
|
||||
rotate_pattern_geometry(&mut pattern, h.pattern_angle);
|
||||
if let (Some(old), Some(new)) =
|
||||
(old_origin, pattern.lines.first().map(|line| line.base_point))
|
||||
{
|
||||
translate_pattern_geometry(
|
||||
&mut pattern,
|
||||
old.x - new.x,
|
||||
old.y - new.y,
|
||||
);
|
||||
}
|
||||
h.pattern = pattern;
|
||||
}
|
||||
}
|
||||
HatchPatternType::Custom => {}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
"style" => {
|
||||
|
|
@ -593,31 +656,27 @@ fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
|
|||
return;
|
||||
}
|
||||
"fill_type" => {
|
||||
h.gradient_color.is_single_color = value == "One color";
|
||||
let one_color = value == "One color";
|
||||
if one_color && !h.gradient_color.is_single_color {
|
||||
h.gradient_color.color_tint = 1.0;
|
||||
}
|
||||
h.gradient_color.is_single_color = one_color;
|
||||
return;
|
||||
}
|
||||
// Gradient shape selection + stop inversion (#415). Both re-derive the
|
||||
// standard DXF gradient name; Linear has no INV name, so inverting a
|
||||
// linear swaps the colour stops instead.
|
||||
"gradient_type" => {
|
||||
use crate::scene::model::hatch_model::GradientKind;
|
||||
let (_, invert) = GradientKind::from_name(&h.gradient_color.name);
|
||||
if let Some(kind) = GradientKind::from_label(value) {
|
||||
if let Some((kind, invert)) = GradientKind::from_choice_label(value) {
|
||||
h.gradient_color.name = kind.dxf_name(invert).to_string();
|
||||
}
|
||||
return;
|
||||
}
|
||||
"gradient_invert" => {
|
||||
use crate::scene::model::hatch_model::GradientKind;
|
||||
let (kind, invert) = GradientKind::from_name(&h.gradient_color.name);
|
||||
let invert = !invert;
|
||||
if matches!(kind, GradientKind::Linear) {
|
||||
if h.gradient_color.colors.len() >= 2 {
|
||||
h.gradient_color.colors.swap(0, 1);
|
||||
}
|
||||
"gradient_centered" => {
|
||||
let centered = if value == "toggle" {
|
||||
h.gradient_color.shift >= 0.5
|
||||
} else {
|
||||
h.gradient_color.name = kind.dxf_name(invert).to_string();
|
||||
}
|
||||
value == "true"
|
||||
};
|
||||
h.gradient_color.shift = if centered { 0.0 } else { 1.0 };
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -630,6 +689,9 @@ fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
|
|||
h.gradient_color.angle = v.to_radians();
|
||||
h.pattern_angle = v.to_radians();
|
||||
}
|
||||
"gradient_tint" if h.gradient_color.enabled => {
|
||||
h.gradient_color.color_tint = v.clamp(0.0, 1.0);
|
||||
}
|
||||
"pattern_angle" => {
|
||||
let angle = v.to_radians();
|
||||
let delta = angle - h.pattern_angle;
|
||||
|
|
@ -651,38 +713,33 @@ fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
|
|||
// Scale every pattern line's offset so the first line's spacing = v,
|
||||
// preserving the relative spacing between lines.
|
||||
"spacing" if v > 0.0 => {
|
||||
let cur = h
|
||||
.pattern
|
||||
.lines
|
||||
.first()
|
||||
.map(|l| l.offset.length())
|
||||
.unwrap_or(0.0);
|
||||
if cur > 1e-9 {
|
||||
let s = v / cur;
|
||||
for line in h.pattern.lines.iter_mut() {
|
||||
line.offset.x *= s;
|
||||
line.offset.y *= s;
|
||||
}
|
||||
h.pattern_scale = v;
|
||||
if matches!(
|
||||
h.pattern_type,
|
||||
acadrust::entities::HatchPatternType::UserDefined
|
||||
) {
|
||||
h.pattern.lines.clear();
|
||||
h.pattern.name = "_USER".to_string();
|
||||
}
|
||||
}
|
||||
// Move the pattern origin: shift every line's base point by the delta
|
||||
// from the current origin (first line), preserving their relative offsets.
|
||||
// Origin rows are relative offsets and return to zero after commit.
|
||||
"origin_x" => {
|
||||
if let Some(cur) = h.pattern.lines.first().map(|l| l.base_point.x) {
|
||||
let d = v - cur;
|
||||
for line in h.pattern.lines.iter_mut() {
|
||||
line.base_point.x += d;
|
||||
}
|
||||
for line in h.pattern.lines.iter_mut() {
|
||||
line.base_point.x += v;
|
||||
}
|
||||
}
|
||||
"origin_y" => {
|
||||
if let Some(cur) = h.pattern.lines.first().map(|l| l.base_point.y) {
|
||||
let d = v - cur;
|
||||
for line in h.pattern.lines.iter_mut() {
|
||||
line.base_point.y += d;
|
||||
}
|
||||
for line in h.pattern.lines.iter_mut() {
|
||||
line.base_point.y += v;
|
||||
}
|
||||
}
|
||||
"iso_pen_width" if v > 0.0 => {
|
||||
let old = h.pattern_scale;
|
||||
if old > 1e-12 {
|
||||
scale_pattern_geometry(&mut h.pattern, v / old);
|
||||
}
|
||||
h.pattern_scale = v;
|
||||
}
|
||||
"elevation" => h.elevation = v,
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -690,13 +747,7 @@ fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) {
|
|||
|
||||
fn apply_transform(h: &mut Hatch, t: &EntityTransform) {
|
||||
crate::scene::view::transform::apply_standard_entity_transform(h, t, |entity, p1, p2| {
|
||||
// Delegate the mirror to acadrust's transform_hatch (via the Entity
|
||||
// trait): it flips the boundary-arc direction flags, re-mirrors the
|
||||
// stored angles and preserves the stored sweep — including the
|
||||
// wrap-encoded end angles above 2π that AutoCAD writes. The old
|
||||
// hand-rolled angle-swap here was only valid for ccw boundary arcs on
|
||||
// an axis-aligned mirror line and went stale the moment those
|
||||
// conventions were fixed upstream.
|
||||
// Keep boundary directions, angles, and sweeps consistent.
|
||||
let t = crate::scene::view::transform::reflection_about_xy_line(p1, p2);
|
||||
acadrust::entities::Entity::apply_transform(entity, &t);
|
||||
});
|
||||
|
|
@ -740,6 +791,15 @@ impl Grippable for Hatch {
|
|||
boundary_centroid(self).unwrap_or((l0.base_point.x, l0.base_point.y));
|
||||
out.push(circle_grip(id, glam::DVec3::new(gx, gy, elev)));
|
||||
id += 1;
|
||||
} else if self.is_associative {
|
||||
if let Some((gx, gy)) = boundary_centroid(self) {
|
||||
out.push(circle_grip(id, glam::DVec3::new(gx, gy, elev)));
|
||||
id += 1;
|
||||
}
|
||||
}
|
||||
// Edit associative boundaries through their source objects.
|
||||
if self.is_associative {
|
||||
return out;
|
||||
}
|
||||
for path in &self.paths {
|
||||
for edge in &path.edges {
|
||||
|
|
@ -811,8 +871,11 @@ impl Grippable for Hatch {
|
|||
.map(|l| (l.base_point.x, l.base_point.y))
|
||||
{
|
||||
if grip_id == id {
|
||||
let (nx, ny) = resolve(&apply, Vec3::new(ox as f32, oy as f32, elev));
|
||||
let (dx, dy) = (nx - ox, ny - oy);
|
||||
let (gx, gy) = boundary_centroid(self).unwrap_or((ox, oy));
|
||||
let (dx, dy) = match apply {
|
||||
GripApply::Absolute(point) => (point.x - gx, point.y - gy),
|
||||
GripApply::Translate(delta) => (delta.x, delta.y),
|
||||
};
|
||||
for line in self.pattern.lines.iter_mut() {
|
||||
line.base_point.x += dx;
|
||||
line.base_point.y += dy;
|
||||
|
|
@ -911,8 +974,14 @@ impl Grippable for Hatch {
|
|||
}
|
||||
}
|
||||
|
||||
fn grip_menu(&self, _grip_id: usize) -> Vec<crate::scene::model::object::GripMenuItem> {
|
||||
fn grip_menu(&self, grip_id: usize) -> Vec<crate::scene::model::object::GripMenuItem> {
|
||||
use crate::scene::model::object::{GripMenuAction, GripMenuItem};
|
||||
if self.pattern.lines.is_empty() || grip_id != 0 {
|
||||
return vec![GripMenuItem {
|
||||
label: "Stretch",
|
||||
action: GripMenuAction::Stretch,
|
||||
}];
|
||||
}
|
||||
vec![
|
||||
GripMenuItem {
|
||||
label: "Stretch",
|
||||
|
|
@ -933,9 +1002,16 @@ impl Grippable for Hatch {
|
|||
]
|
||||
}
|
||||
|
||||
fn apply_grip_menu(&mut self, _grip_id: usize, _action: crate::scene::model::object::GripMenuAction) {
|
||||
// Origin / Angle / Scale need a follow-up value — handled by
|
||||
// `apply_grip_menu_value`.
|
||||
fn apply_grip_menu(&mut self, grip_id: usize, action: crate::scene::model::object::GripMenuAction) {
|
||||
use crate::scene::model::object::GripMenuAction as A;
|
||||
if grip_id == 0 && matches!(action, A::OriginPoint) {
|
||||
if let (Some((gx, gy)), Some((ox, oy))) = (
|
||||
boundary_centroid(self),
|
||||
self.pattern.lines.first().map(|line| (line.base_point.x, line.base_point.y)),
|
||||
) {
|
||||
translate_pattern_geometry(&mut self.pattern, gx - ox, gy - oy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn grip_menu_value_prompt(
|
||||
|
|
|
|||