diff --git a/.cargo/config.toml b/.cargo/config.toml index 1138309ea..56bb92897 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,4 +1,4 @@ -# getrandom 0.3 (pulled by ahash via acadrust) needs this cfg to select its +# getrandom 0.3 (pulled by ahash via opencadcodec) needs this cfg to select its # browser backend on wasm32-unknown-unknown, alongside its `wasm_js` feature # (enabled in Cargo.toml for the wasm target). Without it the web build fails # with "wasm32-unknown-unknown targets are not supported by default". diff --git a/Cargo.lock b/Cargo.lock index 77819d2d0..e1519e3c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,14 +6,11 @@ version = 4 name = "OpenCADStudio" version = "2026.38.0" dependencies = [ - "acadrust", "ashpd", "base64", "bincode", "block2 0.5.1", "bytemuck", - "cadkernel", - "cadkernel-constraints", "calamine", "clap", "console_error_panic_hook", @@ -47,6 +44,10 @@ dependencies = [ "objc2-foundation 0.2.2", "ocs_plugin_api", "open", + "opencadcodec", + "opencadgraph", + "opencadkernel", + "opencadkernel-constraints", "pollster", "printpdf", "rayon", @@ -85,29 +86,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" -[[package]] -name = "acadrust" -version = "0.5.5" -source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=1c3be0c#1c3be0ca296c99f2f126ba1a03c2be1998d7c64e" -dependencies = [ - "ahash 0.8.12", - "anyhow", - "bitflags 2.13.1", - "byteorder", - "encoding_rs", - "flate2", - "indexmap", - "itoa", - "memmap2", - "nom 7.1.3", - "once_cell", - "rayon", - "ryu", - "serde", - "thiserror 1.0.69", - "web-time", -] - [[package]] name = "adler2" version = "2.0.1" @@ -921,25 +899,6 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" -[[package]] -name = "cadkernel" -version = "0.1.0" -source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=deb529b#deb529b34e677c1fedc4e316ce3f49b88f0c1361" -dependencies = [ - "acadrust", - "cavalier_contours", - "rustc-hash 2.1.3", - "spade", -] - -[[package]] -name = "cadkernel-constraints" -version = "0.1.0" -source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=deb529b#deb529b34e677c1fedc4e316ce3f49b88f0c1361" -dependencies = [ - "nalgebra", -] - [[package]] name = "calamine" version = "0.36.1" @@ -4188,13 +4147,13 @@ dependencies = [ name = "ocs_plugin_api" version = "0.2.1" dependencies = [ - "acadrust", "bincode", "cargo-lock", "getrandom 0.2.17", "interprocess", "libloading", "memmap2", + "opencadcodec", "rkyv", "serde", "serde-reflection", @@ -4207,11 +4166,11 @@ dependencies = [ name = "ocs_web_worker" version = "0.1.0" dependencies = [ - "acadrust", "bincode", "console_error_panic_hook", "getrandom 0.3.4", "js-sys", + "opencadcodec", "serde", "sha2 0.10.9", "wasm-bindgen", @@ -4239,6 +4198,57 @@ dependencies = [ "libc", ] +[[package]] +name = "opencadcodec" +version = "0.5.5" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=43a82f9#43a82f9b09ab5250fbe6a03e49c1d5a73a4152f3" +dependencies = [ + "ahash 0.8.12", + "anyhow", + "bitflags 2.13.1", + "byteorder", + "encoding_rs", + "flate2", + "indexmap", + "itoa", + "memmap2", + "nom 7.1.3", + "once_cell", + "rayon", + "ryu", + "serde", + "thiserror 1.0.69", + "web-time", +] + +[[package]] +name = "opencadgraph" +version = "0.1.0" +source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=0d84557#0d84557a5e8b9320272927b8a39433e3338e0473" +dependencies = [ + "opencadkernel", + "serde_json", +] + +[[package]] +name = "opencadkernel" +version = "0.1.0" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=5e24ef7#5e24ef7360fbb676c795e32f020114b3d36d3451" +dependencies = [ + "cavalier_contours", + "opencadcodec", + "rustc-hash 2.1.3", + "spade", +] + +[[package]] +name = "opencadkernel-constraints" +version = "0.1.0" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=5e24ef7#5e24ef7360fbb676c795e32f020114b3d36d3451" +dependencies = [ + "nalgebra", +] + [[package]] name = "openssl-probe" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index b4d831d24..3d0ae3730 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,9 +33,10 @@ rfd = "0.17" clap = { version = "4", features = ["derive", "string"] } env_logger = "0.11" log = "0.4" -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "1c3be0c", features = ["serde"] } -cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "deb529b", features = ["acis", "offset"] } -cadkernel-constraints = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "deb529b" } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "43a82f9", features = ["serde"] } +kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "5e24ef7", features = ["acis", "offset"] } +kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "5e24ef7" } +graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "0d84557" } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } flate2 = "1" image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] } diff --git a/assets/icons/ui/node_graph.svg b/assets/icons/ui/node_graph.svg new file mode 100644 index 000000000..70175913e --- /dev/null +++ b/assets/icons/ui/node_graph.svg @@ -0,0 +1 @@ + diff --git a/benches/performance_benchmarks.rs b/benches/performance_benchmarks.rs index 81b96069f..aca20f9b0 100644 --- a/benches/performance_benchmarks.rs +++ b/benches/performance_benchmarks.rs @@ -44,9 +44,9 @@ use std::time::Instant; use serde::{Deserialize, Serialize}; -use acadrust::entities::{Arc as AcadArc, Circle, Ellipse, Line, LwPolyline, LwVertex}; -use acadrust::types::{Vector2, Vector3}; -use acadrust::{CadDocument, DxfReader, DxfWriter, EntityType}; +use codec::entities::{Arc as AcadArc, Circle, Ellipse, Line, LwPolyline, LwVertex}; +use codec::types::{Vector2, Vector3}; +use codec::{CadDocument, DxfReader, DxfWriter, EntityType}; use glam::{DVec3, Mat4, Vec3}; use iced::{Color, Point, Rectangle}; @@ -1375,9 +1375,9 @@ fn bench_ui_plotstyle_layer_usage(runner: &mut BenchmarkRunner) { let mut doc = CadDocument::new(); for i in 0..200 { - let mut layer = acadrust::tables::Layer::new(&format!("LAYER_{i:03}")); + let mut layer = codec::tables::Layer::new(&format!("LAYER_{i:03}")); layer.handle = doc.allocate_handle(); - layer.color = acadrust::types::Color::Index((i % 8 + 1) as u8); + layer.color = codec::types::Color::Index((i % 8 + 1) as u8); let _ = doc.layers.add(layer); } @@ -1531,8 +1531,8 @@ fn bench_ui_grip_budget(runner: &mut BenchmarkRunner) { axis: None, }); } - let all_handles: Vec = (0..all_grips.len() as u64) - .map(|k| acadrust::Handle::new(k + 1)) + let all_handles: Vec = (0..all_grips.len() as u64) + .map(|k| codec::Handle::new(k + 1)) .collect(); let n_fixture_grips = all_grips.len(); @@ -2213,10 +2213,10 @@ fn bench_view_render_viewport_construction(runner: &mut BenchmarkRunner) { // Populate scene with non-graphical document objects for i in 0..obj_count { - let handle = acadrust::Handle::new(0x2000 + i as u64); + let handle = codec::Handle::new(0x2000 + i as u64); scene.document.objects.insert( handle, - acadrust::objects::ObjectType::Dictionary(acadrust::objects::Dictionary::default()), + codec::objects::ObjectType::Dictionary(codec::objects::Dictionary::default()), ); } // Add lines to model space @@ -2244,7 +2244,7 @@ fn bench_view_render_viewport_construction(runner: &mut BenchmarkRunner) { let t0 = Instant::now(); let primitive = scene.build_viewports( bounds, - acadrust::entities::ViewportRenderMode::Wireframe2D, + codec::entities::ViewportRenderMode::Wireframe2D, None, false, false, @@ -2268,7 +2268,7 @@ fn bench_view_render_viewport_construction(runner: &mut BenchmarkRunner) { // 2. Cached / After: Memoized document render environment + O(1) fast paths let _ = scene.build_viewports( bounds, - acadrust::entities::ViewportRenderMode::Wireframe2D, + codec::entities::ViewportRenderMode::Wireframe2D, None, false, false, @@ -2280,7 +2280,7 @@ fn bench_view_render_viewport_construction(runner: &mut BenchmarkRunner) { let t0 = Instant::now(); let primitive = scene.build_viewports( bounds, - acadrust::entities::ViewportRenderMode::Wireframe2D, + codec::entities::ViewportRenderMode::Wireframe2D, None, false, false, diff --git a/crates/ocs_plugin_api/ARCHITECTURE.md b/crates/ocs_plugin_api/ARCHITECTURE.md index a964cd793..c7e71939b 100644 --- a/crates/ocs_plugin_api/ARCHITECTURE.md +++ b/crates/ocs_plugin_api/ARCHITECTURE.md @@ -11,7 +11,7 @@ Internal-facing architecture documentation for `crates/ocs_plugin_api`. For plug | Tier | Feature | Dependencies | Purpose | |------|---------|--------------|---------| | **Core** | default | none | `PluginManifest`, `ApiVersion`, ribbon vocabulary, type registry, version info. Engine crates and external tooling can depend on this cheaply. | -| **Runtime host surface** | `host` | `acadrust`, `interprocess`, `memmap2`, `rkyv`, etc. | `HostApi`, `BuiltinPlugin`, out-of-process spawn, IPC, shared-memory snapshots, runner. | +| **Runtime host surface** | `host` | `opencadcodec`, `interprocess`, `memmap2`, `rkyv`, etc. | `HostApi`, `BuiltinPlugin`, out-of-process spawn, IPC, shared-memory snapshots, runner. | The host and each plugin run in separate OS processes. The host re-executes itself in `--ocs-plugin-runner` mode, the runner dynamically loads the plugin `cdylib`, and the two sides talk over a local socket. Process isolation keeps plugin crashes from affecting the host or other plugins. @@ -38,7 +38,7 @@ The host and each plugin run in separate OS processes. The host re-executes itse | [`src/shm.rs`](src/shm.rs) | Shared-memory document snapshot layout, `DocumentSnapshotStore`, `SharedDocumentReader`, V3/V4 view data. | | [`src/host_v4.rs`](src/host_v4.rs) | Host-side per-tab V4 snapshot manager. | | [`src/type_registry.rs`](src/type_registry.rs) / [`src/type_registry_types.rs`](src/type_registry_types.rs) | Build-time `serde-reflection` registry, embedded JSON, schema types. | -| [`src/version_info.rs`](src/version_info.rs) | Embedded version metadata and acadrust source-compatibility helper. | +| [`src/version_info.rs`](src/version_info.rs) | Embedded version metadata and opencadcodec source-compatibility helper. | --- @@ -48,7 +48,7 @@ The host and each plugin run in separate OS processes. The host re-executes itse - `API_VERSION_MIN_SUPPORTED` (currently `2`) is the oldest plugin major the host loads. - `OCS_PLUGIN_MAX_API_VERSION` can cap the accepted major at runtime (e.g. `5` to disable V6). - A plugin built against major `N` runs on a host whose major is `>= N` because new vtable entries and enum variants are appended at the end. -- V4 introduces the **acadrust gate**: plugins targeting API v4 or later must resolve the same `acadrust` source as the host (see [`src/version_info.rs`](src/version_info.rs)). +- V4 introduces the **opencadcodec gate**: plugins targeting API v4 or later must resolve the same `opencadcodec` source as the host (see [`src/version_info.rs`](src/version_info.rs)). - V5 introduces `BuiltinPlugin::on_load` and tab-keyed document paths. - V6 appends typed system-variable access to `HostApi`; the host currently supports `CLAYER` and `SNAPANG` (plus a read-only `CTAB`). @@ -222,7 +222,7 @@ Broadcasting is V4-gated inside `PluginManager::broadcast_notification`: V2/V3 p The crate embeds two JSON blobs at build time: - **Type registry** (`OUT_DIR/type_registry.json`) — a language-binding-friendly schema for every editor record root and its dependent types, generated by `serde-reflection` in `build.rs`. See [`src/type_registry.rs`](src/type_registry.rs). -- **Version info** (`OUT_DIR/version_info.json`) — host version, `ocs_plugin_api` version, `acadrust` version and source, `rustc` version, API versions, build timestamp. See [`src/version_info.rs`](src/version_info.rs). +- **Version info** (`OUT_DIR/version_info.json`) — host version, `ocs_plugin_api` version, `opencadcodec` version and source, `rustc` version, API versions, build timestamp. See [`src/version_info.rs`](src/version_info.rs). Both are accessible without enabling the `host` feature. diff --git a/crates/ocs_plugin_api/Cargo.toml b/crates/ocs_plugin_api/Cargo.toml index b8d4361a8..4265dcce4 100644 --- a/crates/ocs_plugin_api/Cargo.toml +++ b/crates/ocs_plugin_api/Cargo.toml @@ -12,10 +12,10 @@ license = "GPL-3.0-only" serde = { version = "1", features = ["derive"] } serde_json = { version = "1", optional = true } -# Pulled in only by the `host` feature, which adds the `acadrust`-typed +# Pulled in only by the `host` feature, which adds the `opencadcodec`-typed # `HostApi` runtime surface. The default crate stays dependency-free so engine # crates and external tooling can depend on the manifest/ribbon contract cheaply. -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "1c3be0c", optional = true, features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "43a82f9", optional = true, features = ["serde"] } # Runtime IPC and serialization (host feature only). interprocess = { version = "2", optional = true } @@ -31,15 +31,15 @@ getrandom = { version = "0.2", optional = true } # Enables the runtime host surface (`HostApi` trait) and the out-of-process # plugin runtime. The OpenCADStudio binary turns this on; pure-data consumers # leave it off. -host = ["dep:acadrust", "dep:serde_json", "dep:interprocess", "dep:bincode", "dep:serde_bytes", "dep:thiserror", "dep:libloading", "dep:memmap2", "dep:rkyv", "dep:getrandom"] +host = ["dep:codec", "dep:serde_json", "dep:interprocess", "dep:bincode", "dep:serde_bytes", "dep:thiserror", "dep:libloading", "dep:memmap2", "dep:rkyv", "dep:getrandom"] [build-dependencies] serde-reflection = "0.6" 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 = "1c3be0c", features = ["serde"] } +# opencadcodec is scanned at build time to generate the embedded type registry. +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "43a82f9", features = ["serde"] } [dev-dependencies] serde_json = "1" diff --git a/crates/ocs_plugin_api/README.md b/crates/ocs_plugin_api/README.md index eee1f09d8..c17292a0a 100644 --- a/crates/ocs_plugin_api/README.md +++ b/crates/ocs_plugin_api/README.md @@ -359,8 +359,8 @@ struct SnapshotWorker { impl SnapshotWorker { fn add_point(&self, x: f64, y: f64, z: f64) -> anyhow::Result { - use acadrust::entities::Point; - use acadrust::EntityType; + use opencadcodec::entities::Point; + use opencadcodec::EntityType; let mut p = Point::from_coords(x, y, z); p.common.layer = "0".to_string(); diff --git a/crates/ocs_plugin_api/build.rs b/crates/ocs_plugin_api/build.rs index 87387e1c5..91f5a9aee 100644 --- a/crates/ocs_plugin_api/build.rs +++ b/crates/ocs_plugin_api/build.rs @@ -66,79 +66,79 @@ fn generate_type_registry(out_dir: &Path) { // entity/object variants and their enums are added to the same registry. type TraceFn = fn(&mut Tracer, &Samples); let types: Vec<(&str, TraceFn)> = vec![ - ("EntityType", trace::), - ("ObjectType", trace::), + ("EntityType", trace::), + ("ObjectType", trace::), ( "HeaderVariables", - trace::, + trace::, ), - ("SummaryInfo", trace::), - ("LineType", trace::), - ("TextStyle", trace::), - ("BlockRecord", trace::), - ("DimStyle", trace::), - ("AppId", trace::), - ("View", trace::), - ("VPort", trace::), - ("Ucs", trace::), - ("VxTableRecord", trace::), - ("DxfClass", trace::), + ("SummaryInfo", trace::), + ("LineType", trace::), + ("TextStyle", trace::), + ("BlockRecord", trace::), + ("DimStyle", trace::), + ("AppId", trace::), + ("View", trace::), + ("VPort", trace::), + ("Ucs", trace::), + ("VxTableRecord", trace::), + ("DxfClass", trace::), ( "BlockVisibilityParameter", - trace::, + trace::, ), - ("FieldDef", trace::), + ("FieldDef", trace::), ( "DgnLsDefinition", - trace::, + trace::, ), - ("DgnLsComponent", trace::), + ("DgnLsComponent", trace::), ( "NotificationCollection", - trace::, + trace::, ), - ("Preview", trace::), - ("Point", trace::), - ("Line", trace::), - ("Circle", trace::), - ("Arc", trace::), - ("Ellipse", trace::), - ("Polyline", trace::), - ("Polyline2D", trace::), - ("Polyline3D", trace::), - ("LwPolyline", trace::), - ("MText", trace::), - ("Spline", trace::), - ("EntityCommon", trace::), - ("Handle", trace::), - ("Vector2", trace::), - ("Vector3", trace::), - ("Color", trace::), - ("Layer", trace::), - ("XDataValue", trace::), - ("XRecord", trace::), - ("XRecordEntry", trace::), - ("XRecordValue", trace::), + ("Preview", trace::), + ("Point", trace::), + ("Line", trace::), + ("Circle", trace::), + ("Arc", trace::), + ("Ellipse", trace::), + ("Polyline", trace::), + ("Polyline2D", trace::), + ("Polyline3D", trace::), + ("LwPolyline", trace::), + ("MText", trace::), + ("Spline", trace::), + ("EntityCommon", trace::), + ("Handle", trace::), + ("Vector2", trace::), + ("Vector3", trace::), + ("Color", trace::), + ("Layer", trace::), + ("XDataValue", trace::), + ("XRecord", trace::), + ("XRecordEntry", trace::), + ("XRecordValue", trace::), ( "XRecordValueType", - trace::, + trace::, ), - ("XRecordSection", trace::), + ("XRecordSection", trace::), ( "DictionaryCloningFlags", - trace::, + trace::, ), ( "KnownXRecordKind", - trace::, + trace::, ), ( "ProxyObjectReference", - trace::, + trace::, ), ( "ProxyReferenceKind", - trace::, + trace::, ), ]; @@ -156,338 +156,338 @@ fn generate_type_registry(out_dir: &Path) { let enums: Vec<(&str, TraceSimpleFn)> = vec![ ( "AssocAnnotationKind", - trace_simple::, + trace_simple::, ), ( "AssocConstraintNodeData", - trace_simple::, + trace_simple::, ), ( "AssocEvalValue", - trace_simple::, + trace_simple::, ), ( "AssocSubcurveKind", - trace_simple::, + trace_simple::, ), ( "AssocSurfaceActionKind", - trace_simple::, + trace_simple::, ), ( "AssocViewObjectActionParamKind", - trace_simple::, + trace_simple::, ), ( "AcisVersion", - trace_simple::, + trace_simple::, ), ( "AssociativeData", - trace_simple::, + trace_simple::, ), ( "AttachmentPointType", - trace_simple::, + trace_simple::, ), ( "BlockContentConnectionType", - trace_simple::, + trace_simple::, ), ( "BlockEvalValue", - trace_simple::, + trace_simple::, ), - ("BorderType", trace_simple::), + ("BorderType", trace_simple::), ( "BoundaryEdge", - trace_simple::, + trace_simple::, ), ( "BreakFlowDirection", - trace_simple::, + trace_simple::, ), ( "CellAlignment", - trace_simple::, + trace_simple::, ), ( "CellStyleType", - trace_simple::, + trace_simple::, ), - ("CellType", trace_simple::), + ("CellType", trace_simple::), ( "CellValueType", - trace_simple::, + trace_simple::, ), ( "ClassObjectData", - trace_simple::, + trace_simple::, ), - ("ClipMode", trace_simple::), - ("ClipType", trace_simple::), + ("ClipMode", trace_simple::), + ("ClipType", trace_simple::), ( "CompoundEntry", - trace_simple::, + trace_simple::, ), ( "CompoundPropertyValue", - trace_simple::, + trace_simple::, ), ( "CompoundStreamContent", - trace_simple::, + trace_simple::, ), ( "DataObjectData", - trace_simple::, + trace_simple::, ), ( "DgnLineStyleData", - trace_simple::, + trace_simple::, ), ( "DgnLsComponentData", - trace_simple::, + trace_simple::, ), ( "DgnLsComponentType", - trace_simple::, + trace_simple::, ), ( "DgnLsPhaseMode", - trace_simple::, + trace_simple::, ), - ("DimSubtype", trace_simple::), - ("Dimension", trace_simple::), + ("DimSubtype", trace_simple::), + ("Dimension", trace_simple::), ( "DimensionType", - trace_simple::, + trace_simple::, ), ( "DynamicBlockData", - trace_simple::, + trace_simple::, ), ( "EmbeddedEntity", - trace_simple::, + trace_simple::, ), ( "ExtendedEntityData", - trace_simple::, + trace_simple::, ), ( "FlowDirectionType", - trace_simple::, + trace_simple::, ), ( "HatchPatternType", - trace_simple::, + trace_simple::, ), ( "HatchStyleType", - trace_simple::, + trace_simple::, ), ( "HelixConstraint", - trace_simple::, + trace_simple::, ), ( "HooklineDirection", - trace_simple::, + trace_simple::, ), ( "HorizontalAlignment", - trace_simple::, + trace_simple::, ), ( "LeaderContentType", - trace_simple::, + trace_simple::, ), ( "LeaderCreationType", - trace_simple::, + trace_simple::, ), ( "LeaderDrawOrderType", - trace_simple::, + trace_simple::, ), ( "LeaderPathType", - trace_simple::, + trace_simple::, ), ( "LineTypeComplexContent", - trace_simple::, + trace_simple::, ), ( "LegacyEntityData", - trace_simple::, + trace_simple::, ), ( "MLineJustification", - trace_simple::, + trace_simple::, ), - ("MTextFlag", trace_simple::), + ("MTextFlag", trace_simple::), ( "MaterialProceduralValue", - trace_simple::, + trace_simple::, ), ( "MultiLeaderDrawOrderType", - trace_simple::, + trace_simple::, ), ( "MultiLeaderPathType", - trace_simple::, + trace_simple::, ), ( "ObjectContextKind", - trace_simple::, + trace_simple::, ), ( "NotificationType", - trace_simple::, + trace_simple::, ), ( "OleFrameEnvelope", - trace_simple::, + trace_simple::, ), ( "OleObjectType", - trace_simple::, + trace_simple::, ), ( "PlotPaperUnits", - trace_simple::, + trace_simple::, ), ( "PlotRotation", - trace_simple::, + trace_simple::, ), - ("PlotType", trace_simple::), + ("PlotType", trace_simple::), ( "PreviewFormat", - trace_simple::, + trace_simple::, ), ( "PolyfaceSmoothType", - trace_simple::, + trace_simple::, ), ( "ProxyPayloadEncoding", - trace_simple::, + trace_simple::, ), ( "ResolutionUnit", - trace_simple::, + trace_simple::, ), - ("ScaledType", trace_simple::), + ("ScaledType", trace_simple::), ( "SemanticPropertyValue", - trace_simple::, + trace_simple::, ), ( "ShadePlotMode", - trace_simple::, + trace_simple::, ), ( "ShadePlotResolutionLevel", - trace_simple::, + trace_simple::, ), ( "SolidHistoryOperation", - trace_simple::, + trace_simple::, ), ( "SurfaceData", - trace_simple::, + trace_simple::, ), ( "SurfaceKind", - trace_simple::, + trace_simple::, ), ( "SurfaceSmoothType", - trace_simple::, + trace_simple::, ), ( "TableBorderType", - trace_simple::, + trace_simple::, ), ( "TableCellContentType", - trace_simple::, + trace_simple::, ), ( "TableFlowDirection", - trace_simple::, + trace_simple::, ), ( "TextAlignmentType", - trace_simple::, + trace_simple::, ), ( "TextAngleType", - trace_simple::, + trace_simple::, ), ( "TextAttachmentDirectionType", - trace_simple::, + trace_simple::, ), ( "TextAttachmentPointType", - trace_simple::, + trace_simple::, ), ( "TextAttachmentType", - trace_simple::, + trace_simple::, ), ( "TextHorizontalAlignment", - trace_simple::, + trace_simple::, ), ( "TextVerticalAlignment", - trace_simple::, + trace_simple::, ), ( "UnderlayType", - trace_simple::, + trace_simple::, ), ( "ValueUnitType", - trace_simple::, + trace_simple::, ), ( "VbaDirectoryValue", - trace_simple::, + trace_simple::, ), ( "VerticalAlignment", - trace_simple::, + trace_simple::, ), ( "ViewportRenderMode", - trace_simple::, + trace_simple::, ), ( "VisualStylePropertyValue", - trace_simple::, + trace_simple::, ), ( "ViewRepSketchGeometry", - trace_simple::, + trace_simple::, ), ( "WipeoutClipMode", - trace_simple::, + trace_simple::, ), ( "WipeoutClipType", - trace_simple::, + trace_simple::, ), - ("WireType", trace_simple::), + ("WireType", trace_simple::), ]; for (name, trace) in enums { trace(&mut tracer); @@ -500,7 +500,7 @@ fn generate_type_registry(out_dir: &Path) { let mut registry = map_to_custom_schema(&traced); let mut section_style_tracer = Tracer::new(TracerConfig::default()); section_style_tracer - .trace_simple_type::() + .trace_simple_type::() .expect("section view style tracing failed"); let section_style_registry = section_style_tracer .registry() @@ -892,122 +892,122 @@ fn add_enum_samples(tracer: &mut Tracer, samples: &mut Samples) { // serde-reflection needs at least one sample value per enum variant in // order to reconstruct the full schema. Provide samples for the enums that // need concrete serialized values. - let _ = tracer.trace_value(samples, &acadrust::LineWeight::ByLayer); - let _ = tracer.trace_value(samples, &acadrust::LineWeight::ByBlock); - let _ = tracer.trace_value(samples, &acadrust::LineWeight::Default); - let _ = tracer.trace_value(samples, &acadrust::LineWeight::Value(0)); + let _ = tracer.trace_value(samples, &codec::LineWeight::ByLayer); + let _ = tracer.trace_value(samples, &codec::LineWeight::ByBlock); + let _ = tracer.trace_value(samples, &codec::LineWeight::Default); + let _ = tracer.trace_value(samples, &codec::LineWeight::Value(0)); - let _ = tracer.trace_value(samples, &acadrust::Transparency::BY_LAYER); - let _ = tracer.trace_value(samples, &acadrust::Transparency::BY_BLOCK); - let _ = tracer.trace_value(samples, &acadrust::Transparency::OPAQUE); + let _ = tracer.trace_value(samples, &codec::Transparency::BY_LAYER); + let _ = tracer.trace_value(samples, &codec::Transparency::BY_BLOCK); + let _ = tracer.trace_value(samples, &codec::Transparency::OPAQUE); - let _ = tracer.trace_value(samples, &acadrust::entities::SmoothSurfaceType::None); + let _ = tracer.trace_value(samples, &codec::entities::SmoothSurfaceType::None); let _ = tracer.trace_value( samples, - &acadrust::entities::SmoothSurfaceType::QuadraticBSpline, + &codec::entities::SmoothSurfaceType::QuadraticBSpline, ); let _ = tracer.trace_value( samples, - &acadrust::entities::SmoothSurfaceType::CubicBSpline, + &codec::entities::SmoothSurfaceType::CubicBSpline, ); - let _ = tracer.trace_value(samples, &acadrust::entities::SmoothSurfaceType::Bezier); + let _ = tracer.trace_value(samples, &codec::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, &codec::entities::AttachmentPoint::TopLeft); + let _ = tracer.trace_value(samples, &codec::entities::AttachmentPoint::TopCenter); + let _ = tracer.trace_value(samples, &codec::entities::AttachmentPoint::TopRight); + let _ = tracer.trace_value(samples, &codec::entities::AttachmentPoint::MiddleLeft); + let _ = tracer.trace_value(samples, &codec::entities::AttachmentPoint::MiddleCenter); + let _ = tracer.trace_value(samples, &codec::entities::AttachmentPoint::MiddleRight); + let _ = tracer.trace_value(samples, &codec::entities::AttachmentPoint::BottomLeft); + let _ = tracer.trace_value(samples, &codec::entities::AttachmentPoint::BottomCenter); + let _ = tracer.trace_value(samples, &codec::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, &codec::entities::DrawingDirection::LeftToRight); + let _ = tracer.trace_value(samples, &codec::entities::DrawingDirection::TopToBottom); + let _ = tracer.trace_value(samples, &codec::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, &codec::entities::LineSpacingStyle::AtLeast); + let _ = tracer.trace_value(samples, &codec::entities::LineSpacingStyle::Exactly); - let _ = tracer.trace_value(samples, &acadrust::xdata::XDataValue::String(String::new())); + let _ = tracer.trace_value(samples, &codec::xdata::XDataValue::String(String::new())); let _ = tracer.trace_value( samples, - &acadrust::xdata::XDataValue::ControlString(String::new()), + &codec::xdata::XDataValue::ControlString(String::new()), ); let _ = tracer.trace_value( samples, - &acadrust::xdata::XDataValue::LayerName(String::new()), + &codec::xdata::XDataValue::LayerName(String::new()), ); let _ = tracer.trace_value( samples, - &acadrust::xdata::XDataValue::BinaryData(Vec::new()), + &codec::xdata::XDataValue::BinaryData(Vec::new()), ); let _ = tracer.trace_value( samples, - &acadrust::xdata::XDataValue::Handle(acadrust::Handle::default()), + &codec::xdata::XDataValue::Handle(codec::Handle::default()), ); let _ = tracer.trace_value( samples, - &acadrust::xdata::XDataValue::Point3D(acadrust::Vector3::default()), + &codec::xdata::XDataValue::Point3D(codec::Vector3::default()), ); let _ = tracer.trace_value( samples, - &acadrust::xdata::XDataValue::Position3D(acadrust::Vector3::default()), + &codec::xdata::XDataValue::Position3D(codec::Vector3::default()), ); let _ = tracer.trace_value( samples, - &acadrust::xdata::XDataValue::Displacement3D(acadrust::Vector3::default()), + &codec::xdata::XDataValue::Displacement3D(codec::Vector3::default()), ); let _ = tracer.trace_value( samples, - &acadrust::xdata::XDataValue::Direction3D(acadrust::Vector3::default()), + &codec::xdata::XDataValue::Direction3D(codec::Vector3::default()), ); - let _ = tracer.trace_value(samples, &acadrust::xdata::XDataValue::Real(0.0)); - let _ = tracer.trace_value(samples, &acadrust::xdata::XDataValue::Distance(0.0)); - let _ = tracer.trace_value(samples, &acadrust::xdata::XDataValue::ScaleFactor(0.0)); - let _ = tracer.trace_value(samples, &acadrust::xdata::XDataValue::Integer16(0)); - let _ = tracer.trace_value(samples, &acadrust::xdata::XDataValue::Integer32(0)); + let _ = tracer.trace_value(samples, &codec::xdata::XDataValue::Real(0.0)); + let _ = tracer.trace_value(samples, &codec::xdata::XDataValue::Distance(0.0)); + let _ = tracer.trace_value(samples, &codec::xdata::XDataValue::ScaleFactor(0.0)); + let _ = tracer.trace_value(samples, &codec::xdata::XDataValue::Integer16(0)); + let _ = tracer.trace_value(samples, &codec::xdata::XDataValue::Integer32(0)); // XRecord value variants let _ = tracer.trace_value( samples, - &acadrust::objects::XRecordValue::String(String::new()), + &codec::objects::XRecordValue::String(String::new()), ); - let _ = tracer.trace_value(samples, &acadrust::objects::XRecordValue::Double(0.0)); - let _ = tracer.trace_value(samples, &acadrust::objects::XRecordValue::Int16(0)); - let _ = tracer.trace_value(samples, &acadrust::objects::XRecordValue::Int32(0)); - let _ = tracer.trace_value(samples, &acadrust::objects::XRecordValue::Int64(0)); - let _ = tracer.trace_value(samples, &acadrust::objects::XRecordValue::Byte(0)); - let _ = tracer.trace_value(samples, &acadrust::objects::XRecordValue::Bool(false)); + let _ = tracer.trace_value(samples, &codec::objects::XRecordValue::Double(0.0)); + let _ = tracer.trace_value(samples, &codec::objects::XRecordValue::Int16(0)); + let _ = tracer.trace_value(samples, &codec::objects::XRecordValue::Int32(0)); + let _ = tracer.trace_value(samples, &codec::objects::XRecordValue::Int64(0)); + let _ = tracer.trace_value(samples, &codec::objects::XRecordValue::Byte(0)); + let _ = tracer.trace_value(samples, &codec::objects::XRecordValue::Bool(false)); let _ = tracer.trace_value( samples, - &acadrust::objects::XRecordValue::Handle(acadrust::Handle::default()), + &codec::objects::XRecordValue::Handle(codec::Handle::default()), ); let _ = tracer.trace_value( samples, - &acadrust::objects::XRecordValue::Point3D(0.0, 0.0, 0.0), + &codec::objects::XRecordValue::Point3D(0.0, 0.0, 0.0), ); - let _ = tracer.trace_value(samples, &acadrust::objects::XRecordValue::Chunk(Vec::new())); + let _ = tracer.trace_value(samples, &codec::objects::XRecordValue::Chunk(Vec::new())); // Proxy reference kinds pulled in by XRecord.object_references - let _ = tracer.trace_value(samples, &acadrust::objects::ProxyReferenceKind::Undefined); + let _ = tracer.trace_value(samples, &codec::objects::ProxyReferenceKind::Undefined); let _ = tracer.trace_value( samples, - &acadrust::objects::ProxyReferenceKind::SoftOwnership, + &codec::objects::ProxyReferenceKind::SoftOwnership, ); let _ = tracer.trace_value( samples, - &acadrust::objects::ProxyReferenceKind::HardOwnership, + &codec::objects::ProxyReferenceKind::HardOwnership, ); - let _ = tracer.trace_value(samples, &acadrust::objects::ProxyReferenceKind::SoftPointer); - let _ = tracer.trace_value(samples, &acadrust::objects::ProxyReferenceKind::HardPointer); + let _ = tracer.trace_value(samples, &codec::objects::ProxyReferenceKind::SoftPointer); + let _ = tracer.trace_value(samples, &codec::objects::ProxyReferenceKind::HardPointer); // KnownXRecordKind variants let _ = tracer.trace_value( samples, - &acadrust::objects::KnownXRecordKind::LayerViewportAlphaOverride, + &codec::objects::KnownXRecordKind::LayerViewportAlphaOverride, ); - let _ = tracer.trace_value(samples, &acadrust::objects::KnownXRecordKind::Unknown); + let _ = tracer.trace_value(samples, &codec::objects::KnownXRecordKind::Unknown); } fn map_to_custom_schema(traced: &Registry) -> TypeRegistry { @@ -1257,11 +1257,11 @@ fn generate_version_info(out_dir: &Path) { .iter() .find(|p| p.name.as_str() == "OpenCADStudio") .expect("OpenCADStudio package in Cargo.lock"); - let acadrust = lockfile + let opencadcodec = lockfile .packages .iter() - .find(|p| p.name.as_str() == "acadrust") - .expect("acadrust package in Cargo.lock"); + .find(|p| p.name.as_str() == "opencadcodec") + .expect("opencadcodec package in Cargo.lock"); let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); let rustc_version = Command::new(rustc) @@ -1280,8 +1280,8 @@ fn generate_version_info(out_dir: &Path) { let info = serde_json::json!({ "ocs_version": ocs.version.to_string(), "ocs_plugin_api_version": env!("CARGO_PKG_VERSION"), - "acadrust_version": acadrust.version.to_string(), - "acadrust_source": acadrust.source.as_ref().map(|s| s.to_string()), + "acadrust_version": opencadcodec.version.to_string(), + "acadrust_source": opencadcodec.source.as_ref().map(|s| s.to_string()), "rustc_version": rustc_version, "api_version": 7, "api_version_min_supported": 2, diff --git a/crates/ocs_plugin_api/src/entity_coverage.rs b/crates/ocs_plugin_api/src/entity_coverage.rs index a58dba1da..276eba7f4 100644 --- a/crates/ocs_plugin_api/src/entity_coverage.rs +++ b/crates/ocs_plugin_api/src/entity_coverage.rs @@ -23,7 +23,7 @@ pub fn entity_snapshot( } #[cfg(feature = "host")] -fn finite_vector(name: &str, value: &crate::host::acadrust::types::Vector3) -> Result<(), String> { +fn finite_vector(name: &str, value: &crate::host::codec::types::Vector3) -> Result<(), String> { if value.x.is_finite() && value.y.is_finite() && value.z.is_finite() { Ok(()) } else { @@ -32,7 +32,7 @@ fn finite_vector(name: &str, value: &crate::host::acadrust::types::Vector3) -> R } #[cfg(feature = "host")] -fn unit_direction(name: &str, value: &crate::host::acadrust::types::Vector3) -> Result<(), String> { +fn unit_direction(name: &str, value: &crate::host::codec::types::Vector3) -> Result<(), String> { finite_vector(name, value)?; let length = value.x.hypot(value.y).hypot(value.z); if (length - 1.0).abs() <= 1e-6 { @@ -43,7 +43,7 @@ fn unit_direction(name: &str, value: &crate::host::acadrust::types::Vector3) -> } #[cfg(feature = "host")] -fn solid_normal(name: &str, value: &crate::host::acadrust::types::Vector3) -> Result<(), String> { +fn solid_normal(name: &str, value: &crate::host::codec::types::Vector3) -> Result<(), String> { finite_vector(name, value)?; if value.x.hypot(value.y).hypot(value.z) > 0.0 { Ok(()) @@ -62,9 +62,9 @@ fn insert_scale(name: &str, value: f64) -> Result<(), String> { } #[cfg(feature = "host")] -fn validate_hatch_paths(paths: &[crate::host::acadrust::entities::BoundaryPath]) -> Result<(), String> { - use crate::host::acadrust::entities::BoundaryEdge; - use crate::host::acadrust::types::Vector2; +fn validate_hatch_paths(paths: &[crate::host::codec::entities::BoundaryPath]) -> Result<(), String> { + use crate::host::codec::entities::BoundaryEdge; + use crate::host::codec::types::Vector2; let close = |a: Vector2, b: Vector2| (a.x - b.x).hypot(a.y - b.y) <= 1e-8; let ellipse_point = |center: Vector2, major: Vector2, ratio: f64, angle: f64| { Vector2::new( @@ -200,7 +200,7 @@ fn validate_hatch_paths(paths: &[crate::host::acadrust::entities::BoundaryPath]) } #[cfg(feature = "host")] -fn validate_hatch_pattern(value: &crate::host::acadrust::entities::Hatch) -> Result<(), String> { +fn validate_hatch_pattern(value: &crate::host::codec::entities::Hatch) -> Result<(), String> { if value.pattern.name.trim().is_empty() { return Err("Hatch.pattern.name is empty".into()); } @@ -235,12 +235,12 @@ fn validate_hatch_pattern(value: &crate::host::acadrust::entities::Hatch) -> Res /// checks to fields the edit touched so legacy DWG leaders stay editable. #[cfg(feature = "host")] fn validate_leader( - old: Option<&crate::host::acadrust::entities::Leader>, - new: &crate::host::acadrust::entities::Leader, + old: Option<&crate::host::codec::entities::Leader>, + new: &crate::host::codec::entities::Leader, ) -> Result<(), String> { - use crate::host::acadrust::entities::LeaderCreationType; - let vec_changed = |a: &crate::host::acadrust::types::Vector3, - b: &crate::host::acadrust::types::Vector3| { + use crate::host::codec::entities::LeaderCreationType; + let vec_changed = |a: &crate::host::codec::types::Vector3, + b: &crate::host::codec::types::Vector3| { old.is_none() || a.x.to_bits() != b.x.to_bits() || a.y.to_bits() != b.y.to_bits() @@ -323,8 +323,8 @@ fn validate_leader( /// finiteness is checked here. #[cfg(feature = "host")] fn validate_mline( - old: Option<&crate::host::acadrust::entities::MLine>, - new: &crate::host::acadrust::entities::MLine, + old: Option<&crate::host::codec::entities::MLine>, + new: &crate::host::codec::entities::MLine, ) -> Result<(), String> { if old.is_none_or(|old| old.flags != new.flags) && new.flags.bits() & !0x0f != 0 { return Err("MLine.flags has unknown bits".into()); @@ -369,7 +369,7 @@ fn validate_mline( return Err("MLine.vertices must not all coincide".into()); } } - if new.flags.contains(crate::host::acadrust::entities::MLineFlags::CLOSED) + if new.flags.contains(crate::host::codec::entities::MLineFlags::CLOSED) && new.vertices.len() < 3 { return Err("closed MLine requires at least 3 vertices".into()); @@ -382,10 +382,10 @@ fn validate_mline( /// validated as inputs; the measurement the geometry implies must be finite. #[cfg(feature = "host")] fn validate_dimension( - old: Option<&crate::host::acadrust::entities::Dimension>, - new: &crate::host::acadrust::entities::Dimension, + old: Option<&crate::host::codec::entities::Dimension>, + new: &crate::host::codec::entities::Dimension, ) -> Result<(), String> { - use crate::host::acadrust::entities::Dimension as D; + use crate::host::codec::entities::Dimension as D; if old == Some(new) { return Ok(()); } @@ -398,7 +398,7 @@ fn validate_dimension( if base.style_name.trim().is_empty() { return Err("Dimension.style_name is empty".into()); } - let (points, scalars, distinct): (Vec<(&str, &crate::host::acadrust::types::Vector3)>, Vec<(&str, f64)>, Vec<(&str, &str)>) = match new { + let (points, scalars, distinct): (Vec<(&str, &crate::host::codec::types::Vector3)>, Vec<(&str, f64)>, Vec<(&str, &str)>) = match new { D::Aligned(d) => ( vec![("definition_point", &d.definition_point), ("first_point", &d.first_point), ("second_point", &d.second_point)], vec![("ext_line_rotation", d.ext_line_rotation)], @@ -468,10 +468,10 @@ fn validate_dimension( /// Field checks shared by MultiLeader creation and mutation. #[cfg(feature = "host")] fn validate_multileader( - old: Option<&crate::host::acadrust::entities::MultiLeader>, - new: &crate::host::acadrust::entities::MultiLeader, + old: Option<&crate::host::codec::entities::MultiLeader>, + new: &crate::host::codec::entities::MultiLeader, ) -> Result<(), String> { - use crate::host::acadrust::entities::LeaderContentType; + use crate::host::codec::entities::LeaderContentType; if old == Some(new) { return Ok(()); } @@ -551,8 +551,8 @@ fn validate_multileader( /// Field checks shared by Table creation and mutation. #[cfg(feature = "host")] fn validate_table( - old: Option<&crate::host::acadrust::entities::Table>, - new: &crate::host::acadrust::entities::Table, + old: Option<&crate::host::codec::entities::Table>, + new: &crate::host::codec::entities::Table, ) -> Result<(), String> { if old == Some(new) { return Ok(()); @@ -625,8 +625,8 @@ fn validate_table( /// PolygonMesh: an M x N grid; the vertex list must match exactly. #[cfg(feature = "host")] fn validate_polygon_mesh( - old: Option<&crate::host::acadrust::entities::PolygonMeshEntity>, - new: &crate::host::acadrust::entities::PolygonMeshEntity, + old: Option<&crate::host::codec::entities::PolygonMeshEntity>, + new: &crate::host::codec::entities::PolygonMeshEntity, ) -> Result<(), String> { if old == Some(new) { return Ok(()); @@ -660,8 +660,8 @@ fn validate_polygon_mesh( /// that edge; 0 marks an unused corner). #[cfg(feature = "host")] fn validate_polyface_mesh( - old: Option<&crate::host::acadrust::entities::PolyfaceMesh>, - new: &crate::host::acadrust::entities::PolyfaceMesh, + old: Option<&crate::host::codec::entities::PolyfaceMesh>, + new: &crate::host::codec::entities::PolyfaceMesh, ) -> Result<(), String> { if old == Some(new) { return Ok(()); @@ -703,8 +703,8 @@ fn validate_polyface_mesh( /// Mesh (subdivision): faces and edges index the vertex list (0-based). #[cfg(feature = "host")] fn validate_mesh( - old: Option<&crate::host::acadrust::entities::Mesh>, - new: &crate::host::acadrust::entities::Mesh, + old: Option<&crate::host::codec::entities::Mesh>, + new: &crate::host::codec::entities::Mesh, ) -> Result<(), String> { if old == Some(new) { return Ok(()); @@ -747,8 +747,8 @@ fn validate_mesh( /// the parameters are validated. #[cfg(feature = "host")] fn validate_helix( - old: Option<&crate::host::acadrust::entities::Helix>, - new: &crate::host::acadrust::entities::Helix, + old: Option<&crate::host::codec::entities::Helix>, + new: &crate::host::codec::entities::Helix, ) -> Result<(), String> { if old.is_some_and(|old| { old.axis_base_point == new.axis_base_point @@ -785,8 +785,8 @@ fn validate_helix( /// by the host; a script supplies the file and the placement. #[cfg(feature = "host")] fn validate_raster_image( - old: Option<&crate::host::acadrust::entities::RasterImage>, - new: &crate::host::acadrust::entities::RasterImage, + old: Option<&crate::host::codec::entities::RasterImage>, + new: &crate::host::codec::entities::RasterImage, ) -> Result<(), String> { if old == Some(new) { return Ok(()); @@ -833,10 +833,10 @@ fn validate_raster_image( /// normalized image coordinates) defines the masked area. #[cfg(feature = "host")] fn validate_wipeout( - old: Option<&crate::host::acadrust::entities::Wipeout>, - new: &crate::host::acadrust::entities::Wipeout, + old: Option<&crate::host::codec::entities::Wipeout>, + new: &crate::host::codec::entities::Wipeout, ) -> Result<(), String> { - use crate::host::acadrust::entities::WipeoutClipType; + use crate::host::codec::entities::WipeoutClipType; if old == Some(new) { return Ok(()); } @@ -878,8 +878,8 @@ fn validate_wipeout( /// `UnderlayDefinition`, which a script names by handle at creation. #[cfg(feature = "host")] fn validate_underlay( - old: Option<&crate::host::acadrust::entities::Underlay>, - new: &crate::host::acadrust::entities::Underlay, + old: Option<&crate::host::codec::entities::Underlay>, + new: &crate::host::codec::entities::Underlay, ) -> Result<(), String> { if old == Some(new) { return Ok(()); @@ -923,8 +923,8 @@ fn validate_underlay( /// other references; this validates the numbers. #[cfg(feature = "host")] fn validate_viewport( - old: Option<&crate::host::acadrust::entities::Viewport>, - new: &crate::host::acadrust::entities::Viewport, + old: Option<&crate::host::codec::entities::Viewport>, + new: &crate::host::codec::entities::Viewport, ) -> Result<(), String> { if old == Some(new) { return Ok(()); @@ -952,8 +952,8 @@ fn validate_viewport( /// ViewBorder: the frame of a drawing view, tied to a viewport and a scale. #[cfg(feature = "host")] fn validate_view_border( - old: Option<&crate::host::acadrust::entities::ViewBorder>, - new: &crate::host::acadrust::entities::ViewBorder, + old: Option<&crate::host::codec::entities::ViewBorder>, + new: &crate::host::codec::entities::ViewBorder, ) -> Result<(), String> { if old == Some(new) { return Ok(()); @@ -978,8 +978,8 @@ fn validate_view_border( /// Light: type, placement, intensity, attenuation and shadow settings. #[cfg(feature = "host")] fn validate_light( - old: Option<&crate::host::acadrust::entities::Light>, - new: &crate::host::acadrust::entities::Light, + old: Option<&crate::host::codec::entities::Light>, + new: &crate::host::codec::entities::Light, ) -> Result<(), String> { if old == Some(new) { return Ok(()); @@ -1227,8 +1227,8 @@ fn validate_basic_entity( /// storage is never rewritten. #[cfg(feature = "host")] fn validate_ole_frame( - old: Option<&crate::host::acadrust::entities::Ole2Frame>, - new: &crate::host::acadrust::entities::Ole2Frame, + old: Option<&crate::host::codec::entities::Ole2Frame>, + new: &crate::host::codec::entities::Ole2Frame, ) -> Result<(), String> { if old == Some(new) { return Ok(()); @@ -1249,8 +1249,8 @@ fn validate_ole_frame( /// in the ACIS payload that the geometry kernel owns. #[cfg(feature = "host")] fn validate_surface( - old: Option<&crate::host::acadrust::entities::Surface>, - new: &crate::host::acadrust::entities::Surface, + old: Option<&crate::host::codec::entities::Surface>, + new: &crate::host::codec::entities::Surface, ) -> Result<(), String> { if old.is_some_and(|old| old.u_isolines == new.u_isolines && old.v_isolines == new.v_isolines) { return Ok(()); @@ -1268,8 +1268,8 @@ fn validate_surface( /// derived by the host, so only the inputs are validated. #[cfg(feature = "host")] fn validate_section_symbol( - old: Option<&crate::host::acadrust::entities::SectionSymbol>, - new: &crate::host::acadrust::entities::SectionSymbol, + old: Option<&crate::host::codec::entities::SectionSymbol>, + new: &crate::host::codec::entities::SectionSymbol, ) -> Result<(), String> { if old.is_some_and(|old| old.points == new.points && old.symbol_scale.to_bits() == new.symbol_scale.to_bits()) { return Ok(()); @@ -1553,8 +1553,8 @@ pub fn validate_entity_mutation( Err(format!("{name} must contain finite coordinates")) } }; - let same3 = |a: &crate::host::acadrust::types::Vector3, - b: &crate::host::acadrust::types::Vector3| { + let same3 = |a: &crate::host::codec::types::Vector3, + b: &crate::host::codec::types::Vector3| { a.x.to_bits() == b.x.to_bits() && a.y.to_bits() == b.y.to_bits() && a.z.to_bits() == b.z.to_bits() @@ -1567,8 +1567,8 @@ pub fn validate_entity_mutation( } }; let changed3 = |name: &str, - old: &crate::host::acadrust::types::Vector3, - new: &crate::host::acadrust::types::Vector3| { + old: &crate::host::codec::types::Vector3, + new: &crate::host::codec::types::Vector3| { if !same3(old, new) { finite_vector(name, new) } else { @@ -2060,7 +2060,7 @@ pub fn validate_canvas_entity_references( // An edit that keeps the block name is left alone so a legacy insert of a // missing block stays editable; creation and renames must name a real, // ordinary block that does not create a cycle. - let insert_is_unchanged_edit = |value: &crate::host::acadrust::entities::Insert| { + let insert_is_unchanged_edit = |value: &crate::host::codec::entities::Insert| { matches!(document.get_entity(value.common.handle), Some(EntityType::Insert(existing)) if existing.block_name == value.block_name) }; @@ -2105,7 +2105,7 @@ pub fn validate_canvas_entity_references( } } if let EntityType::SectionSymbol(value) = entity { - use crate::host::acadrust::objects::{ClassObjectData, ObjectType}; + use crate::host::codec::objects::{ClassObjectData, ObjectType}; for (name, handle, wanted) in [ ("style", value.style_handle, "SectionViewStyle"), ("view representation", value.view_rep_handle, "ViewRep"), @@ -2132,7 +2132,7 @@ pub fn validate_canvas_entity_references( } if !matches!( document.objects.get(&value.scale_handle), - Some(crate::host::acadrust::objects::ObjectType::Scale(_)) + Some(crate::host::codec::objects::ObjectType::Scale(_)) ) { return Err(format!("ViewBorder scale {:?} does not exist", value.scale_handle)); } @@ -2155,7 +2155,7 @@ pub fn validate_canvas_entity_references( } if let EntityType::Underlay(value) = entity { match document.objects.get(&value.definition_handle) { - Some(crate::host::acadrust::objects::ObjectType::UnderlayDefinition(definition)) => { + Some(crate::host::codec::objects::ObjectType::UnderlayDefinition(definition)) => { if definition.underlay_type != value.underlay_type { return Err(format!( "Underlay type {:?} does not match its definition ({:?})", @@ -2175,7 +2175,7 @@ pub fn validate_canvas_entity_references( if let Some(handle) = value.definition_handle.filter(|handle| !handle.is_null()) { if !matches!( document.objects.get(&handle), - Some(crate::host::acadrust::objects::ObjectType::ImageDefinition(_)) + Some(crate::host::codec::objects::ObjectType::ImageDefinition(_)) ) { return Err(format!("RasterImage image definition {handle:?} does not exist")); } @@ -2185,7 +2185,7 @@ pub fn validate_canvas_entity_references( if let Some(handle) = value.table_style_handle.filter(|handle| !handle.is_null()) { if !matches!( document.objects.get(&handle), - Some(crate::host::acadrust::objects::ObjectType::TableStyle(_)) + Some(crate::host::codec::objects::ObjectType::TableStyle(_)) ) { return Err(format!("Table style handle {handle:?} does not exist")); } @@ -2208,8 +2208,8 @@ pub fn validate_canvas_entity_references( } } if let EntityType::MultiLeader(value) = entity { - use crate::host::acadrust::objects::ObjectType; - let handle_exists = |handle: Option, what: &str, ok: &dyn Fn(crate::host::acadrust::Handle) -> bool| { + use crate::host::codec::objects::ObjectType; + let handle_exists = |handle: Option, what: &str, ok: &dyn Fn(crate::host::codec::Handle) -> bool| { match handle.filter(|handle| !handle.is_null()) { Some(handle) if !ok(handle) => Err(format!("MultiLeader {what} handle {handle:?} does not exist")), _ => Ok(()), @@ -2252,7 +2252,7 @@ pub fn validate_canvas_entity_references( } } if let EntityType::Leader(value) = entity { - use crate::host::acadrust::entities::LeaderCreationType; + use crate::host::codec::entities::LeaderCreationType; if !document .dim_styles .iter() @@ -2376,9 +2376,9 @@ pub fn validate_canvas_entity_references( #[cfg(feature = "host")] fn resolve_mline_style<'a>( document: &'a crate::host::CadDocument, - mline: &crate::host::acadrust::entities::MLine, -) -> Option<&'a crate::host::acadrust::objects::MLineStyle> { - use crate::host::acadrust::objects::ObjectType; + mline: &crate::host::codec::entities::MLine, +) -> Option<&'a crate::host::codec::objects::MLineStyle> { + use crate::host::codec::objects::ObjectType; mline .style_handle .filter(|handle| !handle.is_null()) @@ -2980,18 +2980,18 @@ mod tests { #[cfg(feature = "host")] #[test] fn tolerance_geometry_and_style_references_are_validated() { - use crate::host::acadrust::{self, types::Vector3}; - let document = acadrust::CadDocument::new(); - let mut tolerance = acadrust::entities::Tolerance::with_text( + use crate::host::codec::{self, types::Vector3}; + let document = codec::CadDocument::new(); + let mut tolerance = codec::entities::Tolerance::with_text( Vector3::new(1.0, 2.0, 0.0), "{\\Fgdt;p}%%v0.1", ); - let entity = acadrust::EntityType::Tolerance(tolerance.clone()); + let entity = codec::EntityType::Tolerance(tolerance.clone()); validate_new_canvas_entity(&entity).unwrap(); validate_canvas_entity_references(&document, &entity).unwrap(); tolerance.direction = Vector3::new(2.0, 0.0, 0.0); assert!( - validate_new_canvas_entity(&acadrust::EntityType::Tolerance(tolerance.clone())) + validate_new_canvas_entity(&codec::EntityType::Tolerance(tolerance.clone())) .unwrap_err() .contains("unit vector") ); @@ -2999,7 +2999,7 @@ mod tests { tolerance.dimension_style_name = "Missing".into(); assert!(validate_canvas_entity_references( &document, - &acadrust::EntityType::Tolerance(tolerance) + &codec::EntityType::Tolerance(tolerance) ) .unwrap_err() .contains("does not exist")); @@ -3008,18 +3008,18 @@ mod tests { #[cfg(feature = "host")] #[test] fn leader_geometry_and_annotation_references_are_validated() { - use crate::host::acadrust::{self, entities::LeaderCreationType, types::Vector3}; - let mut document = acadrust::CadDocument::new(); - let mut text = acadrust::entities::Text::new(); + use crate::host::codec::{self, entities::LeaderCreationType, types::Vector3}; + let mut document = codec::CadDocument::new(); + let mut text = codec::entities::Text::new(); text.common.handle = document.allocate_handle(); let text_handle = text.common.handle; - document.add_entity(acadrust::EntityType::Text(text)).unwrap(); - let mut leader = acadrust::entities::Leader::two_point( + document.add_entity(codec::EntityType::Text(text)).unwrap(); + let mut leader = codec::entities::Leader::two_point( Vector3::new(0.0, 0.0, 0.0), Vector3::new(10.0, 5.0, 0.0), ); leader.annotation_handle = text_handle; - let entity = acadrust::EntityType::Leader(leader.clone()); + let entity = codec::EntityType::Leader(leader.clone()); validate_new_canvas_entity(&entity).unwrap(); validate_canvas_entity_references(&document, &entity).unwrap(); @@ -3027,33 +3027,33 @@ mod tests { wrong_kind.creation_type = LeaderCreationType::WithTolerance; assert!(validate_canvas_entity_references( &document, - &acadrust::EntityType::Leader(wrong_kind) + &codec::EntityType::Leader(wrong_kind) ) .unwrap_err() .contains("does not match")); let mut missing = leader.clone(); - missing.annotation_handle = acadrust::Handle::new(0xdead); + missing.annotation_handle = codec::Handle::new(0xdead); assert!(validate_canvas_entity_references( &document, - &acadrust::EntityType::Leader(missing) + &codec::EntityType::Leader(missing) ) .unwrap_err() .contains("does not exist")); let mut none_with_handle = leader.clone(); none_with_handle.creation_type = LeaderCreationType::NoAnnotation; - assert!(validate_new_canvas_entity(&acadrust::EntityType::Leader(none_with_handle)) + assert!(validate_new_canvas_entity(&codec::EntityType::Leader(none_with_handle)) .unwrap_err() .contains("no annotation")); let mut one_point = leader.clone(); one_point.vertices.truncate(1); - assert!(validate_new_canvas_entity(&acadrust::EntityType::Leader(one_point)) + assert!(validate_new_canvas_entity(&codec::EntityType::Leader(one_point)) .unwrap_err() .contains("at least 2")); let mut bad_style = leader.clone(); bad_style.dimension_style = "Missing".into(); assert!(validate_canvas_entity_references( &document, - &acadrust::EntityType::Leader(bad_style) + &codec::EntityType::Leader(bad_style) ) .unwrap_err() .contains("dimension style")); @@ -3061,8 +3061,8 @@ mod tests { let mut edited = leader.clone(); edited.vertices[1] = Vector3::new(f64::NAN, 0.0, 0.0); assert!(validate_entity_mutation( - &acadrust::EntityType::Leader(leader.clone()), - &acadrust::EntityType::Leader(edited) + &codec::EntityType::Leader(leader.clone()), + &codec::EntityType::Leader(edited) ) .unwrap_err() .contains("finite")); @@ -3071,48 +3071,48 @@ mod tests { #[cfg(feature = "host")] #[test] fn mline_geometry_and_style_references_are_validated_and_bound() { - use crate::host::acadrust::{self, entities::MLineVertex, types::Vector3}; - let document = acadrust::CadDocument::new(); - let mut mline = acadrust::entities::MLine::new(); + use crate::host::codec::{self, entities::MLineVertex, types::Vector3}; + let document = codec::CadDocument::new(); + let mut mline = codec::entities::MLine::new(); for x in [0.0, 5.0] { mline.vertices.push(MLineVertex::new(Vector3::new(x, 0.0, 0.0))); } - let mut entity = acadrust::EntityType::MLine(mline.clone()); + let mut entity = codec::EntityType::MLine(mline.clone()); validate_new_canvas_entity(&entity).unwrap(); bind_canvas_entity_references(&document, &mut entity).unwrap(); - assert!(matches!(&entity, acadrust::EntityType::MLine(value) if value.style_handle.is_some())); + assert!(matches!(&entity, codec::EntityType::MLine(value) if value.style_handle.is_some())); let mut missing = mline.clone(); missing.style_name = "Missing".into(); - assert!(bind_canvas_entity_references(&document, &mut acadrust::EntityType::MLine(missing)) + assert!(bind_canvas_entity_references(&document, &mut codec::EntityType::MLine(missing)) .unwrap_err().contains("does not exist")); let mut wrong_segments = mline.clone(); wrong_segments.vertices[0].init_segments(3); - assert!(bind_canvas_entity_references(&document, &mut acadrust::EntityType::MLine(wrong_segments)) + assert!(bind_canvas_entity_references(&document, &mut codec::EntityType::MLine(wrong_segments)) .unwrap_err().contains("segments but style")); let mut one = mline.clone(); one.vertices.truncate(1); - assert!(validate_new_canvas_entity(&acadrust::EntityType::MLine(one)).unwrap_err().contains("at least 2")); + assert!(validate_new_canvas_entity(&codec::EntityType::MLine(one)).unwrap_err().contains("at least 2")); let mut closed = mline.clone(); - closed.flags |= acadrust::entities::MLineFlags::CLOSED; - assert!(validate_new_canvas_entity(&acadrust::EntityType::MLine(closed)).unwrap_err().contains("at least 3")); + closed.flags |= codec::entities::MLineFlags::CLOSED; + assert!(validate_new_canvas_entity(&codec::EntityType::MLine(closed)).unwrap_err().contains("at least 3")); let mut scale = mline.clone(); scale.scale_factor = 0.0; - assert!(validate_new_canvas_entity(&acadrust::EntityType::MLine(scale)).unwrap_err().contains("nonzero")); + assert!(validate_new_canvas_entity(&codec::EntityType::MLine(scale)).unwrap_err().contains("nonzero")); } #[cfg(feature = "host")] #[test] fn shape_geometry_and_shape_file_style_are_validated_and_bound() { - use crate::host::acadrust::{self, tables::TableEntry, types::Vector3}; - let mut document = acadrust::CadDocument::new(); - let mut style = acadrust::tables::TextStyle::new("Symbols"); + use crate::host::codec::{self, tables::TableEntry, types::Vector3}; + let mut document = codec::CadDocument::new(); + let mut style = codec::tables::TextStyle::new("Symbols"); style.set_handle(document.allocate_handle()); style.is_shape_file = true; style.font_file = "symbols.shx".into(); let style_handle = style.handle; document.text_styles.add(style).unwrap(); - let mut shape = acadrust::entities::Shape::with_style( + let mut shape = codec::entities::Shape::with_style( Vector3::new(1.0, 2.0, 0.0), "ARROW", "Symbols", @@ -3120,17 +3120,17 @@ mod tests { 0.5, ); shape.shape_number = 1; - let mut entity = acadrust::EntityType::Shape(shape); + let mut entity = codec::EntityType::Shape(shape); validate_new_canvas_entity(&entity).unwrap(); bind_canvas_entity_references(&document, &mut entity).unwrap(); - assert!(matches!(entity, acadrust::EntityType::Shape(ref value) + assert!(matches!(entity, codec::EntityType::Shape(ref value) if value.style_handle == Some(style_handle))); - let acadrust::EntityType::Shape(mut bad) = entity else { + let codec::EntityType::Shape(mut bad) = entity else { unreachable!() }; bad.size = 0.0; assert!( - validate_new_canvas_entity(&acadrust::EntityType::Shape(bad)) + validate_new_canvas_entity(&codec::EntityType::Shape(bad)) .unwrap_err() .contains("greater than zero") ); @@ -3204,8 +3204,8 @@ mod tests { #[cfg(feature = "host")] #[test] fn unsupported_canvas_kind_has_typed_snapshot_fields() { - use crate::host::acadrust; - let hatch = acadrust::EntityType::Hatch(acadrust::entities::Hatch::default()); + use crate::host::codec; + let hatch = codec::EntityType::Hatch(codec::entities::Hatch::default()); let snapshot = entity_snapshot(&hatch).unwrap(); let fields = snapshot .get("Hatch") @@ -3218,21 +3218,21 @@ mod tests { #[cfg(feature = "host")] #[test] fn changed_geometry_is_validated_without_rejecting_untouched_legacy_values() { - use crate::host::acadrust; - let mut before = acadrust::entities::Circle::default(); + use crate::host::codec; + let mut before = codec::entities::Circle::default(); before.radius = f64::NAN; let mut layer_only = before.clone(); layer_only.common.layer = "OTHER".into(); assert!(validate_entity_mutation( - &acadrust::EntityType::Circle(before.clone()), - &acadrust::EntityType::Circle(layer_only), + &codec::EntityType::Circle(before.clone()), + &codec::EntityType::Circle(layer_only), ) .is_ok()); let mut bad_radius = before.clone(); bad_radius.radius = -1.0; assert!(validate_entity_mutation( - &acadrust::EntityType::Circle(before), - &acadrust::EntityType::Circle(bad_radius), + &codec::EntityType::Circle(before), + &codec::EntityType::Circle(bad_radius), ) .unwrap_err() .contains("Circle.radius")); @@ -3241,29 +3241,29 @@ mod tests { #[cfg(feature = "host")] #[test] fn new_phase_seven_shapes_reject_invalid_geometry_and_keep_legacy_fields() { - use crate::host::acadrust::{self, types::Vector3}; - let mut ray = acadrust::entities::Ray::default(); + use crate::host::codec::{self, types::Vector3}; + let mut ray = codec::entities::Ray::default(); ray.direction = Vector3::new(2.0, 0.0, 0.0); assert!( - validate_new_canvas_entity(&acadrust::EntityType::Ray(ray.clone())) + validate_new_canvas_entity(&codec::EntityType::Ray(ray.clone())) .unwrap_err() .contains("Ray.direction") ); - let before = acadrust::EntityType::Ray(ray.clone()); + let before = codec::EntityType::Ray(ray.clone()); ray.common.layer = "CONSTRUCTION".into(); - assert!(validate_entity_mutation(&before, &acadrust::EntityType::Ray(ray.clone())).is_ok()); + assert!(validate_entity_mutation(&before, &codec::EntityType::Ray(ray.clone())).is_ok()); ray.direction = Vector3::new(0.0, 1.0, 0.0); - assert!(validate_entity_mutation(&before, &acadrust::EntityType::Ray(ray)).is_ok()); + assert!(validate_entity_mutation(&before, &codec::EntityType::Ray(ray)).is_ok()); - let mut xline = acadrust::entities::XLine::default(); + let mut xline = codec::entities::XLine::default(); xline.base_point.x = f64::NAN; assert!( - validate_new_canvas_entity(&acadrust::EntityType::XLine(xline)) + validate_new_canvas_entity(&codec::EntityType::XLine(xline)) .unwrap_err() .contains("XLine.base_point") ); - let solid = acadrust::entities::Solid::new( + let solid = codec::entities::Solid::new( Vector3::ZERO, Vector3::UNIT_X, Vector3::UNIT_Y, @@ -3272,23 +3272,23 @@ mod tests { let mut changed = solid.clone(); changed.thickness = f64::INFINITY; assert!(validate_entity_mutation( - &acadrust::EntityType::Solid(solid), - &acadrust::EntityType::Solid(changed) + &codec::EntityType::Solid(solid), + &codec::EntityType::Solid(changed) ) .unwrap_err() .contains("Solid.thickness")); - let face = acadrust::entities::Face3D::new( + let face = codec::entities::Face3D::new( Vector3::ZERO, Vector3::UNIT_X, Vector3::UNIT_Y, Vector3::ZERO, ); let mut changed = face.clone(); - changed.invisible_edges = acadrust::entities::InvisibleEdgeFlags::from_bits(0x10); + changed.invisible_edges = codec::entities::InvisibleEdgeFlags::from_bits(0x10); assert!(validate_entity_mutation( - &acadrust::EntityType::Face3D(face), - &acadrust::EntityType::Face3D(changed) + &codec::EntityType::Face3D(face), + &codec::EntityType::Face3D(changed) ) .unwrap_err() .contains("Face3D.invisible_edges")); @@ -3297,37 +3297,37 @@ mod tests { #[cfg(feature = "host")] #[test] fn insert_transaction_checks_transforms_and_protects_references() { - use crate::host::acadrust::{self, types::Vector3}; - let before = acadrust::entities::Insert::new("DOOR", Vector3::new(1.0, 2.0, 0.0)); + use crate::host::codec::{self, types::Vector3}; + let before = codec::entities::Insert::new("DOOR", Vector3::new(1.0, 2.0, 0.0)); let mut moved = before.clone(); moved.insert_point = Vector3::new(5.0, 6.0, 0.0); moved.set_x_scale(2.0); assert!(validate_entity_mutation( - &acadrust::EntityType::Insert(before.clone()), - &acadrust::EntityType::Insert(moved) + &codec::EntityType::Insert(before.clone()), + &codec::EntityType::Insert(moved) ) .is_ok()); let mut bad = before.clone(); bad.set_y_scale(f64::NAN); assert!(validate_entity_mutation( - &acadrust::EntityType::Insert(before.clone()), - &acadrust::EntityType::Insert(bad) + &codec::EntityType::Insert(before.clone()), + &codec::EntityType::Insert(bad) ) .unwrap_err() .contains("Insert.y_scale")); let mut bad = before.clone(); bad.column_count = 0; assert!(validate_entity_mutation( - &acadrust::EntityType::Insert(before.clone()), - &acadrust::EntityType::Insert(bad) + &codec::EntityType::Insert(before.clone()), + &codec::EntityType::Insert(bad) ) .unwrap_err() .contains("array counts")); let mut bad = before.clone(); bad.block_name = "OTHER".into(); assert!(validate_entity_mutation( - &acadrust::EntityType::Insert(before), - &acadrust::EntityType::Insert(bad) + &codec::EntityType::Insert(before), + &codec::EntityType::Insert(bad) ) .unwrap_err() .contains("block identity")); @@ -3336,8 +3336,8 @@ mod tests { #[cfg(feature = "host")] #[test] fn canvas_layer_patch_preserves_unmapped_hatch_fields() { - use crate::host::acadrust; - let hatch = acadrust::EntityType::Hatch(acadrust::entities::Hatch::default()); + use crate::host::codec; + let hatch = codec::EntityType::Hatch(codec::entities::Hatch::default()); let patched = patch_canvas_layer(&hatch, "HATCHES").unwrap(); assert_eq!(patched.common().layer, "HATCHES"); assert_eq!(hatch.common().layer, "0"); @@ -3350,17 +3350,17 @@ mod tests { #[cfg(feature = "host")] #[test] fn attribute_definition_requires_a_block_owner_and_text_style() { - use crate::host::acadrust::{ + use crate::host::codec::{ self, entities::{Block, BlockEnd}, types::{Handle, Vector3}, }; - let mut document = acadrust::CadDocument::new(); + let mut document = codec::CadDocument::new(); let next = document.next_handle(); let record_handle = Handle::new(next); let block_handle = Handle::new(next + 1); let end_handle = Handle::new(next + 2); - let mut record = acadrust::tables::BlockRecord::new("TAGBLOCK"); + let mut record = codec::tables::BlockRecord::new("TAGBLOCK"); record.handle = record_handle; record.block_entity_handle = block_handle; record.block_end_handle = end_handle; @@ -3369,30 +3369,30 @@ mod tests { block.common.handle = block_handle; block.common.owner_handle = record_handle; document - .add_entity(acadrust::EntityType::Block(block)) + .add_entity(codec::EntityType::Block(block)) .unwrap(); let mut end = BlockEnd::new(); end.common.handle = end_handle; end.common.owner_handle = record_handle; document - .add_entity(acadrust::EntityType::BlockEnd(end)) + .add_entity(codec::EntityType::BlockEnd(end)) .unwrap(); - let mut definition = acadrust::entities::AttributeDefinition::new( + let mut definition = codec::entities::AttributeDefinition::new( "PART_NO".into(), "Part number".into(), "PN-001".into(), ); definition.common.owner_handle = record_handle; definition.insertion_point = Vector3::new(1.0, 2.0, 0.0); - let entity = acadrust::EntityType::AttributeDefinition(definition.clone()); + let entity = codec::EntityType::AttributeDefinition(definition.clone()); validate_new_canvas_entity(&entity).unwrap(); validate_canvas_entity_references(&document, &entity).unwrap(); definition.common.owner_handle = Handle::NULL; assert!(validate_canvas_entity_references( &document, - &acadrust::EntityType::AttributeDefinition(definition.clone()) + &codec::EntityType::AttributeDefinition(definition.clone()) ) .unwrap_err() .contains("block-record owner")); @@ -3400,22 +3400,22 @@ mod tests { definition.text_style = "Missing".into(); assert!(validate_canvas_entity_references( &document, - &acadrust::EntityType::AttributeDefinition(definition.clone()) + &codec::EntityType::AttributeDefinition(definition.clone()) ) .unwrap_err() .contains("does not exist")); definition.text_style = "Standard".into(); definition.tag = "BAD TAG".into(); assert!( - validate_new_canvas_entity(&acadrust::EntityType::AttributeDefinition(definition)) + validate_new_canvas_entity(&codec::EntityType::AttributeDefinition(definition)) .unwrap_err() .contains("no whitespace") ); } #[test] fn hatch_geometry_and_boundaries_are_validated() { - use acadrust::entities::hatch::{BoundaryEdge, BoundaryPath, LineEdge}; - let mut hatch = acadrust::entities::Hatch::new(); + use codec::entities::hatch::{BoundaryEdge, BoundaryPath, LineEdge}; + let mut hatch = codec::entities::Hatch::new(); let mut path = BoundaryPath::new(); for (start, end) in [ ((0.0, 0.0), (10.0, 0.0)), @@ -3424,32 +3424,32 @@ mod tests { ((0.0, 10.0), (0.0, 0.0)), ] { path.edges.push(BoundaryEdge::Line(LineEdge { - start: acadrust::types::Vector2::new(start.0, start.1), - end: acadrust::types::Vector2::new(end.0, end.1), + start: codec::types::Vector2::new(start.0, start.1), + end: codec::types::Vector2::new(end.0, end.1), })); } hatch.paths.push(path); - let entity = acadrust::EntityType::Hatch(hatch.clone()); + let entity = codec::EntityType::Hatch(hatch.clone()); validate_new_canvas_entity(&entity).unwrap(); let mut invalid = hatch.clone(); invalid.paths.clear(); - assert!(validate_new_canvas_entity(&acadrust::EntityType::Hatch(invalid)) + assert!(validate_new_canvas_entity(&codec::EntityType::Hatch(invalid)) .unwrap_err() .contains("at least one boundary path")); let mut invalid = hatch.clone(); invalid.is_solid = false; invalid.pattern_scale = 0.0; - assert!(validate_new_canvas_entity(&acadrust::EntityType::Hatch(invalid)) + assert!(validate_new_canvas_entity(&codec::EntityType::Hatch(invalid)) .unwrap_err() .contains("greater than zero")); - let mut document = acadrust::CadDocument::new(); - let boundary = document.add_entity(acadrust::EntityType::Line( - acadrust::entities::Line::from_points( - acadrust::types::Vector3::ZERO, - acadrust::types::Vector3::UNIT_X, + let mut document = codec::CadDocument::new(); + let boundary = document.add_entity(codec::EntityType::Line( + codec::entities::Line::from_points( + codec::types::Vector3::ZERO, + codec::types::Vector3::UNIT_X, ), )).unwrap(); let mut associative = hatch.clone(); @@ -3457,12 +3457,12 @@ mod tests { associative.paths[0].boundary_handles.push(boundary); validate_canvas_entity_references( &document, - &acadrust::EntityType::Hatch(associative.clone()), + &codec::EntityType::Hatch(associative.clone()), ).unwrap(); - associative.paths[0].boundary_handles[0] = acadrust::Handle::new(u64::MAX); + associative.paths[0].boundary_handles[0] = codec::Handle::new(u64::MAX); assert!(validate_canvas_entity_references( &document, - &acadrust::EntityType::Hatch(associative), + &codec::EntityType::Hatch(associative), ).unwrap_err().contains("does not exist")); } } diff --git a/crates/ocs_plugin_api/src/entity_coverage_types.rs b/crates/ocs_plugin_api/src/entity_coverage_types.rs index 9c056c2e2..5d101c694 100644 --- a/crates/ocs_plugin_api/src/entity_coverage_types.rs +++ b/crates/ocs_plugin_api/src/entity_coverage_types.rs @@ -22,7 +22,7 @@ pub enum ModelAccess { pub struct PropertyCoverage { /// Name shown in the document model, or source name when unmapped. pub name: String, - /// Field in the acadrust snapshot; `kind` is synthesized from the variant. + /// Field in the opencadcodec snapshot; `kind` is synthesized from the variant. pub source_path: String, pub type_id: String, pub optional: bool, diff --git a/crates/ocs_plugin_api/src/host.rs b/crates/ocs_plugin_api/src/host.rs index 22e1d80dc..4714bb113 100644 --- a/crates/ocs_plugin_api/src/host.rs +++ b/crates/ocs_plugin_api/src/host.rs @@ -1,6 +1,6 @@ //! Runtime host surface (`host` feature). //! -//! [`HostApi`] is the `acadrust`-typed adapter a plugin uses at *dispatch* time +//! [`HostApi`] is the `opencadcodec`-typed adapter a plugin uses at *dispatch* time //! — document access, entity creation, XDATA, undo, and the command line. It is //! the stable counterpart to the dependency-free manifest/ribbon contract: a //! plugin's `dispatch` receives `&mut dyn HostApi` rather than the host's @@ -19,16 +19,16 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::manifest::PluginManifest; use crate::ribbon::CadModule; -// Re-export the acadrust crate and the types that appear in the HostApi trait -// so out-of-tree plugins can use them without adding their own acadrust +// Re-export the opencadcodec crate and the types that appear in the HostApi trait +// so out-of-tree plugins can use them without adding their own opencadcodec // dependency (which would risk an ABI-mismatching version). -pub use acadrust; -pub use acadrust::objects::{ +pub use codec; +pub use codec::objects::{ DictionaryCloningFlags, KnownXRecordKind, ProxyObjectReference, ProxyReferenceKind, XRecord, XRecordEntry, XRecordSection, XRecordValue, XRecordValueType, }; -pub use acadrust::xdata::{ExtendedDataRecord, XDataValue}; -pub use acadrust::{CadDocument, EntityType, Handle}; +pub use codec::xdata::{ExtendedDataRecord, XDataValue}; +pub use codec::{CadDocument, EntityType, Handle}; use crate::ipc::protocol::{PluginRequest, PluginResponse}; @@ -1016,14 +1016,14 @@ pub trait HostApi { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] pub struct LayerConfig { pub name: String, - pub color: Option, + pub color: Option, pub linetype: Option, - pub lineweight: Option, + pub lineweight: Option, pub off: Option, pub frozen: Option, pub locked: Option, pub plottable: Option, - pub transparency: Option, + pub transparency: Option, pub description: Option, } diff --git a/crates/ocs_plugin_api/src/host_v4.rs b/crates/ocs_plugin_api/src/host_v4.rs index 3674ea8ff..40bca01b7 100644 --- a/crates/ocs_plugin_api/src/host_v4.rs +++ b/crates/ocs_plugin_api/src/host_v4.rs @@ -13,7 +13,7 @@ use std::collections::HashMap; use std::sync::{Mutex, OnceLock}; -use acadrust::CadDocument; +use codec::CadDocument; use crate::shm::{DocumentSnapshotStore, DocumentViewDataV4, DocumentViewInfo}; diff --git a/crates/ocs_plugin_api/src/ipc/client.rs b/crates/ocs_plugin_api/src/ipc/client.rs index b347d44fb..daf58878d 100644 --- a/crates/ocs_plugin_api/src/ipc/client.rs +++ b/crates/ocs_plugin_api/src/ipc/client.rs @@ -6,8 +6,8 @@ use std::collections::HashMap; use std::path::Path; use std::rc::Rc; -use acadrust::xdata::ExtendedDataRecord; -use acadrust::{CadDocument, EntityType, Handle}; +use codec::xdata::ExtendedDataRecord; +use codec::{CadDocument, EntityType, Handle}; use interprocess::local_socket::traits::Stream as StreamTrait; use interprocess::local_socket::{GenericNamespaced, Stream, ToNsName}; @@ -589,8 +589,8 @@ mod tests { use std::sync::atomic::{AtomicU64, Ordering}; use std::thread; - use acadrust::entities::Point; - use acadrust::{EntityType, Handle}; + use codec::entities::Point; + use codec::{EntityType, Handle}; use interprocess::local_socket::{ traits::{Listener, Stream as StreamTrait}, GenericNamespaced, ListenerOptions, Stream, ToNsName, diff --git a/crates/ocs_plugin_api/src/ipc/mod.rs b/crates/ocs_plugin_api/src/ipc/mod.rs index 44e7e02e9..971b47e9b 100644 --- a/crates/ocs_plugin_api/src/ipc/mod.rs +++ b/crates/ocs_plugin_api/src/ipc/mod.rs @@ -1,6 +1,6 @@ //! Inter-process communication layer for out-of-process plugins. //! -//! Built only with the `host` feature because it needs `acadrust`-typed +//! Built only with the `host` feature because it needs `opencadcodec`-typed //! messages and the plugin runner binary. //! //! Submodules: diff --git a/crates/ocs_plugin_api/src/ipc/protocol.rs b/crates/ocs_plugin_api/src/ipc/protocol.rs index 70d7513df..a7f282be7 100644 --- a/crates/ocs_plugin_api/src/ipc/protocol.rs +++ b/crates/ocs_plugin_api/src/ipc/protocol.rs @@ -21,8 +21,8 @@ use crate::host::{CommandSource, CommandStep, HostSettingValue}; use crate::manifest::ApiVersion; use crate::ribbon::owned::{OwnedPluginManifest, OwnedRibbonGroup}; -pub use acadrust::xdata::{ExtendedDataRecord, XDataValue}; -pub use acadrust::{CadDocument, EntityType, Handle}; +pub use codec::xdata::{ExtendedDataRecord, XDataValue}; +pub use codec::{CadDocument, EntityType, Handle}; pub use crate::host::PreviewWire; /// Events the host forwards to an active plugin `InteractiveCommand`. diff --git a/crates/ocs_plugin_api/src/ipc/server.rs b/crates/ocs_plugin_api/src/ipc/server.rs index 33d1e8df6..1941184d2 100644 --- a/crates/ocs_plugin_api/src/ipc/server.rs +++ b/crates/ocs_plugin_api/src/ipc/server.rs @@ -3,17 +3,17 @@ use crate::host::HostApi; use crate::ipc::protocol::{PluginRequest, PluginResponse}; -fn addressable_document_snapshot(host: &dyn HostApi) -> acadrust::CadDocument { +fn addressable_document_snapshot(host: &dyn HostApi) -> codec::CadDocument { let mut snapshot = host.document().clone(); let attributes = host .document() .entities() .filter_map(|entity| match entity { - acadrust::EntityType::Insert(insert) => Some(insert.attributes.clone()), + codec::EntityType::Insert(insert) => Some(insert.attributes.clone()), _ => None, }) .flatten() - .map(acadrust::EntityType::AttributeEntity) + .map(codec::EntityType::AttributeEntity) .collect::>(); for attribute in attributes { // Attribute entities remain canonical children of INSERT in the host diff --git a/crates/ocs_plugin_api/src/ipc/v4/client.rs b/crates/ocs_plugin_api/src/ipc/v4/client.rs index d22983e66..4eeeec8e2 100644 --- a/crates/ocs_plugin_api/src/ipc/v4/client.rs +++ b/crates/ocs_plugin_api/src/ipc/v4/client.rs @@ -9,8 +9,8 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{mpsc, Arc, Mutex}; use std::time::{Duration, Instant}; -use acadrust::xdata::ExtendedDataRecord; -use acadrust::{CadDocument, EntityType, Handle}; +use codec::xdata::ExtendedDataRecord; +use codec::{CadDocument, EntityType, Handle}; use interprocess::local_socket::traits::Stream as StreamTrait; use interprocess::local_socket::{GenericNamespaced, Stream, ToNsName}; use interprocess::TryClone; @@ -937,7 +937,7 @@ mod tests { use crate::ipc::transport::recv; use crate::ipc::v4::protocol::{HostToPluginV4, PluginToHostV4}; use crate::test_lock::ENV_LOCK; - use acadrust::entities::Point; + use codec::entities::Point; fn unique_socket_name() -> String { static COUNTER: AtomicU64 = AtomicU64::new(0); diff --git a/crates/ocs_plugin_api/src/ipc/v4/protocol.rs b/crates/ocs_plugin_api/src/ipc/v4/protocol.rs index bbf969f41..7f5822603 100644 --- a/crates/ocs_plugin_api/src/ipc/v4/protocol.rs +++ b/crates/ocs_plugin_api/src/ipc/v4/protocol.rs @@ -114,7 +114,7 @@ mod tests { CommandRequest::Point { point: [1.0, 2.0, 3.0] }, CommandRequest::Text { text: "2".into() }, CommandRequest::Token { text: "R".into() }, - CommandRequest::Entity { handle: acadrust::Handle::new(9), point: [0.0; 3] }, + CommandRequest::Entity { handle: codec::Handle::new(9), point: [0.0; 3] }, CommandRequest::Selection, CommandRequest::Enter, CommandRequest::Cancel, @@ -149,7 +149,7 @@ mod tests { TableOperation::LayerCreate { config: LayerConfig { name: "Walls".into(), - color: Some(acadrust::types::Color::Index(5)), + color: Some(codec::types::Color::Index(5)), frozen: Some(true), description: Some("d".into()), ..Default::default() @@ -170,7 +170,7 @@ mod tests { TableOperation::StyleSetCurrent { kind: crate::host::TableStyleKind::Dim, name: "A".into() }, TableOperation::BlockCreate { name: "B".into(), - entities: vec![acadrust::Handle::new(4), acadrust::Handle::new(5)], + entities: vec![codec::Handle::new(4), codec::Handle::new(5)], base_point: [1.0, 2.0, 3.0], erase_originals: true, description: Some("d".into()), @@ -180,7 +180,7 @@ mod tests { TableOperation::BlockDelete { name: "C".into() }, TableOperation::BlockEntityAdd { block: "C".into(), - entity: acadrust::EntityType::Line(acadrust::entities::Line::new()), + entity: codec::EntityType::Line(codec::entities::Line::new()), }, TableOperation::LinetypeCreate { name: "L".into(), description: "d".into(), pattern: vec![1.0, -1.0] }, TableOperation::LinetypeModify { name: "L".into(), description: None, pattern: Some(vec![2.0, -2.0]) }, @@ -196,7 +196,7 @@ mod tests { assert!(matches!(bincode::deserialize::(&bytes).unwrap(), PluginRequest::TableOperation { operation: decoded } if decoded == operation)); } - for result in [Ok(acadrust::Handle::new(9)), Err("refused".to_owned())] { + for result in [Ok(codec::Handle::new(9)), Err("refused".to_owned())] { let bytes = bincode::serialize(&PluginResponse::TableResult(result.clone())).unwrap(); assert!(matches!(bincode::deserialize::(&bytes).unwrap(), PluginResponse::TableResult(decoded) if decoded == result)); @@ -211,24 +211,24 @@ mod tests { primitive: SolidPrimitive::Pyramid { center: [1.0, 2.0, 3.0], radius: 4.0, height: 5.0, sides: 6 }, layer: Some("SOLIDS".into()), }, - SolidOperation::Transform { handle: acadrust::Handle::new(7), matrix: [1.0; 16] }, - SolidOperation::RegionFromProfile { source: acadrust::Handle::new(3), layer: None, delete_source: true }, + SolidOperation::Transform { handle: codec::Handle::new(7), matrix: [1.0; 16] }, + SolidOperation::RegionFromProfile { source: codec::Handle::new(3), layer: None, delete_source: true }, SolidOperation::EmbedPicture { path: "p.png".into(), origin: [0.0; 3], width: 5.0, layer: None }, SolidOperation::Boolean { - first: acadrust::Handle::new(1), - second: acadrust::Handle::new(2), + first: codec::Handle::new(1), + second: codec::Handle::new(2), operation: crate::host::SolidBoolean::Subtract, layer: None, keep_operands: false, }, - SolidOperation::SurfaceFromProfile { source: acadrust::Handle::new(4), layer: Some("S".into()), delete_source: false }, - SolidOperation::Extrude { source: acadrust::Handle::new(5), direction: [0.0, 0.0, 2.0], layer: None, delete_source: true }, + SolidOperation::SurfaceFromProfile { source: codec::Handle::new(4), layer: Some("S".into()), delete_source: false }, + SolidOperation::Extrude { source: codec::Handle::new(5), direction: [0.0, 0.0, 2.0], layer: None, delete_source: true }, ] { let bytes = bincode::serialize(&PluginRequest::SolidOperation { operation: operation.clone() }).unwrap(); assert!(matches!(bincode::deserialize::(&bytes).unwrap(), PluginRequest::SolidOperation { operation: decoded } if decoded == operation)); } - for result in [Ok(acadrust::Handle::new(9)), Err("refused".to_owned())] { + for result in [Ok(codec::Handle::new(9)), Err("refused".to_owned())] { let bytes = bincode::serialize(&PluginResponse::SolidResult(result.clone())).unwrap(); assert!(matches!(bincode::deserialize::(&bytes).unwrap(), PluginResponse::SolidResult(decoded) if decoded == result)); @@ -237,14 +237,14 @@ mod tests { #[test] fn selection_request_and_response_roundtrip() { - let request = PluginRequest::SetSelection { handles: vec![acadrust::Handle::new(9)] }; + let request = PluginRequest::SetSelection { handles: vec![codec::Handle::new(9)] }; let bytes = bincode::serialize(&request).unwrap(); assert!(matches!(bincode::deserialize::(&bytes).unwrap(), - PluginRequest::SetSelection { handles } if handles == vec![acadrust::Handle::new(9)])); - let response = PluginResponse::Selection(vec![acadrust::Handle::new(9)]); + PluginRequest::SetSelection { handles } if handles == vec![codec::Handle::new(9)])); + let response = PluginResponse::Selection(vec![codec::Handle::new(9)]); let bytes = bincode::serialize(&response).unwrap(); assert!(matches!(bincode::deserialize::(&bytes).unwrap(), - PluginResponse::Selection(handles) if handles == vec![acadrust::Handle::new(9)])); + PluginResponse::Selection(handles) if handles == vec![codec::Handle::new(9)])); } #[test] diff --git a/crates/ocs_plugin_api/src/lib.rs b/crates/ocs_plugin_api/src/lib.rs index c55cc0342..c36f23d49 100644 --- a/crates/ocs_plugin_api/src/lib.rs +++ b/crates/ocs_plugin_api/src/lib.rs @@ -2,7 +2,7 @@ //! //! The stable, semver-versioned contract an add-on package targets instead of //! the `OpenCADStudio` binary internals. It is intentionally **dependency -//! free** (no `iced`, no `acadrust`) so engine crates and external tooling can +//! free** (no `iced`, no `opencadcodec`) so engine crates and external tooling can //! depend on it cheaply. //! //! Two pieces live here: @@ -13,12 +13,12 @@ //! ([`RibbonGroup`], [`ToolDef`], …) a plugin uses to describe its tab. //! //! The runtime host surface a plugin uses at *dispatch* time (document access, -//! command line, undo) is `acadrust`-typed and therefore lives in the `host` -//! feature; it re-exports `acadrust` so plugins do not need to depend on it +//! command line, undo) is `opencadcodec`-typed and therefore lives in the `host` +//! feature; it re-exports `opencadcodec` so plugins do not need to depend on it //! directly and risk an ABI mismatch from a different version. //! //! For binary compatibility, the host and every plugin must resolve the same -//! `acadrust` source. The host does this via a `[patch.crates-io]` entry in +//! `opencadcodec` source. The host does this via a `[patch.crates-io]` entry in //! `Cargo.toml`; out-of-tree plugins should copy that exact patch. //! //! For internal architecture (process model, wire protocols, versioning policy, @@ -40,7 +40,7 @@ pub mod entity_coverage; pub mod entity_coverage_types; pub mod version_info; -/// Runtime host surface — only built with the `host` feature (pulls `acadrust`). +/// Runtime host surface — only built with the `host` feature (pulls `opencadcodec`). #[cfg(feature = "host")] pub mod host; diff --git a/crates/ocs_plugin_api/src/process.rs b/crates/ocs_plugin_api/src/process.rs index 161ff97a1..352ba0ccd 100644 --- a/crates/ocs_plugin_api/src/process.rs +++ b/crates/ocs_plugin_api/src/process.rs @@ -8,7 +8,7 @@ //! 2. The runner presents a pre-shared token via [`crate::ipc::protocol::PLUGIN_TOKEN_ENV`]; //! the host rejects the connection on mismatch. //! 3. The host requests the manifest, checks `api_version` and (for v4+) the -//! acadrust source gate, then keeps the process alive. +//! opencadcodec source gate, then keeps the process alive. //! 4. Host → plugin calls (`dispatch`, `execute_code`, interactive events) are //! sent over the socket with a configurable per-call timeout. //! 5. Stdout/stderr of the child are drained into `PluginIoLine` records for @@ -1171,8 +1171,8 @@ mod timeout_tests { use crate::ipc::transport::{recv, send}; use crate::ribbon::owned::OwnedPluginManifest; use crate::test_lock::ENV_LOCK; - use acadrust::xdata::ExtendedDataRecord; - use acadrust::{CadDocument, EntityType, Handle}; + use codec::xdata::ExtendedDataRecord; + use codec::{CadDocument, EntityType, Handle}; use interprocess::local_socket::{ traits::{Listener, Stream as StreamTrait}, GenericNamespaced, ListenerOptions, Stream, ToNsName, diff --git a/crates/ocs_plugin_api/src/process/manager.rs b/crates/ocs_plugin_api/src/process/manager.rs index 003f6ff02..82d4d3e18 100644 --- a/crates/ocs_plugin_api/src/process/manager.rs +++ b/crates/ocs_plugin_api/src/process/manager.rs @@ -286,10 +286,10 @@ mod tests { 0 } fn for_each_entity(&self, _f: &mut dyn FnMut(crate::host::ReaderEntity<'_>)) {} - fn layer_name(&self, _handle: acadrust::Handle) -> Option<&str> { + fn layer_name(&self, _handle: codec::Handle) -> Option<&str> { None } - fn app_id_name(&self, _handle: acadrust::Handle) -> Option<&str> { + fn app_id_name(&self, _handle: codec::Handle) -> Option<&str> { None } } @@ -299,34 +299,34 @@ mod tests { fn tab_index(&self) -> usize { 0 } - fn document(&self) -> &acadrust::CadDocument { + fn document(&self) -> &codec::CadDocument { panic!("not used") } - fn document_mut(&mut self) -> &mut acadrust::CadDocument { + fn document_mut(&mut self) -> &mut codec::CadDocument { panic!("not used") } fn document_reader(&self) -> Box { Box::new(EmptyReader) } - fn add_entity(&mut self, _entity: acadrust::EntityType) -> acadrust::Handle { + fn add_entity(&mut self, _entity: codec::EntityType) -> codec::Handle { panic!("not used") } fn bump_geometry(&mut self) {} fn read_record( &self, - _handle: acadrust::Handle, + _handle: codec::Handle, _app_name: &str, - ) -> Option<&acadrust::xdata::ExtendedDataRecord> { + ) -> Option<&codec::xdata::ExtendedDataRecord> { None } fn write_record( &mut self, - _handle: acadrust::Handle, - _record: acadrust::xdata::ExtendedDataRecord, + _handle: codec::Handle, + _record: codec::xdata::ExtendedDataRecord, ) -> bool { false } - fn remove_record(&mut self, _handle: acadrust::Handle, _app_name: &str) -> bool { + fn remove_record(&mut self, _handle: codec::Handle, _app_name: &str) -> bool { false } fn push_undo(&mut self, _label: &str) {} diff --git a/crates/ocs_plugin_api/src/process/v4.rs b/crates/ocs_plugin_api/src/process/v4.rs index dfcd7b4a1..25b1b6d2e 100644 --- a/crates/ocs_plugin_api/src/process/v4.rs +++ b/crates/ocs_plugin_api/src/process/v4.rs @@ -338,7 +338,7 @@ mod tests { use crate::ipc::protocol::{HostResponse, PluginRequest, PluginResponse}; use crate::ipc::transport::recv; use crate::ipc::v4::protocol::{HostToPluginV4, PluginToHostV4}; - use acadrust::{CadDocument, Handle}; + use codec::{CadDocument, Handle}; use interprocess::local_socket::{ traits::{Listener, Stream as StreamTrait}, GenericNamespaced, ListenerOptions, ToNsName, @@ -386,7 +386,7 @@ mod tests { struct DummyHost { push_info_messages: StdMutex>, - transactions: StdMutex)>>, + transactions: StdMutex)>>, } impl DummyHost { fn new() -> Self { @@ -398,7 +398,7 @@ mod tests { fn take_push_info(&self) -> Vec { std::mem::take(&mut *self.push_info_messages.lock().unwrap()) } - fn take_transactions(&self) -> Vec<(String, Vec)> { + fn take_transactions(&self) -> Vec<(String, Vec)> { std::mem::take(&mut *self.transactions.lock().unwrap()) } } @@ -415,7 +415,7 @@ mod tests { fn document_reader(&self) -> Box { Box::new(EmptyReader) } - fn add_entity(&mut self, _entity: acadrust::EntityType) -> Handle { + fn add_entity(&mut self, _entity: codec::EntityType) -> Handle { panic!("not used") } fn bump_geometry(&mut self) {} @@ -423,13 +423,13 @@ mod tests { &self, _handle: Handle, _app_name: &str, - ) -> Option<&acadrust::xdata::ExtendedDataRecord> { + ) -> Option<&codec::xdata::ExtendedDataRecord> { None } fn write_record( &mut self, _handle: Handle, - _record: acadrust::xdata::ExtendedDataRecord, + _record: codec::xdata::ExtendedDataRecord, ) -> bool { false } @@ -440,7 +440,7 @@ mod tests { fn update_entities_transaction( &mut self, label: &str, - entities: Vec, + entities: Vec, ) -> Result<(), String> { self.transactions.lock().unwrap().push((label.to_owned(), entities)); Ok(()) @@ -624,10 +624,10 @@ mod tests { let handler: Arc, PluginNotification) + Send + Sync> = Arc::new(|_, _| {}); let conn = V4Connection::new(host_stream, handler).unwrap(); - let mut point = acadrust::entities::Point::new(); + let mut point = codec::entities::Point::new(); point.common.handle = Handle::new(42); point.location.x = 7.0; - let expected = acadrust::EntityType::Point(point); + let expected = codec::EntityType::Point(point); let sent = expected.clone(); let runner = thread::spawn(move || { diff --git a/crates/ocs_plugin_api/src/shm.rs b/crates/ocs_plugin_api/src/shm.rs index 5344737da..4ded3f7f0 100644 --- a/crates/ocs_plugin_api/src/shm.rs +++ b/crates/ocs_plugin_api/src/shm.rs @@ -29,7 +29,7 @@ use std::io; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering}; -use acadrust::{CadDocument, EntityType, Handle}; +use codec::{CadDocument, EntityType, Handle}; use memmap2::{Mmap, MmapMut, MmapOptions}; use rkyv::{check_archived_root, to_bytes, Archive, Deserialize, Serialize}; @@ -444,8 +444,8 @@ pub struct LayerView { pub name: String, } -impl From<&acadrust::tables::Layer> for LayerView { - fn from(layer: &acadrust::tables::Layer) -> Self { +impl From<&codec::tables::Layer> for LayerView { + fn from(layer: &codec::tables::Layer) -> Self { Self { handle: layer.handle.value(), name: layer.name.clone(), @@ -460,8 +460,8 @@ pub struct AppIdView { pub name: String, } -impl From<&acadrust::tables::AppId> for AppIdView { - fn from(app_id: &acadrust::tables::AppId) -> Self { +impl From<&codec::tables::AppId> for AppIdView { + fn from(app_id: &codec::tables::AppId) -> Self { Self { handle: app_id.handle.value(), name: app_id.name.clone(), @@ -539,8 +539,8 @@ impl ReaderEntityKind { // ── V4 full-entity document view ──────────────────────────────────────────── /// Serializable V4 document view. The outer structure is rkyv; each entity's -/// `data` is a bincode-encoded `acadrust::EntityType` so that the plugin can -/// reconstruct the full typed entity without relying on acadrust's own rkyv +/// `data` is a bincode-encoded `codec::EntityType` so that the plugin can +/// reconstruct the full typed entity without relying on opencadcodec's own rkyv /// support. #[derive(Archive, Serialize, Deserialize, Debug, Clone)] #[archive(check_bytes)] @@ -605,9 +605,9 @@ impl From<&EntityType> for EntityViewV4 { mod tests { use super::*; use crate::host::{DocumentReader, ReaderEntityKind}; - use acadrust::entities::Point; - use acadrust::tables::Layer; - use acadrust::{CadDocument, EntityType}; + use codec::entities::Point; + use codec::tables::Layer; + use codec::{CadDocument, EntityType}; fn unique_path(name: &str) -> PathBuf { std::env::temp_dir().join(format!( diff --git a/crates/ocs_plugin_api/src/type_registry.rs b/crates/ocs_plugin_api/src/type_registry.rs index 2b5dfc794..a3e9d4288 100644 --- a/crates/ocs_plugin_api/src/type_registry.rs +++ b/crates/ocs_plugin_api/src/type_registry.rs @@ -1,7 +1,7 @@ //! Embedded type registry generated at build time. //! //! The JSON embedded here is produced by tracing every editor record root and -//! its dependent `acadrust` types with `serde-reflection`, then mapping the +//! its dependent `opencadcodec` types with `serde-reflection`, then mapping the //! result into a stable, language-binding-friendly schema defined in //! [`crate::type_registry_types`]. The registry is embedded via //! `include_str!(concat!(env!("OUT_DIR"), "/type_registry.json"))` so the @@ -9,7 +9,7 @@ //! //! The registry covers the complete entity, object, symbol-table, header, and //! document-record roots used by the editor. Language bindings and tooling can -//! inspect every variant without pulling the full `acadrust` dependency tree. +//! inspect every variant without pulling the full `opencadcodec` dependency tree. pub use crate::type_registry_types::*; diff --git a/crates/ocs_plugin_api/src/version_info.rs b/crates/ocs_plugin_api/src/version_info.rs index 8d25e0ede..177a05a86 100644 --- a/crates/ocs_plugin_api/src/version_info.rs +++ b/crates/ocs_plugin_api/src/version_info.rs @@ -2,13 +2,13 @@ //! //! At compile time `build.rs` writes a small JSON blob to //! `OUT_DIR/version_info.json` containing host version, `ocs_plugin_api` -//! version, `acadrust` version and source, API version bounds, and a build +//! version, `opencadcodec` version and source, API version bounds, and a build //! timestamp. The blob is embedded via `include_str!` and is available without //! enabling the `host` feature. //! -//! The acadrust source string is used to detect binary-incompatible plugin +//! The opencadcodec source string is used to detect binary-incompatible plugin //! builds. For API v4 and later the host compares the plugin's resolved -//! `acadrust` source with its own via [`acadrust_sources_compatible`]. Two +//! `opencadcodec` source with its own via [`acadrust_sources_compatible`]. Two //! sources are considered compatible only when they resolve to the same 40 //! character git commit hash. //! @@ -60,7 +60,7 @@ fn parsed_version_info() -> &'static VersionInfo { }) } -/// Returns the host's full `acadrust` Cargo source. +/// Returns the host's full `opencadcodec` Cargo source. pub fn host_acadrust_source() -> &'static str { &parsed_version_info().acadrust_source } @@ -151,8 +151,8 @@ mod tests { assert_eq!(value["api_version_min_supported"].as_i64(), Some(2)); // acadrust_version should have a patch component (e.g. "0.4.0"). - let acadrust = value["acadrust_version"].as_str().expect("acadrust_version string"); - assert_eq!(acadrust.split('.').count(), 3); + let opencadcodec = value["acadrust_version"].as_str().expect("acadrust_version string"); + assert_eq!(opencadcodec.split('.').count(), 3); } #[test] @@ -208,7 +208,7 @@ mod tests { #[test] fn extracts_full_hash_from_cargo_source() { - let src = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"; + let src = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"; assert_eq!( acadrust_source_hash(src), Some("94df2c3f87fa051b16ffc3923f80e9247c85c5fd") @@ -228,22 +228,22 @@ mod tests { #[test] fn source_comparison_matches_full_hashes() { - let a = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"; - let b = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"; + let a = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"; + let b = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"; assert!(acadrust_sources_compatible(a, b)); } #[test] fn source_comparison_detects_mismatch() { - let a = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"; - let b = "git+https://github.com/HakanSeven12/cadcodec.git?rev=0908da7#0908da7b6e4f702a6c78359a57f53e2b79cf39eb"; + let a = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"; + let b = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=0908da7#0908da7b6e4f702a6c78359a57f53e2b79cf39eb"; assert!(!acadrust_sources_compatible(a, b)); } #[test] fn source_comparison_case_insensitive() { - let a = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"; - let b = "git+https://github.com/HakanSeven12/cadcodec.git?rev=94df2c3#94DF2C3F87FA051B16FFC3923F80E9247C85C5FD"; + let a = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94df2c3f87fa051b16ffc3923f80e9247c85c5fd"; + let b = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=94df2c3#94DF2C3F87FA051B16FFC3923F80E9247C85C5FD"; assert!(acadrust_sources_compatible(a, b)); } } diff --git a/crates/ocs_web_worker/Cargo.toml b/crates/ocs_web_worker/Cargo.toml index 666bc4714..ad0c2cc78 100644 --- a/crates/ocs_web_worker/Cargo.toml +++ b/crates/ocs_web_worker/Cargo.toml @@ -8,7 +8,7 @@ publish = false crate-type = ["cdylib"] [dependencies] -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "1c3be0c", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "43a82f9", features = ["serde"] } bincode = "1.3" serde = { version = "1", features = ["derive"] } console_error_panic_hook = "0.1" diff --git a/crates/ocs_web_worker/src/lib.rs b/crates/ocs_web_worker/src/lib.rs index a3593b95d..ed8c657a6 100644 --- a/crates/ocs_web_worker/src/lib.rs +++ b/crates/ocs_web_worker/src/lib.rs @@ -1,8 +1,8 @@ use std::io::Cursor; use std::sync::Arc; -use acadrust::io::dwg::DwgReader; -use acadrust::{DwgReadOptions, DxfReader, DxfReaderConfiguration}; +use codec::io::dwg::DwgReader; +use codec::{DwgReadOptions, DxfReader, DxfReaderConfiguration}; use js_sys::{Function, Uint8Array}; use sha2::{Digest, Sha256}; use wasm_bindgen::prelude::*; @@ -121,14 +121,14 @@ pub fn parse_document( } if recovery_mode { outcome.document.notifications.notify( - acadrust::notification::NotificationType::Error, + codec::notification::NotificationType::Error, format!("Initial read failed; recovery mode continued: {initial_error}"), ); - acadrust::push_read_diagnostic( + codec::push_read_diagnostic( &mut outcome.stats.diagnostics, - acadrust::ReadDiagnostic::new( + codec::ReadDiagnostic::new( "strict-read-failed", - acadrust::ReadStage::RecordStream, + codec::ReadStage::RecordStream, initial_error, ), ); @@ -147,30 +147,30 @@ pub fn sha256_document(bytes: Uint8Array) -> String { sha256_document_bytes(&bytes.to_vec()) } -fn report_fingerprint_needed(stats: &acadrust::ReadStats) -> bool { +fn report_fingerprint_needed(stats: &codec::ReadStats) -> bool { stats.recovered() || stats.skipped_source_records > 0 || !stats.stream_completed } -fn recoverable_reader_error(error: &acadrust::DxfError) -> bool { +fn recoverable_reader_error(error: &codec::DxfError) -> bool { matches!( error, - acadrust::DxfError::Compression(_) - | acadrust::DxfError::Parse(_) - | acadrust::DxfError::InvalidDxfCode(_) - | acadrust::DxfError::InvalidHandle(_) - | acadrust::DxfError::ObjectNotFound(_) - | acadrust::DxfError::InvalidEntityType(_) - | acadrust::DxfError::ChecksumMismatch { .. } - | acadrust::DxfError::InvalidHeader(_) - | acadrust::DxfError::InvalidFormat(_) - | acadrust::DxfError::InvalidSentinel(_) - | acadrust::DxfError::Decompression(_) - | acadrust::DxfError::Encoding(_) + codec::DxfError::Compression(_) + | codec::DxfError::Parse(_) + | codec::DxfError::InvalidDxfCode(_) + | codec::DxfError::InvalidHandle(_) + | codec::DxfError::ObjectNotFound(_) + | codec::DxfError::InvalidEntityType(_) + | codec::DxfError::ChecksumMismatch { .. } + | codec::DxfError::InvalidHeader(_) + | codec::DxfError::InvalidFormat(_) + | codec::DxfError::InvalidSentinel(_) + | codec::DxfError::Decompression(_) + | codec::DxfError::Encoding(_) ) } fn encode_result( - result: Result)>, + result: Result)>, source_sha256: Option, recoverable_parse_error: bool, bytes: &[u8], diff --git a/crates/plugin-template-api2/Cargo.toml b/crates/plugin-template-api2/Cargo.toml index 0236b7294..b1794533c 100644 --- a/crates/plugin-template-api2/Cargo.toml +++ b/crates/plugin-template-api2/Cargo.toml @@ -10,4 +10,4 @@ crate-type = ["cdylib"] [dependencies] ocs_plugin_api = { path = "../ocs_plugin_api", features = ["host"] } -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "8a28c21", features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8a28c21", features = ["serde"] } diff --git a/docs/cadcodec-reader-gaps.md b/docs/cadcodec-reader-gaps.md index 19895ff63..597e6f5f4 100644 --- a/docs/cadcodec-reader-gaps.md +++ b/docs/cadcodec-reader-gaps.md @@ -1,13 +1,13 @@ -# cadcodec DXF reader/writer gaps found by the OCS Python host audit +# opencadcodec DXF reader/writer gaps found by the OCS Python host audit -Reports on `HakanSeven12/cadcodec` (the `acadrust` crate). Every finding was made against -**cadcodec revision `5b682ed`** by saving a document with `DxfWriter` or `DwgWriter`, +Reports on `HakanSeven12/opencadcodec` (the `opencadcodec` crate). Every finding was made against +**opencadcodec revision `5b682ed`** by saving a document with `DxfWriter` or `DwgWriter`, reloading it, and comparing entity fields. -**Status (23 September 2026):** the fixes landed in cadcodec as -[#48](https://github.com/HakanSeven12/cadcodec/pull/48) (issues 1-6) and -[#51](https://github.com/HakanSeven12/cadcodec/pull/51) (the first three style findings), and -the block description with commit `dd1d7bf` (cadcodec issue #49). OCS pins `1c3be0c`, which +**Status (23 September 2026):** the fixes landed in opencadcodec as +[#48](https://github.com/HakanSeven12/opencadcodec/pull/48) (issues 1-6) and +[#51](https://github.com/HakanSeven12/opencadcodec/pull/51) (the first three style findings), and +the block description with commit `dd1d7bf` (opencadcodec issue #49). OCS pins `1c3be0c`, which includes it; the canaries below were flipped against `dd1d7bf`. What is still open: - **ATTDEF `lock_position`** (issue 1): the reader is complete, but the DXF writer emits no @@ -17,7 +17,7 @@ includes it; the canaries below were flipped against `dd1d7bf`. What is still op - **`TextStyle::true_type_font`** (style findings): still neither written nor read. Each report names an executable canary in OCS (`src/app/plugin_host.rs`). The -canaries assert today's wrong result, so they fail loudly once cadcodec is +canaries assert today's wrong result, so they fail loudly once opencadcodec is fixed; flip the marked expectation when that happens. To reproduce, from the OCS repository: @@ -115,7 +115,7 @@ raw value: `50 => { if let Some(v) = pair.as_double() { underlay.rotation = v; } **What happens:** a rotation of `0.5` rad is written as 28.6479 degrees and read back as 28.6479 rad. Because the writer converts again on every save, the error compounds: a second save-and-reopen yields 1641.4. This silently corrupts -existing drawings each time they pass through cadcodec. +existing drawings each time they pass through opencadcodec. **Evidence:** `audit_python_underlay_lifecycle_over_real_ipc`; the canary `expect_edited_dxf` / `expect_reedited_dxf` records 28.6 then 1641.4. @@ -210,14 +210,14 @@ These are format constraints or OCS-side issues, not reader bugs: ### Text and dimension style findings (2026-09-21) Found by `audit_python_text_and_dim_styles_over_real_ipc`; each is pinned by a canary. The first -three are fixed in [HakanSeven12/cadcodec#51](https://github.com/HakanSeven12/cadcodec/pull/51) +three are fixed in [HakanSeven12/opencadcodec#51](https://github.com/HakanSeven12/opencadcodec/pull/51) (independent of #48); the last two are not filed, because fixing them needs the XDATA layout AutoCAD expects and that cannot be checked here. - **`STYLE` generation flags:** the DXF writer hard-codes group 71 to 0, so a text style's `flags.backward` and `flags.upside_down` are lost on every DXF save (DWG keeps them). - **`STYLE` oblique angle units:** group 50 is written and read as the raw radian value, while DXF - defines it in degrees; it round-trips inside cadcodec but AutoCAD would read 15 degrees as 0.26. + defines it in degrees; it round-trips inside opencadcodec but AutoCAD would read 15 degrees as 0.26. DWG stores radians correctly. - **`DIMSTYLE` text style name:** the DXF reader keeps only the text-style handle (group 340) and never resolves `dimtxsty` from it, so the name reopens as `Standard` (the handle is right; DWG resolves both). @@ -225,5 +225,5 @@ AutoCAD expects and that cannot be checked here. - **`BLOCK_RECORD` description:** the DXF codec did not carry `BlockRecord::description` (found by `audit_python_blocks_over_real_ipc`). It needs no XDATA: it is BLOCK group 4, which the reader parsed onto the discarded BLOCK marker and the writer never emitted. Fixed in - cadcodec `dd1d7bf` (cadcodec issue #49). + opencadcodec `dd1d7bf` (opencadcodec issue #49). diff --git a/docs/cadkernel-body-path.md b/docs/cadkernel-body-path.md index 312e36960..6832f78aa 100644 --- a/docs/cadkernel-body-path.md +++ b/docs/cadkernel-body-path.md @@ -1,4 +1,4 @@ -# Scripting ACIS bodies through cadkernel: findings and plan +# Scripting ACIS bodies through opencadkernel: findings and plan Investigation of the blocker on **Solid3D, Body, Region and Surface** (`docs/plugin-host-model-coverage-ledger.md`). The blocker said a script can @@ -7,7 +7,7 @@ forbids rewriting an opaque payload without a proven lossless path. This note records what OCS's own kernel path can already do, measured with two spike tests (`spike_kernel_body_round_trip`, `spike_kernel_timings` in `src/app/plugin_host.rs`, both `#[ignore]` because they are slow in a debug -build). Measured on cadcodec `5b682ed` / cadkernel `6f046af`, debug build. +build). Measured on opencadcodec `5b682ed` / opencadkernel `6f046af`, debug build. **Conclusion:** the blocker was overstated. OCS already owns a kernel-backed create/transform/boolean pipeline whose output is verified lossless before it is @@ -63,7 +63,7 @@ Two things to understand about "lossless": ## Design -The obstacle is the channel. The generic entity path moves an acadrust +The obstacle is the channel. The generic entity path moves an opencadcodec `EntityType` from the plugin to the host; the plugin has no kernel and must never see or rewrite ACIS bytes. Three ways to give a script the operations: @@ -141,11 +141,11 @@ Phase 1 alone would move Solid3D from blocked to complete-in-OCS. ## Boolean timings (release build) Measured 2026-09-20 in a scratch crate built with `--release` (opt-level 3) -against the same cadkernel revision OCS pins (`6f046af`, features `acis` and +against the same opencadkernel revision OCS pins (`6f046af`, features `acis` and `offset`), calling exactly what OCS's `solid_model::boolean_result` calls: `brep::operation_tolerance` then `brep::combine`. A debug build of OCS is about 22 times slower, so booleans must never be exercised in a debug test run. The -scratch program is not in the repository; to repeat it, depend on cadkernel at +scratch program is not in the repository; to repeat it, depend on opencadkernel at that revision and time `brep::combine` on `brep::make::{cuboid, cylinder, sphere, torus}` operands. diff --git a/docs/native-vs-web.md b/docs/native-vs-web.md index 6d314ebdf..b91d026fc 100644 --- a/docs/native-vs-web.md +++ b/docs/native-vs-web.md @@ -28,7 +28,7 @@ crates reach `vtkio → xz2 → lzma-sys` — a C library that cannot cross-comp to `wasm32` — so the web build dropped a `solid3d` feature and the Model tab, solid tessellation and ACIS import all did nothing there. -The kernel is now cadkernel, which is pure Rust and has no C dependency at +The kernel is now opencadkernel, which is pure Rust and has no C dependency at all, so none of that applies and the feature is gone. The web build makes primitives, runs booleans, draws ACIS solids read from a file and writes them back out as exact geometry, exactly as the desktop build does. diff --git a/docs/plugin-architecture.md b/docs/plugin-architecture.md index 439c41d37..2e5994ee0 100644 --- a/docs/plugin-architecture.md +++ b/docs/plugin-architecture.md @@ -34,7 +34,7 @@ engine crate, and user-installable packages from a curated index. the repos they install from). Process isolation limits the blast radius of a buggy or malicious plugin, but it is not a security sandbox. - Cross-toolchain binary compatibility — see [Compatibility](#compatibility--abi). -- Sandboxed scripting (Python/Lua); replacing the `acadrust` entity model. +- Sandboxed scripting (Python/Lua); replacing the `opencadcodec` entity model. --- @@ -56,7 +56,7 @@ engine crate, and user-installable packages from a curated index. │ pure Rust API ┌───────────────────────────────▼────────────────────────────────────┐ │ Layer C — Domain engine crate (optional) │ -│ hydraulics / COGO / … — `std` only, no iced/acadrust │ +│ hydraulics / COGO / … — `std` only, no iced/opencadcodec │ └────────────────────────────────────────────────────────────────────┘ ``` @@ -69,7 +69,7 @@ engine crate, and user-installable packages from a curated index. **Hard rules** 1. The host (`src/plugin/`) imports no plugin code — it only knows the contract. -2. Engine crates import neither `iced`, `acadrust`, nor `OpenCADStudio`. +2. Engine crates import neither `iced`, `opencadcodec`, nor `OpenCADStudio`. 3. A plugin never edits host source; it runs entirely from its own crate. --- @@ -83,7 +83,7 @@ plugin compiles against. Two tiers: ribbon vocabulary — `CadModule`, `ToolDef`, `RibbonGroup`, `RibbonItem`, `IconKind`, `ModuleEvent`, `StyleKey`. Engine crates and tooling depend on this cheaply. -- **`host` feature** (pulls `acadrust`): the runtime surface — the `HostApi` +- **`host` feature** (pulls `opencadcodec`): the runtime surface — the `HostApi` trait, the `BuiltinPlugin` entry-point trait, the `export_plugin!` macro, and the out-of-process plugin runtime (`PluginProcess`, `runner`). @@ -94,7 +94,7 @@ A plugin enables the `host` feature. For API v4 and later the host records two pieces of build-time metadata that affect whether a prebuilt plugin can safely be loaded: -- `acadrust_source` — the exact git source of the `acadrust` crate the host was +- `acadrust_source` — the exact git source of the `opencadcodec` crate the host was built against. - `rustc_version` — the output of `rustc --version` for the compiler that built the host. @@ -180,9 +180,9 @@ crate-type = ["cdylib"] [dependencies] ocs_plugin_api = { git = "https://github.com/HakanSeven12/OpenCADStudio", features = ["host"] } -# Match the host's acadrust so the loaded library is binary-compatible. +# Match the host's opencadcodec so the loaded library is binary-compatible. [patch.crates-io] -acadrust = { git = "https://github.com/HakanSeven12/acadrust", branch = "main" } +opencadcodec = { git = "https://github.com/HakanSeven12/opencadcodec", branch = "main" } ``` Your release build must also use the same `rustc` as the host. Record it in @@ -243,7 +243,7 @@ command_prefixes = ["EX_"] xdata_apps = [] # Both are filled at release-build time; see the CI step below. rustc_version = "rustc 1.98.0 (hash date)" -acadrust_source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=#" +acadrust_source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=#" ``` The full, buildable scaffold is in [`docs/plugin-template/`](plugin-template); @@ -322,7 +322,7 @@ and `acadrust_source` placeholders from the CI run's actual toolchain and resolved dependency, rather than hand-editing them per release — see `docs/plugin-template/.github/workflows/release.yml` for the exact `sed` substitutions, including reading `acadrust_source` out of `Cargo.lock`'s -resolved `acadrust` package entry (`source = "git+...#"`) since that's +resolved `opencadcodec` package entry (`source = "git+...#"`) since that's the same value the host embeds at its own build time. The template's CI pins `rust-toolchain@stable`, which is a floating target — a real release pipeline should pin the exact toolchain version the host was built with (check diff --git a/docs/plugin-host-model-completion-plan.md b/docs/plugin-host-model-completion-plan.md index e56953166..73e24d2fb 100644 --- a/docs/plugin-host-model-completion-plan.md +++ b/docs/plugin-host-model-completion-plan.md @@ -6,10 +6,10 @@ Build a general Python document model over the OCS host API so a script can cont **State as of 21 September 2026.** The feature is host API v7. An earlier draft pull request, [#1391](https://github.com/HakanSeven12/OpenCADStudio/pull/1391), covered the first slice of it and was closed in favour of this more complete change. -- **Entities:** explicit writes for all 43 canvas kinds (41 creatable; the legacy `Polyline` and `Body` are update-only). **31 kinds are `Complete`** (every C/R/E/D/U/I/W/V/P gate passes). The remaining kinds are integration-tested and held short of `Complete` by a named blocker in the ledger: seven by cadcodec DXF bugs (fixes submitted as [cadcodec#48](https://github.com/HakanSeven12/cadcodec/pull/48), not yet merged, so OCS still pins the unfixed revision), Polyline3D by the DWG format, Polyline2D by a kind change on DXF save, RasterImage by an image-reactor structure that cannot be verified, and Body by having no creation path. +- **Entities:** explicit writes for all 43 canvas kinds (41 creatable; the legacy `Polyline` and `Body` are update-only). **31 kinds are `Complete`** (every C/R/E/D/U/I/W/V/P gate passes). The remaining kinds are integration-tested and held short of `Complete` by a named blocker in the ledger: seven by opencadcodec DXF bugs (fixes submitted as [opencadcodec#48](https://github.com/HakanSeven12/opencadcodec/pull/48), not yet merged, so OCS still pins the unfixed revision), Polyline3D by the DWG format, Polyline2D by a kind change on DXF save, RasterImage by an image-reactor structure that cannot be verified, and Body by having no creation path. - **Drawing tables:** layers, text and dimension styles, blocks and their contents, linetypes and layouts, through the additive `TableOperation` request (see the log entries dated 2026-09-21). - **Commands:** `HostApi::run_command` drives the real OCS commands step by step; `doc.command`, `doc.start_command` and `doc.modify` build on it. -- **Known open items:** the unfiled cadcodec findings (`cadcodec-reader-gaps.md`), complex (text and shape) linetypes, plot devices and named page setups, and PEDIT's remaining options. +- **Known open items:** the unfiled opencadcodec findings (`cadcodec-reader-gaps.md`), complex (text and shape) linetypes, plot devices and named page setups, and PEDIT's remaining options. Repositories: @@ -28,15 +28,15 @@ Host and adapter changes live in the same repository and are committed together. The earlier Phase 8 was **integration and compatibility**, not a new entity kind. `Insert` was added afterward as a separate coverage increment. Before marking any kind complete, establish an executable integration harness that exercises the actual plugin runner and IPC, not only direct Rust conversion helpers: -1. Stage the in-tree v7 plugin with `bash plugins/opencad-python/tools/stage-bundled.sh [--debug]`. Verify the staged `plugin.toml` says API v7 and its acadrust source and Rust compiler match OCS. Keep the repository-relative `ocs_plugin_api` path; never check in a machine-local path. +1. Stage the in-tree v7 plugin with `bash plugins/opencad-python/tools/stage-bundled.sh [--debug]`. Verify the staged `plugin.toml` says API v7 and its opencadcodec source and Rust compiler match OCS. Keep the repository-relative `ocs_plugin_api` path; never check in a machine-local path. 2. Through real IPC, create, inspect, edit, and delete a representative entity. Verify an invalid multi-entity transaction changes nothing, a valid transaction produces one undo entry, and undo **and redo** restore the expected geometry and references. 3. In the running GUI, exercise a real point pick, entity pick, cancellation/Escape, and plugin command cancellation. Verify drawing/selection/command notifications, including overflow recovery. 4. Repeat an input request and event poll across two tabs; verify token and notification isolation and cleanup when a tab closes. 5. Save and reopen representative edits as **DWG and DXF**, verifying the actual geometry and preserved unmapped fields. Use a real viewport/canvas check where the entity has visible geometry; a converter round trip alone does not satisfy this. -6. Record platform, OCS/acadrust revisions, fixture files, commands, results, and any engine limitation. Keep repeatable tests in the relevant repositories. +6. Record platform, OCS/opencadcodec revisions, fixture files, commands, results, and any engine limitation. Keep repeatable tests in the relevant repositories. Phase 8 is complete only when these checks pass through the bundled plugin and -real runner boundary. If a test is blocked by OCS or acadrust behavior, record +real runner boundary. If a test is blocked by OCS or opencadcodec behavior, record the specific failure and leave Phase 8 open. ## Per-kind completion gate @@ -52,9 +52,9 @@ Use one checklist row per kind in a coverage ledger. Status values: `unstarted`, 7. **Packaged build:** Run the in-tree adapter tests with `--locked`, stage the plugin with `stage-bundled.sh`, and load it from the macOS application resources through the separate runner process. The staged manifest must - match the host's Rust compiler and acadrust source. + match the host's Rust compiler and opencadcodec source. -If OCS/acadrust cannot create or serialize a kind reliably, mark it **blocked**, add a regression test or fixture demonstrating the failure, and move to the next independent kind. An explicit unsupported status is honest progress, not completion. +If OCS/opencadcodec cannot create or serialize a kind reliably, mark it **blocked**, add a regression test or fixture demonstrating the failure, and move to the next independent kind. An explicit unsupported status is honest progress, not completion. ## Existing mapped kinds: audit before claiming completion @@ -97,7 +97,7 @@ The last five may require new engine capabilities rather than only Python conver ## Work pattern for each coding increment -1. Inspect the acadrust entity struct, constructors, private fields, serializer, drawing path, and linked tables/objects. Record the intended editable/read-only/unmapped properties first. +1. Inspect the opencadcodec entity struct, constructors, private fields, serializer, drawing path, and linked tables/objects. Record the intended editable/read-only/unmapped properties first. 2. Put generic validation and transaction behavior in the host. Keep the Python adapter thin; add a manifest override only when the generator cannot safely express a field. 3. Add focused host validation, adapter conversion, document-model, real transaction/undo/redo, IPC, GUI, and DWG/DXF fixture checks. Record test evidence in the ledger, including which checks were manual. 4. Run `cargo test -p ocs_plugin_api --features host --lib` and the relevant @@ -113,7 +113,7 @@ The last five may require new engine capabilities rather than only Python conver - Added a V4 local-socket nested-request test for `UpdateEntitiesTransaction` using a simulated runner peer and a recording host. This proves the request and response cross the V4 IPC framing and reach `HostApi`; it does **not** prove the actual Python plugin runs through IPC. - Extended the OCS app-host Point batch test to assert redo after undo. This proves the real app history path for those edits; creation/deletion history remains untested by this case. - Staged the actual debug Python cdylib with `stage-bundled.sh`. Its generated - manifest declares API v7 and the exact OCS acadrust and Rust compiler + manifest declares API v7 and the exact OCS opencadcodec and Rust compiler fingerprints. The plugin now builds from `plugins/opencad-python` in this repository and ships from the macOS application resources. - The opt-in `staged_python_plugin_line_lifecycle_over_real_ipc` app test passed with the staged v7 Python plugin and built OCS executable. It creates, edits, and deletes a Line through Python over the actual runner IPC; confirms three undo entries and undo/redo; and writes/reopens the edited Line as DWG and DXF. It does not exercise GUI picking, multiple tabs, or cancellation. Re-run with `OCS_TEST_PYTHON_PLUGIN= OCS_PLUGIN_RUNNER_EXE= cargo test app::plugin_host::tests::staged_python_plugin_line_lifecycle_over_real_ipc --lib -- --test-threads=1 --nocapture` from the OCS checkout. @@ -127,14 +127,14 @@ The last five may require new engine capabilities rather than only Python conver - `staged_python_shape_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P through the actual runner, checks the real SHX linework before and after DWG/DXF reopen, and proves that invalid size/style edits remain atomic. The DXF renderer fallback now searches named `is_shape_file` styles as well as legacy unnamed styles. Shape is **Complete**; the queue now continues with `AttributeDefinition`. - Added creation-time `owner_handle` to the generated document-model adapter. It is present in snapshots and accepted by `doc.create_entity`, but rejected after creation; host transactions independently reject owner changes. Generic host validation now requires a non-null canvas owner to resolve to a block record, except `AttributeEntity`, whose owner may be an `Insert`. - Mapped stable AttributeDefinition fields and validated tag, placement, normal, height, width, angles, text style, line count and block ownership. `embedded_mtext` stays snapshot-readable but unmapped so partial Python edits preserve its linked R2018+ layout payload. -- `staged_python_attribute_definition_lifecycle_over_real_ipc` passes the real create/read/edit/delete/undo/selection/render/validation/packaged-build path using a structurally valid block and linked Insert. Full DWG state and core DXF geometry survive reopen; deletion stays absent after both formats. OCS also stopped double-converting ATTDEF/ATTRIB DXF rotation already converted by acadrust. -- AttributeDefinition remains **Integration-tested / DXF blocked**, not Complete: acadrust `568a12c`'s ATTDEF DXF reader did not restore optional AcDbText fields including width factor. Revalidate this recorded failure against the currently merged `7ea4247` revision before keeping or clearing the W-gate blocker. +- `staged_python_attribute_definition_lifecycle_over_real_ipc` passes the real create/read/edit/delete/undo/selection/render/validation/packaged-build path using a structurally valid block and linked Insert. Full DWG state and core DXF geometry survive reopen; deletion stays absent after both formats. OCS also stopped double-converting ATTDEF/ATTRIB DXF rotation already converted by opencadcodec. +- AttributeDefinition remains **Integration-tested / DXF blocked**, not Complete: opencadcodec `568a12c`'s ATTDEF DXF reader did not restore optional AcDbText fields including width factor. Revalidate this recorded failure against the currently merged `7ea4247` revision before keeping or clearing the W-gate blocker. - Closed `AttributeEntity` without flattening canonical drawing storage. V4 views and IPC snapshots expose each nested child by handle, while host create/update/delete and transactions rewrite the owning Insert so rendering, serialization and undo remain consistent. - `staged_python_attribute_entity_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P with the actual v7 adapter. It creates a linked attribute from its block's ATTDEF, reads and edits value/placement/rotation, selects the parent Insert, renders text, rejects invalid tag/height/owner edits atomically, reopens edited and deleted state in DWG and DXF, and proves three-step undo/redo. AttributeEntity is **Complete**; the queue now continues with `Hatch`. - Completed `Hatch` with full solid/pattern definitions and typed nested boundary paths. The adapter generator now handles single-payload tagged enums as `{kind, value}` dictionaries, which covers every Hatch edge variant and is reusable by later nested entity models. Unknown adapter properties are rejected instead of being silently ignored. - `staged_python_hatch_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P with closed line-loop fixtures: solid creation, conversion to a stored-line pattern, boundary replacement, selection and live render-cache checks, atomic invalid scale/association/open-loop/unmapped-payload rejection, edited/deleted DWG and DXF reopen, post-reopen edits, and three-step undo/redo. Hatch is **Complete**; the queue now continues with `Leader`. -- Mapped `Leader` (host validation and reference binding in `entity_coverage.rs`, policy/build.rs/manifest entries, adapter count 23). `staged_python_leader_lifecycle_over_real_ipc` passes C/R/E/D/U/I/V/P through the real runner. W is blocked by acadrust: the DXF writer omits the annotation handle (340), DXF reads lose `annotation_offset` (213), and DWG R2010+ does not store text size or hookline flag. Each gap is pinned by a canary assertion so it fails loudly once fixed. The queue now continues with `MLine`. -- Completed `MLine`. Host validation and style binding live in `ocs_plugin_api`; derived vertex geometry and element parameters are recomputed by the host (`normalize_scripted_mline`) on scripted create/edit, so scripts supply positions only. The adapter maps `flags` as an integer through a manifest override because the generator cannot classify acadrust's serde-newtype `MLineFlags`. `staged_python_mline_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P with DWG and DXF reopen. The queue now continues with `Dimension`. +- Mapped `Leader` (host validation and reference binding in `entity_coverage.rs`, policy/build.rs/manifest entries, adapter count 23). `staged_python_leader_lifecycle_over_real_ipc` passes C/R/E/D/U/I/V/P through the real runner. W is blocked by opencadcodec: the DXF writer omits the annotation handle (340), DXF reads lose `annotation_offset` (213), and DWG R2010+ does not store text size or hookline flag. Each gap is pinned by a canary assertion so it fails loudly once fixed. The queue now continues with `MLine`. +- Completed `MLine`. Host validation and style binding live in `ocs_plugin_api`; derived vertex geometry and element parameters are recomputed by the host (`normalize_scripted_mline`) on scripted create/edit, so scripts supply positions only. The adapter maps `flags` as an integer through a manifest override because the generator cannot classify opencadcodec's serde-newtype `MLineFlags`. `staged_python_mline_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P with DWG and DXF reopen. The queue now continues with `Dimension`. - Completed `Dimension` for all nine subtypes. Because the payload is an enum, the adapter generator gained a `manual_kinds` hook (hand-written `_to_dict/_from_dict/_apply`) and the host catalog a `synthetic` policy section. The host derives `actual_measurement` and the base definition point on every scripted create/edit. Python ints are now accepted wherever a coordinate or angle is expected. `staged_python_dimension_lifecycle_over_real_ipc` records each subtype separately and all nine pass C/R/E/D/U/I/W/V/P. The queue now continues with `MultiLeader`. - Completed `MultiLeader`. The adapter generator now converts `Color`, `LineWeight`, mixed unit/payload enums, fixed float arrays and bitflags-2 newtypes, and a `keep_types` override lets a hand-written setter reuse generated converters (used for the merge-style `context` patch). `Leader.override_color` became writable through the same `Color` form; neither file format stores it. `staged_python_multileader_lifecycle_over_real_ipc` passes the full gate with DWG and DXF reopen. The queue now continues with `Table`. - Completed `Table`. The generator now handles tuples and `usize`; the host keeps DXF cell merge dimensions in step with `merged_ranges`. `staged_python_table_lifecycle_over_real_ipc` passes the full gate with DWG and DXF reopen. The queue now continues with `PolygonMesh`. @@ -146,26 +146,26 @@ The last five may require new engine capabilities rather than only Python conver - Mapped `Viewport` with a paper-space owner rule and frozen-layer reference validation. Every gate except a paper-space canvas oracle passes through the shared driver; the kind stays integration-tested. The queue now continues with `ViewBorder`. - Mapped `ViewBorder` with viewport and scale reference rules. All gates pass in DWG; DXF loading does not restore the typed entity (pinned by a canary). The queue now continues with `SectionSymbol`. - Recorded `SectionSymbol` (queue item 15) as **blocked**: it needs a section view style and view representation the host cannot create, and its raw point-count fields have no documented derivation. Mapped `Light` (item 16); every gate passes except DXF `cast_shadows`, which the reader drops (pinned by a canary). The queue now continues with `Region`. -- Recorded `Region`, `Body`, `Solid3D`, `Surface` (queue items 17-20) and `Ole2Frame` (item 21) as **blocked**. The ACIS kinds need a host-side kernel-backed create/transform API that regenerates the payload, wires and silhouettes; the OLE frame needs a valid embedded-storage fixture. No opaque payload is rewritten. With this the ordered queue is complete: 12 kinds are fully complete, Leader, Helix, Underlay, ViewBorder and Light are mapped but blocked at DWG/DXF persistence by acadrust gaps, RasterImage and Viewport lack an oracle or linkage, and six kinds are blocked. Next work is the engine fixes behind the canary assertions, the mapped-kind audit of the 17 pre-existing kinds, and merging `origin/main`. +- Recorded `Region`, `Body`, `Solid3D`, `Surface` (queue items 17-20) and `Ole2Frame` (item 21) as **blocked**. The ACIS kinds need a host-side kernel-backed create/transform API that regenerates the payload, wires and silhouettes; the OLE frame needs a valid embedded-storage fixture. No opaque payload is rewritten. With this the ordered queue is complete: 12 kinds are fully complete, Leader, Helix, Underlay, ViewBorder and Light are mapped but blocked at DWG/DXF persistence by opencadcodec gaps, RasterImage and Viewport lack an oracle or linkage, and six kinds are blocked. Next work is the engine fixes behind the canary assertions, the mapped-kind audit of the 17 pre-existing kinds, and merging `origin/main`. - Audited the 17 pre-existing mapped kinds through the shared real-runner driver (`audit_python_*`). The audit found and fixed genuine validation gaps: Ellipse had none, Text and MText accepted a zero height, Arc accepted non-finite angles, Point and Line accepted infinite thickness, and most basic kinds had no creation-time validation. A shared `validate_basic_entity` now covers Point, Line, Circle, Arc, Ellipse, Text, MText, LwPolyline, Polyline, Polyline2D, Polyline3D and Spline, refusing only problems an edit introduces so already-invalid legacy values stay editable. Twelve of the 17 kinds now pass every gate (Point, Line, Circle, Arc, Ellipse, LwPolyline, Text, MText, Ray, XLine, Solid, Face3D). Four expose persistence findings pinned by canaries (Polyline reopens as Polyline3D; Polyline2D reopens as LwPolyline in DXF; Polyline3D loses elevation in DWG; Spline loses weights in DXF), and Insert is update-only with all applicable gates passing. The driver gained per-format DWG expectations and a host-made block fixture for Insert. -- Audited `AttributeDefinition` against acadrust `5b682ed` with a full-field comparison through both formats. Everything passes in DWG. DXF keeps only tag, prompt, value, insertion point, height and rotation because the reader handles eight group codes; alignment, width factor, oblique angle, attribute flags, field length, generation flags, lock and text style reopen as defaults. The kind stays integration-tested and the gap is pinned by canary expectations; the fix belongs in cadcodec's `read_attdef`. The shared driver's block fixture now also serves `MAKEBLOCK` scripts. +- Audited `AttributeDefinition` against opencadcodec `5b682ed` with a full-field comparison through both formats. Everything passes in DWG. DXF keeps only tag, prompt, value, insertion point, height and rotation because the reader handles eight group codes; alignment, width factor, oblique angle, attribute flags, field length, generation flags, lock and text style reopen as defaults. The kind stays integration-tested and the gap is pinned by canary expectations; the fix belongs in opencadcodec's `read_attdef`. The shared driver's block fixture now also serves `MAKEBLOCK` scripts. - Decided the two open audit questions. **Insert is creatable** by naming an existing block (existence, ordinary-block and no-cycle checks; attribute records stay with the AttributeEntity flow), which completes the kind. **Legacy Polyline is update-only** with a hint pointing scripts at Polyline2D and Polyline3D, because both formats convert it on save. The generator gained an `update_only_message` for such kinds, integer coordinates are accepted in Insert scales, and the driver gained `MAKEPOLYLINE` and `MAKEBLOCK` fixtures. -- Wrote up the cadcodec DXF reader gaps as draft issues in [cadcodec-reader-gaps.md](cadcodec-reader-gaps.md) (nothing filed): ATTDEF, boolean groups read through `as_i16()`, UNDERLAY rotation units, LEADER, DRAWINGVIEW/SECTIONLINE dispatch and TABLE merged ranges. Verifying them corrected an earlier attribution: the Spline "DXF drops weights" finding was an OCS bug (the writer emits weights only for a rational spline), now fixed by setting `flags.rational` from the weights, so Spline is complete. +- Wrote up the opencadcodec DXF reader gaps as draft issues in [cadcodec-reader-gaps.md](cadcodec-reader-gaps.md) (nothing filed): ATTDEF, boolean groups read through `as_i16()`, UNDERLAY rotation units, LEADER, DRAWINGVIEW/SECTIONLINE dispatch and TABLE merged ranges. Verifying them corrected an earlier attribution: the Spline "DXF drops weights" finding was an OCS bug (the writer emits weights only for a rational spline), now fixed by setting `flags.rational` from the weights, so Spline is complete. - Mapped `Ole2Frame` as update-only (queue item 21, previously blocked). OCS already builds real embedded frames (`build_embedded_ole`), which supplied the fixture the blocker was waiting for. Frame corners and the aspect lock are scriptable; the opaque storage and envelope are excluded and refused on edit. The audit proves the embedded picture survives DWG and DXF unchanged. Creation from Python remains a follow-up. The remaining blocked kinds are SectionSymbol, Region, Body, Solid3D and Surface. -- Investigated the cadkernel path for the ACIS kinds ([cadkernel-body-path.md](cadkernel-body-path.md)). The earlier "blocked" reasoning was overstated: OCS already builds primitives, transforms bodies and runs booleans through cadkernel, and `solid_to_sat` plus `kernel_acis_body` verify each result lifts back with no loss. Two `#[ignore]` spike tests measured exact volumes and DWG and DXF round trips for a box, moved, rotated and mirrored copies, three booleans, four other primitives and a `Body` entity. The real gap is an operation channel from a script to that pipeline; the note recommends an additive `solid_operation` host method and a phased plan (Solid3D create and transform, then booleans and Body, then Region, then Surface). Booleans took about 23 s each in a debug build and need release timing first. Region, Body, Solid3D and Surface move from blocked to unblocked-by-design; SectionSymbol stays blocked. -- Built the cadkernel operation channel and completed `Solid3D` (create and rigid move). `HostApi::solid_operation` is additive (default "unsupported"), so older hosts and plugins are unaffected; it travels as `PluginRequest::SolidOperation` and `PluginResponse::SolidResult`, and the host records its own undo step only after validation so a refused operation leaves nothing behind. The Python surface is `doc.solids.*`. Every result is verified lossless by the kernel path before it is accepted and a payload that will not lift is refused untouched. `Body` moves through the same channel and `Region` is accepted but untested. Booleans, Region creation from curves and Surface remain, in that order; booleans wait on release-build timing. Solid3D, Body and Region are now mapped read-only with the opaque payload excluded and preserved. +- Investigated the opencadkernel path for the ACIS kinds ([cadkernel-body-path.md](cadkernel-body-path.md)). The earlier "blocked" reasoning was overstated: OCS already builds primitives, transforms bodies and runs booleans through opencadkernel, and `solid_to_sat` plus `kernel_acis_body` verify each result lifts back with no loss. Two `#[ignore]` spike tests measured exact volumes and DWG and DXF round trips for a box, moved, rotated and mirrored copies, three booleans, four other primitives and a `Body` entity. The real gap is an operation channel from a script to that pipeline; the note recommends an additive `solid_operation` host method and a phased plan (Solid3D create and transform, then booleans and Body, then Region, then Surface). Booleans took about 23 s each in a debug build and need release timing first. Region, Body, Solid3D and Surface move from blocked to unblocked-by-design; SectionSymbol stays blocked. +- Built the opencadkernel operation channel and completed `Solid3D` (create and rigid move). `HostApi::solid_operation` is additive (default "unsupported"), so older hosts and plugins are unaffected; it travels as `PluginRequest::SolidOperation` and `PluginResponse::SolidResult`, and the host records its own undo step only after validation so a refused operation leaves nothing behind. The Python surface is `doc.solids.*`. Every result is verified lossless by the kernel path before it is accepted and a payload that will not lift is refused untouched. `Body` moves through the same channel and `Region` is accepted but untested. Booleans, Region creation from curves and Surface remain, in that order; booleans wait on release-build timing. Solid3D, Body and Region are now mapped read-only with the opaque payload excluded and preserved. - Completed `Region`. `SolidOperation::RegionFromProfile` reuses the REGION command's kernel calls and `doc.solids.region` exposes it; the lifecycle test creates regions from real circle and polyline entities and covers deletion of the source, refusals, a move, both formats and undo. Along the way `create_entity` now accepts `(x, y, z)` tuples for point properties (decided from the schema's property types) and every generated float setter accepts Python ints, so `create_entity('Circle', center=(0, 0, 0), radius=5)` works. Left: booleans (release timing first), holes and open-curve merging for regions, and `Surface`. - Completed `Surface` for planes, extruded open profiles, isolines and rigid moves. `SurfaceFromProfile` and `Extrude` join the kernel operations (`doc.solids.surface` and `doc.solids.extrude`); `Extrude` also gives Solid3D extrusions from closed profiles, checked against analytic volumes. The test found and fixed a real bug: Surface's `kind` field silently overwrote the entity-type `kind` key in Python dicts, so it is exposed as `surface_kind`, and a new uniqueness test guards the whole catalog. Transform now accepts surfaces (a plane stays a plane; other kinds turn generic). Left: booleans (release timing first), regions with holes, and lofted, revolved, swept and NURBS surfaces. SectionSymbol is the only unmapped kind. - Timed booleans in a release build (details in [cadkernel-body-path.md](cadkernel-body-path.md)). A curved box-and-cylinder boolean takes about 1 to 2.2 s, overlapping spheres 0.2 s and planar operands 1 to 2 ms, against 23 s in a debug build. The kernel refuses many curved cases quickly with a reason (`Coincident`, `CutRefused`, `NoClosedForm`), including a second cut in an already drilled box. Recommendation: add a synchronous `SolidOperation::Boolean` that surfaces the refusal and leaves the operands untouched, test it with planar operands plus one ignored curved case, and note the 30 s per-script budget. No code changed in this step. -- Implemented `SolidOperation::Boolean` and `doc.solids.union`, `subtract` and `intersect`, as recommended by the timing study. It runs synchronously, surfaces the kernel's refusal (`Coincident`, `CutRefused`, `NoClosedForm`) with a plain-language reason and the words "nothing was changed", consumes the operands unless `keep_operands`, and records one undo step. The lifecycle test uses planar boxes (millisecond cost, exact volumes, debug-safe) and an `#[ignore]` test documents a curved refusal. With this the kernel operations in the cadkernel note are all built. SectionSymbol is the only unmapped kind. -- Mapped `SectionSymbol`, the last unmapped kind, so all 43 canvas kinds now have explicit writes. The blocker recorded earlier was overstated: cadcodec documents `points` as canonical, says the counts equal the point count in every verified export, and provides `sync_display_fields()`, which OCS's own edits already call; OCS also renders a symbol with no style or view link. The host derives the counts and projections when `points` changes and validates that references are existing `SectionViewStyle` and `ViewRep` class objects. Every gate passes except DXF, where cadcodec reopens a top-level `SECTIONLINE` as an unknown entity (issue 5 in the cadcodec write-up, pinned by a canary). Still unverifiable here: the meaning of `raw_flags_90` and AutoCAD's acceptance of a symbol with no parent view. +- Implemented `SolidOperation::Boolean` and `doc.solids.union`, `subtract` and `intersect`, as recommended by the timing study. It runs synchronously, surfaces the kernel's refusal (`Coincident`, `CutRefused`, `NoClosedForm`) with a plain-language reason and the words "nothing was changed", consumes the operands unless `keep_operands`, and records one undo step. The lifecycle test uses planar boxes (millisecond cost, exact volumes, debug-safe) and an `#[ignore]` test documents a curved refusal. With this the kernel operations in the opencadkernel note are all built. SectionSymbol is the only unmapped kind. +- Mapped `SectionSymbol`, the last unmapped kind, so all 43 canvas kinds now have explicit writes. The blocker recorded earlier was overstated: opencadcodec documents `points` as canonical, says the counts equal the point count in every verified export, and provides `sync_display_fields()`, which OCS's own edits already call; OCS also renders a symbol with no style or view link. The host derives the counts and projections when `points` changes and validates that references are existing `SectionViewStyle` and `ViewRep` class objects. Every gate passes except DXF, where opencadcodec reopens a top-level `SECTIONLINE` as an unknown entity (issue 5 in the opencadcodec write-up, pinned by a canary). Still unverifiable here: the meaning of `raw_flags_90` and AutoCAD's acceptance of a symbol with no parent view. - Completed `Ole2Frame` by adding creation. `SolidOperation::EmbedPicture` and `doc.embed_picture` build the frame from a picture file with OCS's own embedding code, so a script never touches the opaque storage; PNG, JPEG and BMP are stored byte-for-byte and other formats are re-encoded as PNG. The lifecycle test now creates through Python and the new creation-rules test covers formats, aspect, origin, layer and seven refusals. 30 kinds are complete. - Completed `Viewport` by closing its missing canvas oracle and its derived state. The shared driver now switches the scene to the layout that owns a viewport and requires a drawn frame. The host assigns a unique id (at least 2) and derives `custom_scale = height / view_height` on every scripted create or edit, as MVIEW does, so `custom_scale` became read-only. A control showed my earlier suspicion was wrong: the default id 0 still draws a frame in a layout with no sheet viewport, so the id matters for uniqueness, not visibility. 31 kinds are complete. -- Improved `RasterImage` linkage. A scripted image now reuses one definition per file and registers it in `ACAD_IMAGE_DICT`, which owns it; this persists through DWG and DXF. The `IMAGEDEF_REACTOR` is deliberately not written: its owner convention is ambiguous between cadcodec's struct docs and its DXF writer and cannot be checked against AutoCAD here, so the kind stays integration-tested. An exploratory spike also showed that OCS's own IMAGE command creates bare images that lose their file path on save, an OCS bug outside this plugin work that is flagged separately. +- Improved `RasterImage` linkage. A scripted image now reuses one definition per file and registers it in `ACAD_IMAGE_DICT`, which owns it; this persists through DWG and DXF. The `IMAGEDEF_REACTOR` is deliberately not written: its owner convention is ambiguous between opencadcodec's struct docs and its DXF writer and cannot be checked against AutoCAD here, so the kind stays integration-tested. An exploratory spike also showed that OCS's own IMAGE command creates bare images that lose their file path on save, an OCS bug outside this plugin work that is flagged separately. ### 2026-09-21: beyond entities, the layer table (`doc.layers`) -Entity CRUD is at its ceiling without the cadcodec fixes (PR HakanSeven12/cadcodec#48), so work moved to +Entity CRUD is at its ceiling without the opencadcodec fixes (PR HakanSeven12/opencadcodec#48), so work moved to drawing objects a script needs. New additive v7 request `TableOperation` (`LayerCreate`, `LayerModify`, `LayerRename`, `LayerDelete { erase_objects }`, `LayerSetCurrent`) with `HostApi::table_operation`, executed by `HostSession::table_operation`. Every refusal (invalid or duplicate name, bad color, @@ -186,7 +186,7 @@ reference-following rename) by widening four `style_ops` methods to `pub(super)` renamed text style's name follow into dimension styles. Style operations require the active tab. Evidence: `audit_python_text_and_dim_styles_over_real_ipc` (26 refusals, live/DWG/DXF persistence, copy semantics, rename following references, current/delete guards, five-step undo), IPC round-trip and Python unit -tests. Four cadcodec findings (STYLE flags and oblique units, DIMSTYLE name, `true_type_font`) are pinned by +tests. Four opencadcodec findings (STYLE flags and oblique units, DIMSTYLE name, `true_type_font`) are pinned by canaries and listed in `cadcodec-reader-gaps.md`. Next: linetypes, blocks, layouts; then headless commands. ### 2026-09-21: blocks (`doc.blocks`) @@ -199,7 +199,7 @@ tables). Creation reuses `Scene::define_block_from_owned_entities`. Python: `blo lookup and iteration, records with member handles and kinds. Evidence: `audit_python_blocks_over_real_ipc` (18 refusals; shifted geometry and base point; inserts following a rename; original kept vs erased; live/DWG/DXF persistence; guarded delete; three-step undo restoring the definitions), IPC round-trip and Python -unit tests. One more cadcodec finding (DXF drops the block description) is pinned by a canary. Not covered: +unit tests. One more opencadcodec finding (DXF drops the block description) is pinned by a canary. Not covered: editing a definition's contents in place, and attribute definitions inside a scripted block. Next: linetypes and layouts, then headless commands. @@ -303,9 +303,9 @@ to a helper thread would be unsound here (the editor state is not `Send` and som a stack-growing dependency such as `stacker`, adds a crate to H7's tree to guard a case that cannot occur. The 64 MiB thread in the PEDIT audit stays, because a test thread gets only 2 MiB. -### 2026-09-21: cadcodec PR #51 (text and dimension style DXF fixes) +### 2026-09-21: opencadcodec PR #51 (text and dimension style DXF fixes) -Second small upstream PR, [HakanSeven12/cadcodec#51](https://github.com/HakanSeven12/cadcodec/pull/51): STYLE generation +Second small upstream PR, [HakanSeven12/opencadcodec#51](https://github.com/HakanSeven12/opencadcodec/pull/51): STYLE generation flags (group 71 was hard-coded to 0), the STYLE oblique angle (group 50 is degrees; it was written and read as raw radians) and the DIMSTYLE text-style name (resolved from the group 340 handle). Three commits, three regression tests, independent of #48 (merged locally with it; the combined suite passes). Not included: `true_type_font` and the block description, which need diff --git a/docs/plugin-host-model-coverage-ledger.md b/docs/plugin-host-model-coverage-ledger.md index 4f0555b45..1b4d57fad 100644 --- a/docs/plugin-host-model-coverage-ledger.md +++ b/docs/plugin-host-model-coverage-ledger.md @@ -2,9 +2,9 @@ Use with [the completion plan](plugin-host-model-completion-plan.md). This ledger is current as of 23 September 2026 (the object tables, block contents and command runner added since are described in [the design note](plugin-host-model.md) and the plan's progress log; they are not entity kinds): **43 of 43 canvas kinds have explicit geometry writes** (41 creatable, plus update-only legacy `Polyline` and `Body`), **36 kinds are complete**, and none remains layer-only. `Mapped` means at least one non-layer field is exposed for Python writes; it does **not** mean the kind passes the completion gate. `Update-only` means Python creation is unavailable. `Layer-only` means raw inspection and canvas layer edits exist, but geometry is not mapped. Record actual evidence or a blocking issue before changing any status to `Complete`. -**Rows that were blocked at W by cadcodec.** The DXF fixes they waited on landed as [HakanSeven12/cadcodec#48](https://github.com/HakanSeven12/cadcodec/pull/48) and [#51](https://github.com/HakanSeven12/cadcodec/pull/51), and OCS pins cadcodec `dd1d7bf` since 23 September 2026. The canaries flipped in the real-IPC audits and Helix, Underlay, ViewBorder, SectionSymbol and Light moved to `Complete`. Two rows keep a narrower blocker: AttributeDefinition (the DXF writer emits no group 280, so `lock_position` is lost) and Leader (a true-colour `override_color`, which DXF group 77 cannot hold and the DWG writer does not store). +**Rows that were blocked at W by opencadcodec.** The DXF fixes they waited on landed as [HakanSeven12/opencadcodec#48](https://github.com/HakanSeven12/opencadcodec/pull/48) and [#51](https://github.com/HakanSeven12/opencadcodec/pull/51), and OCS pins opencadcodec `dd1d7bf` since 23 September 2026. The canaries flipped in the real-IPC audits and Helix, Underlay, ViewBorder, SectionSymbol and Light moved to `Complete`. Two rows keep a narrower blocker: AttributeDefinition (the DXF writer emits no group 280, so `lock_position` is lost) and Leader (a true-colour `override_color`, which DXF group 77 cannot hold and the DWG writer does not store). -Completion gate codes: **C** = create through Python document model; **R** = read; **E** = edit defining geometry and another property; **D** = delete; **U** = undo/redo and atomic failure; **I** = real IPC plus GUI/canvas pick or observable behavior; **W** = DWG and DXF save/reopen; **V** = invalid-input checks and untouched-field/reference preservation; **P** = build and load the in-tree bundled plugin with a manifest matching the host toolchain and acadrust source. A kind is `Complete` only when **C/R/E/D/U/I/W/V/P** all pass and its property catalog matches implementation. `Blocked` requires a specific dependency or failing fixture. +Completion gate codes: **C** = create through Python document model; **R** = read; **E** = edit defining geometry and another property; **D** = delete; **U** = undo/redo and atomic failure; **I** = real IPC plus GUI/canvas pick or observable behavior; **W** = DWG and DXF save/reopen; **V** = invalid-input checks and untouched-field/reference preservation; **P** = build and load the in-tree bundled plugin with a manifest matching the host toolchain and opencadcodec source. A kind is `Complete` only when **C/R/E/D/U/I/W/V/P** all pass and its property catalog matches implementation. `Blocked` requires a specific dependency or failing fixture. | Kind | Baseline | Gate evidence / blocker | |---|---|---| @@ -17,7 +17,7 @@ Completion gate codes: **C** = create through Python document model; **R** = rea | Polyline2D | **Integration-tested / kind changes on DXF save** | Exercised: `vertices`, `closed`, `thickness`. Passes every gate in DWG. DXF reopens a 2-D polyline as an `LwPolyline` (vertices, closure and width survive, but the kind changes and `thickness` is not part of that record's digest here); pinned by a canary expectation. `audit_python_polyline2d_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). | | Polyline3D | **Integration-tested / blocked at W by the DWG format (elevation)** | Exercised: `vertices`, `elevation`. Passes every gate in DXF; DWG does not store a 3-D polyline's `elevation`, so it reopens as 0 (pinned by a canary). Vertices persist. `audit_python_polyline3d_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). | | LwPolyline | **Complete** | Exercised: `vertices` (with a bulge), `is_closed`, `constant_width`. Rejects fewer than 2 vertices, non-finite locations, bulges and widths, a negative constant width and a zero normal. `audit_python_lwpolyline_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). | -| Spline | **Complete** | Exercised: `control_points`, `weights`. Validation covers degree, control-point count, knot count and order, weights and finite points. The host sets `flags.rational` whenever weights are present, because the DXF writer emits group 41 only for a rational spline; the audit first showed weights vanishing in DXF and traced it to that missing flag (an OCS bug, not a cadcodec one), now fixed. `audit_python_spline_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P with DWG and DXF reopen, re-edit, atomic rollback and three-step undo/redo. | +| Spline | **Complete** | Exercised: `control_points`, `weights`. Validation covers degree, control-point count, knot count and order, weights and finite points. The host sets `flags.rational` whenever weights are present, because the DXF writer emits group 41 only for a rational spline; the audit first showed weights vanishing in DXF and traced it to that missing flag (an OCS bug, not a opencadcodec one), now fixed. `audit_python_spline_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P with DWG and DXF reopen, re-edit, atomic rollback and three-step undo/redo. | | Text | **Complete** | Exercised: `text`, `rotation`, `height`. Rejects a non-positive height (previously accepted), a non-positive width factor, non-finite placement and angles, and a zero normal. Style and alignment fields are mapped but not individually exercised. `audit_python_text_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). | | MText | **Complete** | Exercised: `text`, `rectangle_width`, `rotation`. Rejects a non-positive height (previously accepted), a negative width, non-finite placement, a non-positive line-spacing factor and a zero normal. Attachment, columns and background fields are mapped but not individually exercised. `audit_python_mtext_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). | | Ray | **Complete** | Exercised: `base_point`, `direction`. Unit-direction and finite-point validation already existed; the audit added the full DWG and DXF reopen and re-edit gate. `audit_python_ray_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). | @@ -27,28 +27,28 @@ Completion gate codes: **C** = create through Python document model; **R** = rea | Insert | **Complete** | Decision (2026-09-20): Insert is creatable. A script names an existing block with `block_name` and a placement; the host does not generate attribute records, because attributes stay with the `AttributeEntity` flow that already creates them from the block's ATTDEFs. Writable: `block_name` (fixed after creation; the mutation check refuses a different block), `insert_point`, `x_scale`, `y_scale`, `z_scale`, `rotation`, `normal`, `column_count`, `row_count`, `column_spacing`, `row_spacing`. Creation and rename rules: the block must exist, must not be model or paper space, and must not make a block contain itself directly or through nested inserts; scales must be finite and nonzero, array counts positive, and the name non-empty. An edit that keeps the block name is exempt from the block checks so a legacy insert of a missing block stays editable. `audit_python_insert_lifecycle_over_real_ipc` (shared driver, host-made block) passes C/R/E/D/U/I/W/V/P: creation by block name, a six-property transaction edit, canvas wires, rejection of zero scale, non-finite rotation and point, zero array count, zero normal and an unknown block, an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. `audit_python_insert_creation_rules_over_real_ipc` covers unknown block, model-space block, bad scale, zero count, empty name, direct self-insertion and indirect nesting cycles. Not covered: MINSERT (`dwg_minsert`) and attached-attribute creation, which stay excluded and preserved. | | Tolerance | **Complete** | `staged_python_tolerance_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P with the staged API v7 plugin: high-level create/read/transaction edit/delete; insertion, direction, and text edits; live canvas selection; grouped undo/redo; invalid direction and missing style atomic rollback; exact untouched common/style/private-field preservation; and edited DWG plus DXF reopen. Host-owned coverage exposes placement, direction, normal, text, style name, height and gap; style handle is read-only and the undocumented DWG short remains unmapped. `tolerance_geometry_and_style_references_are_validated` and the existing Tolerance tessellation tests cover reference and rendered-frame behavior. | | Shape | **Complete** | `staged_python_shape_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P with a generated parser-valid SHX file and a real shape-file `TextStyle`: high-level create/read/transaction edit/delete; insertion, size, rotation and width-factor edits; live canvas selection; grouped undo/redo; invalid size and missing style rollback; exact identity/style/common-field preservation; and DWG plus DXF reopen. Each live and reopened document renders the three-point SHX glyph rather than the placeholder. Host-owned reference binding writes the stable style handle needed by DWG. The DXF fallback now searches all `is_shape_file` styles, preserving named-style glyph resolution after reopen. | -| AttributeDefinition | **Integration-tested / blocked at W by cadcodec (DXF ATTDEF lock)** | Host commit `5b854bd1` maps block ownership plus every stable ATTDEF field except the nested `embedded_mtext` payload, which is snapshot-readable and preserved untouched. Two staged tests cover it. `staged_python_attribute_definition_lifecycle_over_real_ipc` passes with a real block record, Block/BlockEnd markers and a linked Insert. The 2026-09-20 audit `audit_python_attribute_definition_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, host-made block) compares **every writable field** through both formats: creation with non-default tag, prompt, value, placement, alignment point, height, rotation, width factor, oblique angle, alignments, all attribute flags, field length, generation flags and lock; a nine-property transaction edit; canvas wires; eight rejection cases (whitespace tag, non-positive height and width factor, missing style, owner change, zero normal, non-finite rotation, unknown alignment) plus an atomic rollback; DWG reopen with the **complete** edited state and re-edit; deletion; and three-step undo/redo. **W blocker, revalidated on cadcodec `dd1d7bf`:** the reader now handles every ATTDEF group and all fields round-trip through DXF except `lock_position`, because the DXF writer emits no group 280 (neither the version byte nor the lock flag after it). Pinned by canary expectations for the edit and the re-edit. Not `Complete` until `write_attdef` emits group 280. `embedded_mtext` mutation remains unsupported pending linked-layout validation. | +| AttributeDefinition | **Integration-tested / blocked at W by opencadcodec (DXF ATTDEF lock)** | Host commit `5b854bd1` maps block ownership plus every stable ATTDEF field except the nested `embedded_mtext` payload, which is snapshot-readable and preserved untouched. Two staged tests cover it. `staged_python_attribute_definition_lifecycle_over_real_ipc` passes with a real block record, Block/BlockEnd markers and a linked Insert. The 2026-09-20 audit `audit_python_attribute_definition_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, host-made block) compares **every writable field** through both formats: creation with non-default tag, prompt, value, placement, alignment point, height, rotation, width factor, oblique angle, alignments, all attribute flags, field length, generation flags and lock; a nine-property transaction edit; canvas wires; eight rejection cases (whitespace tag, non-positive height and width factor, missing style, owner change, zero normal, non-finite rotation, unknown alignment) plus an atomic rollback; DWG reopen with the **complete** edited state and re-edit; deletion; and three-step undo/redo. **W blocker, revalidated on opencadcodec `dd1d7bf`:** the reader now handles every ATTDEF group and all fields round-trip through DXF except `lock_position`, because the DXF writer emits no group 280 (neither the version byte nor the lock flag after it). Pinned by canary expectations for the edit and the re-edit. Not `Complete` until `write_attdef` emits group 280. `embedded_mtext` mutation remains unsupported pending linked-layout validation. | | AttributeEntity | **Complete** | OCS commit `cf2a9235` and adapter commit `9c6b5c3` make nested ATTRIB records addressable by handle while retaining canonical storage in `Insert.attributes`. Host validation binds tag to a definition in the Insert's block, validates placement/text geometry and style references, keeps owner and definition handles read-only, and preserves embedded MTEXT. `staged_python_attribute_entity_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P with a real block, ATTDEF and Insert: high-level create/read/transaction edit/delete, parent selection, rendered text, atomic invalid tag/height/owner rejection, edited and deleted DWG/DXF reopen, and three-step undo/redo. `nested_insert_attribute_crud_uses_parent_storage_and_undo` additionally proves the child never becomes an orphan flat entity in the host document. | | Hatch | **Complete** | OCS commit `98b56ef6` and adapter commit `a9b5de8` expose solid and patterned Hatches with elevation/normal, complete pattern definitions, pattern controls, style, association state, boundary paths and seed points. Nested tagged boundary edges use the reusable `{kind, value}` adapter form. Host validation requires closed finite loops, validates line/arc/ellipse/polyline/spline payloads and stored pattern lines, resolves associative boundary handles, and preserves gradient and MPOLYGON payloads as explicitly unsupported. `staged_python_hatch_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P: Python solid creation, patterned boundary edit, live render-model pattern/boundary checks, selection, atomic invalid scale/association/open-loop/unmapped-property rejection, edited and deleted DWG/DXF reopen, post-reopen edits, and three-step undo/redo. | -| Leader | **Integration-tested / W open items (DWG-derived text fields, true-colour override)** | Fields mapped writable: `vertices` (≥2 finite points, not all coincident), `annotation_handle`, `creation_type`, `path_type`, `dimension_style`, `arrow_enabled`, `hookline_enabled`, `hookline_direction`, `text_height`, `text_width`, `normal`, `horizontal_direction`, `block_offset`, `annotation_offset`. Read-only: `origin`, `dimension_gap`, `arrowhead_type`, `arrow_size`, `byblock_color`. `override_color` is writable through the new generator `Color` form (`{'kind': 'Rgb', 'value': {'r','g','b'}}`, `Index`, `ByLayer`, `ByBlock`, `None`). Unmapped but preserved on edit: the `dwg_unknown_*` fields. Host reference binding validates that `annotation_handle` exists, is not the leader itself, matches `creation_type` (Text/MText, Tolerance, Insert; `NoAnnotation` forbids a handle) and that `dimension_style` exists; nothing is silently defaulted. `staged_python_leader_lifecycle_over_real_ipc` passes C/R/E/D/U/I/V/P against acadrust `7ea4247`: linked Text annotation, path/height/arrow/offset transaction edit with untouched-field preservation, GUI pick grouping (scripted selection stays exact), seven single-edit rejections plus a cross-entity atomic rollback, four-step undo/redo, real render linework, DWG and DXF reopen and re-edit. Erasing a leader also erases its annotation (scene leader grouping); undo restores both. **W:** (1) the DXF annotation link (group 340) and (2) `annotation_offset` (group 213) round-trip since cadcodec `dd1d7bf`. Still open: (3) DWG R2010+ does not store `text_height`, `text_width` or `hookline_enabled` (acadrust writes them only for R13–R2007 / R13–R14), which needs a decision to document or make read-only; (4) a true-colour `override_color` reopens as `ByLayer` in both formats — DXF group 77 holds an ACI index only, and the DWG writer does not store it (canary assertion in the test). | +| Leader | **Integration-tested / W open items (DWG-derived text fields, true-colour override)** | Fields mapped writable: `vertices` (≥2 finite points, not all coincident), `annotation_handle`, `creation_type`, `path_type`, `dimension_style`, `arrow_enabled`, `hookline_enabled`, `hookline_direction`, `text_height`, `text_width`, `normal`, `horizontal_direction`, `block_offset`, `annotation_offset`. Read-only: `origin`, `dimension_gap`, `arrowhead_type`, `arrow_size`, `byblock_color`. `override_color` is writable through the new generator `Color` form (`{'kind': 'Rgb', 'value': {'r','g','b'}}`, `Index`, `ByLayer`, `ByBlock`, `None`). Unmapped but preserved on edit: the `dwg_unknown_*` fields. Host reference binding validates that `annotation_handle` exists, is not the leader itself, matches `creation_type` (Text/MText, Tolerance, Insert; `NoAnnotation` forbids a handle) and that `dimension_style` exists; nothing is silently defaulted. `staged_python_leader_lifecycle_over_real_ipc` passes C/R/E/D/U/I/V/P against opencadcodec `7ea4247`: linked Text annotation, path/height/arrow/offset transaction edit with untouched-field preservation, GUI pick grouping (scripted selection stays exact), seven single-edit rejections plus a cross-entity atomic rollback, four-step undo/redo, real render linework, DWG and DXF reopen and re-edit. Erasing a leader also erases its annotation (scene leader grouping); undo restores both. **W:** (1) the DXF annotation link (group 340) and (2) `annotation_offset` (group 213) round-trip since opencadcodec `dd1d7bf`. Still open: (3) DWG R2010+ does not store `text_height`, `text_width` or `hookline_enabled` (opencadcodec writes them only for R13–R2007 / R13–R14), which needs a decision to document or make read-only; (4) a true-colour `override_color` reopens as `ByLayer` in both formats — DXF group 77 holds an ACI index only, and the DWG writer does not store it (canary assertion in the test). | | MLine | **Complete** | Writable: `vertices` (raw vertex dicts; a script may pass only `position`), `flags` (integer, unknown bits rejected), `justification`, `normal`, `scale_factor`, `style_name`. Read-only: `start_point`, `style_handle`, `style_element_count`. Host validation requires ≥2 finite non-coincident vertices, ≥3 for a closed multiline, a finite nonzero scale and normal, an existing non-empty `MLineStyle`, and per-vertex segments that are either empty or match the style element count. Vertex `direction`/`miter`, segment layout and per-element miter offsets (justification and scale included) are host-derived by `normalize_scripted_mline` on every scripted create or edit, reusing the drawing command's `rebuild_mline_geometry`/`sync_mline_element_parameters`; dash and fill cuts survive an edit unless a vertex moves. `staged_python_mline_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P: create against the Standard style, a three-vertex/scale/justification transaction edit with untouched-field preservation, corner miter widening and element offsets checked, real `mline_lines` linework before and after DWG and DXF reopen, nine single-edit rejections plus a cross-entity atomic rollback, re-edit after reopen in both formats, deletion, and three-step undo/redo. Known limit: switching to a style with a different element count is rejected until the script also clears the vertices' `segments`; only the rejection is covered by the integration test. | -| Dimension | **Complete** (all 9 subtypes) | `EntityType::Dimension` wraps a nine-variant enum, so the adapter uses a hand-written flat model (`dimension_model.rs.inc`, wired in through the manifest's `manual_kinds`) and the coverage catalog lists its properties through the policy's `synthetic` section. Python sees `subtype` (fixed at creation), the shared base fields (`text`, `style_name`, `normal`, `text_middle_point`, `attachment_point`, `text_rotation`, `horizontal_direction`, `flip_arrow1/2`, `text_user_positioned`) and only the fields of that subtype; a field from another subtype is rejected, not ignored. `actual_measurement` and the base definition point are host-derived (`normalize_scripted_dimension`, the same recompute grip edits use). Unmapped but preserved: `user_text`, `insertion_point`, `block_name`, version and DWG flags, style overrides in XDATA. Per-subtype status, all verified by `staged_python_dimension_lifecycle_over_real_ipc` (C/R/E/D/U/I/W/V/P; measurement and canvas wires checked for each, DWG and DXF reopen recorded per subtype): Linear (10), Aligned (5), Radius (5), Diameter (10), Angular3Pt (90°), Angular2Ln (90°), Ordinate (5), Arc (5·π/2), LargeRadial (6) — every one creates, reads, derives the right measurement, renders wires, and keeps its measurement through DWG and DXF. Edits (Linear second point/text/attachment, Radius definition point) update the measurement, nine rejection cases plus a cross-entity atomic rollback and three create-time rejections leave the document untouched, and post-reopen re-edit passes in both formats. Not covered: text formatting overrides, arrowheads and other DIMSTYLE overrides stored in XDATA, the anonymous `*D` block. The subtype-specific field meanings for the angular subtypes follow the DXF group-code layout rather than the acadrust convenience constructors. | +| Dimension | **Complete** (all 9 subtypes) | `EntityType::Dimension` wraps a nine-variant enum, so the adapter uses a hand-written flat model (`dimension_model.rs.inc`, wired in through the manifest's `manual_kinds`) and the coverage catalog lists its properties through the policy's `synthetic` section. Python sees `subtype` (fixed at creation), the shared base fields (`text`, `style_name`, `normal`, `text_middle_point`, `attachment_point`, `text_rotation`, `horizontal_direction`, `flip_arrow1/2`, `text_user_positioned`) and only the fields of that subtype; a field from another subtype is rejected, not ignored. `actual_measurement` and the base definition point are host-derived (`normalize_scripted_dimension`, the same recompute grip edits use). Unmapped but preserved: `user_text`, `insertion_point`, `block_name`, version and DWG flags, style overrides in XDATA. Per-subtype status, all verified by `staged_python_dimension_lifecycle_over_real_ipc` (C/R/E/D/U/I/W/V/P; measurement and canvas wires checked for each, DWG and DXF reopen recorded per subtype): Linear (10), Aligned (5), Radius (5), Diameter (10), Angular3Pt (90°), Angular2Ln (90°), Ordinate (5), Arc (5·π/2), LargeRadial (6) — every one creates, reads, derives the right measurement, renders wires, and keeps its measurement through DWG and DXF. Edits (Linear second point/text/attachment, Radius definition point) update the measurement, nine rejection cases plus a cross-entity atomic rollback and three create-time rejections leave the document untouched, and post-reopen re-edit passes in both formats. Not covered: text formatting overrides, arrowheads and other DIMSTYLE overrides stored in XDATA, the anonymous `*D` block. The subtype-specific field meanings for the angular subtypes follow the DXF group-code layout rather than the opencadcodec convenience constructors. | | MultiLeader | **Complete** | Mapped through the generic generator, which gained support for `Color`, `LineWeight`, mixed unit/payload enums, fixed `[f64; N]` arrays, bitflags-2 newtypes and per-type module paths for this kind. Writable: `content_type`, `path_type`, `line_color`, `line_weight`, `enable_landing`, `enable_dogleg`, `dogleg_length`, `arrowhead_size`, `text_color`, `text_frame`, the four text attachments, attachment direction/point, `text_alignment`, `text_angle_type`, `text_direction_negative`, `scale_factor`, `enable_annotation_scale`, `extend_leader_to_text`, block content fields, the reference handles (`style_handle`, `text_style_handle`, `arrowhead_handle`, `line_type_handle`, `block_content_handle`) and `context`, which merges as a patch so a script can change `text_string` without resetting leader roots. Read-only: `dwg_version`, `property_override_flags`, `text_align_in_ipe` and the entity-level `text_height` (neither writer stores it; set `context.text_height`). Excluded but preserved: `arrowhead_overrides`, `block_attributes`. Host validation checks finite/positive scale, height, dogleg and arrow sizes, finite context geometry and transform, non-empty leader lines, content-type/context consistency, and that every referenced handle exists as the right object kind. `staged_python_multileader_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P: MText creation with a three-point leader root, a partial-context edit that replaces the path (four points), text, text location, dogleg and colour while preserving scale/content/common fields, canvas wires, eleven rejection cases plus a duplicate-handle atomic rollback, DWG and DXF reopen (text, path, dogleg, `context.text_height`, colour) with post-reopen re-edit, and three-step undo/redo. Not covered: block-content MultiLeaders (no block fixture yet), tolerance content, and style-defined defaults. | | Table | **Complete** | Mapped through the generic generator (now also handling tuples and `usize`, with the generic `LegacyBorderOverrides` fields excluded and preserved). Writable: `insertion_point`, `horizontal_direction`, `normal`, `table_style_handle`, `rows`, `columns`, `merged_ranges`, `break_spacing`, `break_flow_direction`, `break_options`. Read-only: `block_name`, `block_record_handle`, `data_version`, `value_flags`, the override flags, `name`, `description`, `field_handles`, `base_style`, `break_data`, `break_ranges`, `legacy_style_override`. Creation yields the engine default 3x3 grid; scripts read `rows`/`columns`, modify or extend them and assign them back (rows and columns replace wholesale, so copy an existing row dict when adding one). Host validation requires a non-empty grid, positive finite row heights and column widths, every row to have exactly one cell per column, finite cell and content geometry, merged ranges inside the grid and non-overlapping, and existing table-style, cell text-style and cell block handles. `normalize_scripted_table` mirrors `merged_ranges` onto the origin cell's `merge_width/height` on every scripted change to the ranges. `staged_python_table_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P: default-grid creation, a row append, column-width edit, a text cell, a three-column merge and break spacing in one transaction with untouched rows/columns/style/common fields preserved, canvas wires, ten rejection cases plus an atomic rollback, DWG and DXF reopen (row count, width, cell text, insertion) with re-edit, and three-step undo/redo. Format notes: DWG restores `merged_ranges` but not per-cell merge dimensions; DXF restores the cell dimensions but its reader does not rebuild `merged_ranges` (recorded by a canary assertion); both keep the merge. Not covered: the anonymous table block is not regenerated, cell styles and formats beyond text, linked data and field content. | | PolygonMesh | **Complete** | Writable: `flags`, `m_vertex_count`, `n_vertex_count`, `m_smooth_density`, `n_smooth_density`, `smooth_type`, `elevation`, `normal`, `vertices` (each `{location, flags}`; a vertex's own common data is not scripted and defaults). Host validation requires M and N of at least 2, exactly M×N vertices, non-negative densities, finite elevation and locations, and a nonzero normal, so inconsistent counts are refused before any mutation. `staged_python_polygon_mesh_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`) passes C/R/E/D/U/I/W/V/P: a 3×3 grid, a vertex move plus density and cubic smoothing in one transaction, canvas wires, seven rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. | | PolyfaceMesh | **Complete** | Writable: `elevation`, `flags`, `normal`, `start_width`, `end_width`, `smooth_surface`, `thickness`, `vertices` (location, flags, bulge, widths, tangent, id) and `faces` (four indices, flags, optional colour). `seqend_handle` is read-only and preserved. Host validation requires at least 3 finite vertices and 1 face, every face index within the vertex count (a negative index hides that edge, 0 marks an unused corner), at least 3 indices per face, and finite widths, thickness and elevation. `staged_python_polyface_mesh_lifecycle_over_real_ipc` passes the full gate: two triangles, a vertex move plus a new vertex and a hidden-edge face in one transaction, canvas wires, six rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. | | Mesh | **Complete** | Writable: `blend_crease`, `subdivision_level`, `vertices`, `faces`, `edges` (start, end, optional crease), `override_option`; `version` is read-only. Host validation requires at least 3 finite vertices, faces of at least 3 in-range indices, edges with distinct in-range endpoints and a finite non-negative crease, and a non-negative subdivision level, so out-of-range topology is rejected before mutation. `staged_python_mesh_lifecycle_over_real_ipc` passes the full gate: a quad with a creased edge, an added vertex and triangle plus subdivision level 1 in one transaction, canvas wires, seven rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. | -| Helix | **Complete** | W passes since cadcodec `dd1d7bf`: the DXF canary in the real-IPC audit flipped. Writable generating parameters: `axis_base_point`, `start_point`, `axis_vector`, `radius` (the top radius; the base radius is the start point's distance from the axis), `turns`, `turn_height`, `handedness`, `constraint`. Read-only: `spline`, `major_version`, `maintenance_version`. The host rebuilds the derived NURBS spline with OCS's own helix builder on every scripted create or parameter edit (`normalize_scripted_helix`), so the spline always matches the parameters. Host validation requires finite positive radius, turns and turn height, a nonzero axis and a start point off the axis. `staged_python_helix_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`) passes C/R/E/D/U/I/V/P: creation with a derived spline ending at z = turns × turn height, a turns and handedness edit that regenerates it, canvas wires, seven rejection cases plus an atomic rollback, DWG reopen and re-edit, DXF reopen and re-edit, and three-step undo/redo. **W (formerly blocked):** the DXF reader parsed boolean group 290 as an `i16` and never applied it, so a clockwise helix reopened counter-clockwise; fixed in cadcodec `dd1d7bf`, and the handedness now round-trips through both formats. | -| RasterImage | **Integration-tested** | Writable: `file_path` (creation only; a later change is rejected because the image definition owns it), `insertion_point`, `u_vector`, `v_vector` (world units per pixel), `flags`, `clipping_enabled`, `brightness`, `contrast`, `fade`, `clip_boundary`. Read-only: `size` (pixel size, taken from the file), `class_version`, `definition_handle`, `definition_reactor_handle`. Creating from a script requires a readable image file: the host reads its pixel size, creates the `ImageDefinition` object, links it, and sets `size` and a whole-image clip boundary from the file. Host validation requires finite, non-parallel `u`/`v` vectors, brightness/contrast/fade within 0..=100, known display flags, finite clip vertices, at least 2 clip vertices when clipping is enabled, and an existing definition. `staged_python_raster_image_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real PNG fixture) passes C/R/E/D/U/I/W/V/P inside OCS: creation with a host-made definition, placement/scale/brightness/contrast/fade and clip edit in one transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG and DXF reopen with the definition link and file path intact, re-edit, and three-step undo/redo. **Linkage (updated 2026-09-20):** a scripted image now reuses one `ImageDefinition` per file and registers each definition in `ACAD_IMAGE_DICT` under the file's name (a `_2`, `_3` suffix keeps keys unique), creating that dictionary under the root when the drawing has none, and the dictionary owns the definition, as for an image AutoCAD attached. `audit_python_raster_image_definition_linkage_over_real_ipc` checks two images of one file sharing a definition, a third file getting its own, the entries resolving with the right owner, and all of it surviving DWG and DXF reopen and an undo without a dangling entry. **Why not `Complete`:** the host still writes no `IMAGEDEF_REACTOR`: cadcodec's struct documents the reactor's owner as the definition while its DXF writer follows a different convention, and AutoCAD's behavior cannot be checked here, so guessing risks a structure AutoCAD repairs or rejects. **Related finding:** OCS's own IMAGE command creates a bare `RasterImage` with no definition, and a save through either writer drops its file path (`spike_raster_image_definition_persistence`), while the definition-linked form keeps it. Editing an existing definition (file path, resolution) is out of scope. | +| Helix | **Complete** | W passes since opencadcodec `dd1d7bf`: the DXF canary in the real-IPC audit flipped. Writable generating parameters: `axis_base_point`, `start_point`, `axis_vector`, `radius` (the top radius; the base radius is the start point's distance from the axis), `turns`, `turn_height`, `handedness`, `constraint`. Read-only: `spline`, `major_version`, `maintenance_version`. The host rebuilds the derived NURBS spline with OCS's own helix builder on every scripted create or parameter edit (`normalize_scripted_helix`), so the spline always matches the parameters. Host validation requires finite positive radius, turns and turn height, a nonzero axis and a start point off the axis. `staged_python_helix_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`) passes C/R/E/D/U/I/V/P: creation with a derived spline ending at z = turns × turn height, a turns and handedness edit that regenerates it, canvas wires, seven rejection cases plus an atomic rollback, DWG reopen and re-edit, DXF reopen and re-edit, and three-step undo/redo. **W (formerly blocked):** the DXF reader parsed boolean group 290 as an `i16` and never applied it, so a clockwise helix reopened counter-clockwise; fixed in opencadcodec `dd1d7bf`, and the handedness now round-trips through both formats. | +| RasterImage | **Integration-tested** | Writable: `file_path` (creation only; a later change is rejected because the image definition owns it), `insertion_point`, `u_vector`, `v_vector` (world units per pixel), `flags`, `clipping_enabled`, `brightness`, `contrast`, `fade`, `clip_boundary`. Read-only: `size` (pixel size, taken from the file), `class_version`, `definition_handle`, `definition_reactor_handle`. Creating from a script requires a readable image file: the host reads its pixel size, creates the `ImageDefinition` object, links it, and sets `size` and a whole-image clip boundary from the file. Host validation requires finite, non-parallel `u`/`v` vectors, brightness/contrast/fade within 0..=100, known display flags, finite clip vertices, at least 2 clip vertices when clipping is enabled, and an existing definition. `staged_python_raster_image_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real PNG fixture) passes C/R/E/D/U/I/W/V/P inside OCS: creation with a host-made definition, placement/scale/brightness/contrast/fade and clip edit in one transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG and DXF reopen with the definition link and file path intact, re-edit, and three-step undo/redo. **Linkage (updated 2026-09-20):** a scripted image now reuses one `ImageDefinition` per file and registers each definition in `ACAD_IMAGE_DICT` under the file's name (a `_2`, `_3` suffix keeps keys unique), creating that dictionary under the root when the drawing has none, and the dictionary owns the definition, as for an image AutoCAD attached. `audit_python_raster_image_definition_linkage_over_real_ipc` checks two images of one file sharing a definition, a third file getting its own, the entries resolving with the right owner, and all of it surviving DWG and DXF reopen and an undo without a dangling entry. **Why not `Complete`:** the host still writes no `IMAGEDEF_REACTOR`: opencadcodec's struct documents the reactor's owner as the definition while its DXF writer follows a different convention, and AutoCAD's behavior cannot be checked here, so guessing risks a structure AutoCAD repairs or rejects. **Related finding:** OCS's own IMAGE command creates a bare `RasterImage` with no definition, and a save through either writer drops its file path (`spike_raster_image_definition_persistence`), while the definition-linked form keeps it. Editing an existing definition (file path, resolution) is out of scope. | | Wipeout | **Complete** | Writable: `insertion_point`, `u_vector`, `v_vector` (the region's world extent), `size`, `flags`, `clipping_enabled`, `brightness`, `contrast`, `fade`, `clip_mode`, `clip_type` and `clip_boundary_vertices` (normalized image coordinates, -0.5..0.5). Read-only: `class_version`, `definition_handle`, `definition_reactor_handle`. Scripted creation matches OCS's native WIPEOUT command, which also leaves the definition link unset. Host validation requires finite non-parallel `u`/`v` vectors, a positive `size`, brightness/contrast/fade within 0..=100, known flags, finite clip vertices, exactly 2 vertices for a rectangular clip and at least 3 for a polygonal one. `staged_python_wipeout_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`) passes C/R/E/D/U/I/W/V/P: rectangular creation, a switch to a three-vertex polygonal inside-clip with a move in one transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. | -| Underlay | **Complete** | W passes since cadcodec `dd1d7bf`: the DXF canary in the real-IPC audit flipped. Writable: `underlay_type` and `definition_handle` (both fixed by the definition: creation names an existing `UnderlayDefinition`, and a mismatched type or missing definition is rejected), `insertion_point`, `x_scale`, `y_scale`, `z_scale`, `rotation`, `normal`, `flags`, `contrast`, `fade`, `clip_boundary_vertices`, `clip_inverted`. The file path and page live on the definition object, which scripts cannot create or list yet; they reference one the drawing already owns. Host validation requires finite nonzero scales, a finite rotation, a nonzero normal, contrast/fade within 0..=100, known flags, finite clip vertices (none, or at least 2) and an existing definition of the same type. `staged_python_underlay_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle` with a fixture PDF definition) passes C/R/E/D/U/I/V/P and DWG W: creation, a placement/scale/rotation/contrast/fade/clip transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG reopen and re-edit, and three-step undo/redo. **W (formerly blocked):** DXF stores the rotation in degrees and the reader returned it unconverted, compounding on every save-reopen; fixed in cadcodec `dd1d7bf`, and the rotation now round-trips through both formats. Not covered: creating definitions from a file path, DWF/DGN fixtures. | +| Underlay | **Complete** | W passes since opencadcodec `dd1d7bf`: the DXF canary in the real-IPC audit flipped. Writable: `underlay_type` and `definition_handle` (both fixed by the definition: creation names an existing `UnderlayDefinition`, and a mismatched type or missing definition is rejected), `insertion_point`, `x_scale`, `y_scale`, `z_scale`, `rotation`, `normal`, `flags`, `contrast`, `fade`, `clip_boundary_vertices`, `clip_inverted`. The file path and page live on the definition object, which scripts cannot create or list yet; they reference one the drawing already owns. Host validation requires finite nonzero scales, a finite rotation, a nonzero normal, contrast/fade within 0..=100, known flags, finite clip vertices (none, or at least 2) and an existing definition of the same type. `staged_python_underlay_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle` with a fixture PDF definition) passes C/R/E/D/U/I/V/P and DWG W: creation, a placement/scale/rotation/contrast/fade/clip transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG reopen and re-edit, and three-step undo/redo. **W (formerly blocked):** DXF stores the rotation in degrees and the reader returned it unconverted, compounding on every save-reopen; fixed in opencadcodec `dd1d7bf`, and the rotation now round-trips through both formats. Not covered: creating definitions from a file path, DWF/DGN fixtures. | | Viewport | **Complete (OCS round trip)** | Writable: `center`, `width`, `height`, `status`, `view_center`, `view_direction`, `view_target`, `lens_length`, `front_clip_z`, `back_clip_z`, `view_height`, `twist_angle`, `frozen_layers` (layer handles), `render_mode`, `circle_sides`, `ucs_icon_visible`. Read-only: `id`, `custom_scale`, snap and grid settings, UCS fields, `clip_boundary_handle`, `style_sheet`, shade-plot, background, visual-style and sun handles, lighting, `ambient_color`, `elevation`. Reference rules: the owner must be a paper-space layout block and every frozen-layer handle an existing layer. **Derived by the host (new 2026-09-20), as the MVIEW command does:** a scripted viewport gets an `id` unique in its layout and at least 2 (max existing plus one), and `custom_scale` is `height / view_height`, recomputed whenever either changes, so `custom_scale` is now read-only (setting the scale directly would have let it drift from the geometry). Host validation requires finite centers, positive width, height, view height, lens length and scale, a nonzero view direction, finite clip planes and twist, and at least 3 circle sides. **Canvas oracle (new):** the shared driver now activates the layout that owns the viewport (`Scene::set_current_layout`) and requires the viewport to draw a frame both after creation and after the edit. `staged_python_viewport_lifecycle_over_real_ipc` passes C/R/E/D/U/I/V/W/P: creation in the paper-space block, a move/resize/view-height/twist/frozen-layer transaction, seven rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. `audit_python_viewport_ids_and_scale_over_real_ipc` checks unique ids (2, 3, 4), the derived scale on create and after a zoom and a resize, an id kept across edits, a rejected direct `custom_scale` write, and that every viewport draws a frame. A control showed that the engine default id 0 is **not** invisible in a layout with no sheet viewport (it draws a frame), which corrects an earlier suspicion; the derived id matters for uniqueness. Not covered: clip boundary entities (`clip_boundary_handle` is read-only and preserved), non-rectangular viewports, and AutoCAD acceptance of the written viewports, which cannot be tested here. | -| ViewBorder | **Complete** | W passes since cadcodec `dd1d7bf`: the DXF canary in the real-IPC audit flipped. Writable: `min`, `max`, `center` (2-D pairs), `scale`, `rotation_angle`, `active_viewport` and `scale_handle` (both required at creation). `version` is read-only. Reference rules: `active_viewport` must be an existing Viewport entity and `scale_handle` an existing `Scale` object. Host validation requires finite bounds with `min` below `max` on both axes, a positive scale and a finite rotation. `staged_python_view_border_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, driver-made viewport and scale fixtures) passes C/R/E/D/U/I/V/P and DWG W: creation, a resize/recenter/scale/rotation transaction, canvas wires, seven rejection cases plus an atomic rollback, DWG reopen and re-edit, and three-step undo/redo. **W (formerly blocked):** a DXF `DRAWINGVIEW` in the entity list reopened as an opaque entity kind; fixed in cadcodec `dd1d7bf`, and DXF now restores the typed ViewBorder. Not covered: the linked view representation and drawing-view definition records. | -| SectionSymbol | **Complete** | W passes since cadcodec `dd1d7bf`: the DXF canary in the real-IPC audit flipped. The earlier blocker was overstated, and reading cadcodec and OCS showed why. cadcodec documents `points` as the canonical geometry, says the point-count fields equal the point count in every verified ODA export, and ships `sync_display_fields()` to refresh the `end_a`/`end_b`/`tick_a`/`tick_b`/`label` projections; OCS's own grip and transform code already calls it. OCS also renders a symbol with no style and no view link. So the kind is mapped: writable `points` (each `{point, bulge, label, label_offset, raw_flag_280}`), `symbol_scale`, `style_handle` and `view_rep_handle`; read-only `view_symbol_version`, `raw_view_symbol_70`, `version`, `raw_flags_90`, the two derived counts and the five projections. The host sets `raw_point_count_90` and `raw_point_record_count` to the point count and calls `sync_display_fields()` whenever `points` is created or changed, and leaves a symbol whose points did not change exactly as read. Host validation requires at least two finite points with finite bulges and label offsets, a positive scale, and that each non-null reference is an existing `SectionViewStyle` (style) or `ViewRep` (view representation) class object; the wrong object kind is refused. Creating one without references gives a standalone symbol. `audit_python_section_symbol_lifecycle_over_real_ipc` (shared driver) passes C/R/E/D/U/I/V/P and DWG W: a two-point symbol, a three-point edit with a new label, bulge and scale in one transaction (counts and projections follow), canvas wires, seven rejection cases plus an atomic rollback, DWG reopen and re-edit, three-step undo/redo. `audit_python_section_symbol_references_over_real_ipc` covers valid references, a standalone symbol and four wrong-object refusals with host-made class objects. **W (formerly blocked; issue 5 in [cadcodec-reader-gaps.md](cadcodec-reader-gaps.md)):** the DXF reader dispatched `SECTIONLINE` only inside blocks, so a symbol in the entity list reopened as `Unknown`; fixed in cadcodec `dd1d7bf`. Open items that cannot be checked here: `raw_flags_90` has unknown semantics (0 for a new symbol, preserved on read), and whether AutoCAD accepts a symbol with no parent view. Scripts cannot create the style or view-representation objects. | -| Light | **Complete** | W passes since cadcodec `dd1d7bf`: the DXF canary in the real-IPC audit flipped. Writable: `name`, `light_type` (1 distant, 2 point, 3 spot), `position`, `target`, `status`, `light_color`, `plot_glyph`, `intensity`, the attenuation type and limits, `hotspot_angle`, `falloff_angle`, `cast_shadows`, `shadow_type`, `shadow_map_size`, `shadow_map_softness`, `photometric_mode`, `photometric_data`; `class_version` is read-only. Host validation requires a non-empty name, a known light type, finite placement, a spot or distant target that differs from the position, non-negative intensity, angles and limits, an attenuation start not above its end, a spot hotspot not above its falloff, and a non-negative shadow map size. `staged_python_light_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`) passes C/R/E/D/U/I/V/P and DWG W: a spot light with an RGB colour, an intensity/target/position/shadow transaction that preserves the colour and untouched fields, the canvas glyph, eight rejection cases plus an atomic rollback, DWG reopen and re-edit, and three-step undo/redo. **W (formerly blocked):** the DXF reader did not restore `cast_shadows`; fixed in cadcodec `dd1d7bf`, and every mapped field now round-trips through both formats. Not covered: photometric web data edits and sun/skylight objects. | -| Region | **Complete (create from a profile + rigid move)** | New `SolidOperation::RegionFromProfile` and `doc.solids.region(profile, layer=None, delete_source=False)`. The host builds the region exactly as the REGION command does (`presspull_model::profile_geometry` then `cadkernel::brep::planar_region`), verifies it with `solid_to_sat`, puts it on `layer` or on the profile's layer, and erases the profile only when `delete_source` is set. One closed planar profile per call (a circle, ellipse, closed polyline, spline and so on); an open profile, a ray, a region, a missing entity or an empty layer is refused with a message before anything changes. Rigid moves go through the same `doc.solids.transform` channel as Solid3D. `audit_python_region_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P through the real runner: regions from a circle (source kept), a closed rectangle (source deleted) and a second circle on its own layer with exact extents, canvas geometry, six refusals that change nothing and record no undo step, a move with the exact shifted extent, DWG and DXF reopen with the payload lifting and the same extent, a reopened region moving again, and undo restoring the region exactly. Not covered: regions with holes and merging open curves into a boundary (the REGION command's second path), and AutoCAD acceptance of the payload. | +| ViewBorder | **Complete** | W passes since opencadcodec `dd1d7bf`: the DXF canary in the real-IPC audit flipped. Writable: `min`, `max`, `center` (2-D pairs), `scale`, `rotation_angle`, `active_viewport` and `scale_handle` (both required at creation). `version` is read-only. Reference rules: `active_viewport` must be an existing Viewport entity and `scale_handle` an existing `Scale` object. Host validation requires finite bounds with `min` below `max` on both axes, a positive scale and a finite rotation. `staged_python_view_border_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, driver-made viewport and scale fixtures) passes C/R/E/D/U/I/V/P and DWG W: creation, a resize/recenter/scale/rotation transaction, canvas wires, seven rejection cases plus an atomic rollback, DWG reopen and re-edit, and three-step undo/redo. **W (formerly blocked):** a DXF `DRAWINGVIEW` in the entity list reopened as an opaque entity kind; fixed in opencadcodec `dd1d7bf`, and DXF now restores the typed ViewBorder. Not covered: the linked view representation and drawing-view definition records. | +| SectionSymbol | **Complete** | W passes since opencadcodec `dd1d7bf`: the DXF canary in the real-IPC audit flipped. The earlier blocker was overstated, and reading opencadcodec and OCS showed why. opencadcodec documents `points` as the canonical geometry, says the point-count fields equal the point count in every verified ODA export, and ships `sync_display_fields()` to refresh the `end_a`/`end_b`/`tick_a`/`tick_b`/`label` projections; OCS's own grip and transform code already calls it. OCS also renders a symbol with no style and no view link. So the kind is mapped: writable `points` (each `{point, bulge, label, label_offset, raw_flag_280}`), `symbol_scale`, `style_handle` and `view_rep_handle`; read-only `view_symbol_version`, `raw_view_symbol_70`, `version`, `raw_flags_90`, the two derived counts and the five projections. The host sets `raw_point_count_90` and `raw_point_record_count` to the point count and calls `sync_display_fields()` whenever `points` is created or changed, and leaves a symbol whose points did not change exactly as read. Host validation requires at least two finite points with finite bulges and label offsets, a positive scale, and that each non-null reference is an existing `SectionViewStyle` (style) or `ViewRep` (view representation) class object; the wrong object kind is refused. Creating one without references gives a standalone symbol. `audit_python_section_symbol_lifecycle_over_real_ipc` (shared driver) passes C/R/E/D/U/I/V/P and DWG W: a two-point symbol, a three-point edit with a new label, bulge and scale in one transaction (counts and projections follow), canvas wires, seven rejection cases plus an atomic rollback, DWG reopen and re-edit, three-step undo/redo. `audit_python_section_symbol_references_over_real_ipc` covers valid references, a standalone symbol and four wrong-object refusals with host-made class objects. **W (formerly blocked; issue 5 in [cadcodec-reader-gaps.md](cadcodec-reader-gaps.md)):** the DXF reader dispatched `SECTIONLINE` only inside blocks, so a symbol in the entity list reopened as `Unknown`; fixed in opencadcodec `dd1d7bf`. Open items that cannot be checked here: `raw_flags_90` has unknown semantics (0 for a new symbol, preserved on read), and whether AutoCAD accepts a symbol with no parent view. Scripts cannot create the style or view-representation objects. | +| Light | **Complete** | W passes since opencadcodec `dd1d7bf`: the DXF canary in the real-IPC audit flipped. Writable: `name`, `light_type` (1 distant, 2 point, 3 spot), `position`, `target`, `status`, `light_color`, `plot_glyph`, `intensity`, the attenuation type and limits, `hotspot_angle`, `falloff_angle`, `cast_shadows`, `shadow_type`, `shadow_map_size`, `shadow_map_softness`, `photometric_mode`, `photometric_data`; `class_version` is read-only. Host validation requires a non-empty name, a known light type, finite placement, a spot or distant target that differs from the position, non-negative intensity, angles and limits, an attenuation start not above its end, a spot hotspot not above its falloff, and a non-negative shadow map size. `staged_python_light_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`) passes C/R/E/D/U/I/V/P and DWG W: a spot light with an RGB colour, an intensity/target/position/shadow transaction that preserves the colour and untouched fields, the canvas glyph, eight rejection cases plus an atomic rollback, DWG reopen and re-edit, and three-step undo/redo. **W (formerly blocked):** the DXF reader did not restore `cast_shadows`; fixed in opencadcodec `dd1d7bf`, and every mapped field now round-trips through both formats. Not covered: photometric web data edits and sun/skylight objects. | +| Region | **Complete (create from a profile + rigid move)** | New `SolidOperation::RegionFromProfile` and `doc.solids.region(profile, layer=None, delete_source=False)`. The host builds the region exactly as the REGION command does (`presspull_model::profile_geometry` then `opencadkernel::brep::planar_region`), verifies it with `solid_to_sat`, puts it on `layer` or on the profile's layer, and erases the profile only when `delete_source` is set. One closed planar profile per call (a circle, ellipse, closed polyline, spline and so on); an open profile, a ray, a region, a missing entity or an empty layer is refused with a message before anything changes. Rigid moves go through the same `doc.solids.transform` channel as Solid3D. `audit_python_region_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P through the real runner: regions from a circle (source kept), a closed rectangle (source deleted) and a second circle on its own layer with exact extents, canvas geometry, six refusals that change nothing and record no undo step, a move with the exact shifted extent, DWG and DXF reopen with the payload lifting and the same extent, a reopened region moving again, and undo restoring the region exactly. Not covered: regions with holes and merging open curves into a boundary (the REGION command's second path), and AutoCAD acceptance of the payload. | | Body | **Integration-tested (rigid move only)** | Mapped read-only like Solid3D (same payload container). `audit_python_body_transform_over_real_ipc` moves a Body carrying a kernel box through `doc.solids.translate`, checks the exact extent, DWG reopen with a losslessly lifting payload and the exact volume, and the explanatory refusal of `create_entity('Body')`. Scripts cannot create a Body, and a non-solid (sheet or wire) body that does not lift is refused untouched. | -| Solid3D | **Complete (create + rigid move)** | New in this session: an additive `HostApi::solid_operation` (`SolidOperation::Create` and `Transform`, plus a `SolidPrimitive` enum) carried by `PluginRequest::SolidOperation` / `PluginResponse::SolidResult` through both IPC clients, the server and `HostSession`, and a Python `doc.solids` API: `box`, `wedge`, `cylinder`, `sphere`, `torus`, `pyramid`, `transform`, `translate`, `rotate`, `mirror`. The host builds bodies with cadkernel, and every result goes through `solid_to_sat`, which lifts its own output back and refuses anything lossy. A transform first lifts the existing payload with `kernel_acis_body` and is refused, leaving the payload untouched, when that is lossy. Only rigid moves and mirrors are accepted (scaling and shear are refused). Regenerated `wires` follow the body, `silhouettes` are cleared and `history_handle` is dropped. Python sees `uid`, `point_of_reference` and `history_handle` read-only; `acis_data`, `wires` and `silhouettes` are excluded and preserved; creation through `create_entity` is refused with a message pointing at `doc.solids`. `audit_python_solid3d_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P through the real runner with the kernel as oracle: all six primitives match their analytic volumes (box, wedge and cube exactly, curved solids within 2 percent of the tessellated volume), a translate, a rotate and a mirror keep the volume and move the extent exactly and keep 12 edge wires, 13 refusals (zero and non-finite sizes, a torus with minor above major, too few pyramid sides, an empty layer, an unknown primitive, a wrong number count, a scaling matrix, a non-finite offset, a payload the kernel cannot lift, a missing entity) change nothing and leave no undo step, DWG and DXF reopen with the payload lifting losslessly and the same volume and extent, a reopened payload moves again, and undo restores the original solid exactly. Not covered: solids that carry parametric history, and AutoCAD acceptance of the written payload, which cannot be tested here. **Booleans** (`SolidOperation::Boolean`, `doc.solids.union`, `subtract` and `intersect`) are covered by `audit_python_boolean_lifecycle_over_real_ipc`: two overlapping boxes give exact volumes (union 360, subtract and intersect 120) and extents, the operands are consumed unless `keep_operands` (undo restores both exactly), a result takes the first operand's layer or an explicit one, a second planar operation chains on the result (a through-hole gives 104), eight refusals change nothing and record no undo step (same operand twice, a non-solid, a missing entity, an unliftable payload, an empty layer, an unknown operation, a disjoint intersection), and DWG and DXF reopen losslessly with a reopened result moving again. The kernel refuses many curved cases (measured in release: about 1 to 2 s when it succeeds, and `Coincident`, `CutRefused` or `NoClosedForm` when it does not, including a second cut in a drilled box); its reason reaches the script and nothing changes, shown by an `#[ignore]` curved test that takes about 27 s in a debug build. Booleans block the host thread and count against the script's 30 s budget; only Solid3D operands are accepted. | +| Solid3D | **Complete (create + rigid move)** | New in this session: an additive `HostApi::solid_operation` (`SolidOperation::Create` and `Transform`, plus a `SolidPrimitive` enum) carried by `PluginRequest::SolidOperation` / `PluginResponse::SolidResult` through both IPC clients, the server and `HostSession`, and a Python `doc.solids` API: `box`, `wedge`, `cylinder`, `sphere`, `torus`, `pyramid`, `transform`, `translate`, `rotate`, `mirror`. The host builds bodies with opencadkernel, and every result goes through `solid_to_sat`, which lifts its own output back and refuses anything lossy. A transform first lifts the existing payload with `kernel_acis_body` and is refused, leaving the payload untouched, when that is lossy. Only rigid moves and mirrors are accepted (scaling and shear are refused). Regenerated `wires` follow the body, `silhouettes` are cleared and `history_handle` is dropped. Python sees `uid`, `point_of_reference` and `history_handle` read-only; `acis_data`, `wires` and `silhouettes` are excluded and preserved; creation through `create_entity` is refused with a message pointing at `doc.solids`. `audit_python_solid3d_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P through the real runner with the kernel as oracle: all six primitives match their analytic volumes (box, wedge and cube exactly, curved solids within 2 percent of the tessellated volume), a translate, a rotate and a mirror keep the volume and move the extent exactly and keep 12 edge wires, 13 refusals (zero and non-finite sizes, a torus with minor above major, too few pyramid sides, an empty layer, an unknown primitive, a wrong number count, a scaling matrix, a non-finite offset, a payload the kernel cannot lift, a missing entity) change nothing and leave no undo step, DWG and DXF reopen with the payload lifting losslessly and the same volume and extent, a reopened payload moves again, and undo restores the original solid exactly. Not covered: solids that carry parametric history, and AutoCAD acceptance of the written payload, which cannot be tested here. **Booleans** (`SolidOperation::Boolean`, `doc.solids.union`, `subtract` and `intersect`) are covered by `audit_python_boolean_lifecycle_over_real_ipc`: two overlapping boxes give exact volumes (union 360, subtract and intersect 120) and extents, the operands are consumed unless `keep_operands` (undo restores both exactly), a result takes the first operand's layer or an explicit one, a second planar operation chains on the result (a through-hole gives 104), eight refusals change nothing and record no undo step (same operand twice, a non-solid, a missing entity, an unliftable payload, an empty layer, an unknown operation, a disjoint intersection), and DWG and DXF reopen losslessly with a reopened result moving again. The kernel refuses many curved cases (measured in release: about 1 to 2 s when it succeeds, and `Coincident`, `CutRefused` or `NoClosedForm` when it does not, including a second cut in a drilled box); its reason reaches the script and nothing changes, shown by an `#[ignore]` curved test that takes about 27 s in a debug build. Booleans block the host thread and count against the script's 30 s budget; only Solid3D operands are accepted. | | Surface | **Complete (plane surface, extruded open profile, isolines, rigid move)** | New `SolidOperation::SurfaceFromProfile` and `SolidOperation::Extrude`, exposed as `doc.solids.surface(profile)` and `doc.solids.extrude(profile, direction)`. A closed planar profile becomes a `Plane` surface (built as the region body, so it keeps the plane kind); a profile extruded along a nonzero direction becomes a Solid3D when it is closed and a generic Surface when it is open, using OCS's own `extrusion_body`. Every payload is verified lossless by `solid_to_sat`, and rigid moves (now accepted for surfaces too) keep a plane a plane and turn any other kind generic, as OCS does after an edit. Python sees `surface_kind`, `modeler_format_version`, `point_of_reference` and `history_handle` read-only and `u_isolines` and `v_isolines` writable (0 to 200); the payload, wires, silhouettes and the kind-specific `surface_data` are excluded and preserved. The kind field is exposed as `surface_kind` because a bare `kind` key names the entity type in every Python dict; the new `exposed_property_names_are_unique_per_kind` test guards against that class of collision. `audit_python_surface_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P through the real runner: two plane surfaces (one on its own layer), a surface from an open polyline extrusion, three extruded solids checked against their analytic volumes (a box exactly, curved ones within 2 percent), `delete_source` erasing only the intended profile, canvas geometry, nine refusals that change nothing and record no undo step, an isoline transaction with four rejections (500, negative, the read-only kind, an excluded key), moves with exact extents, DWG and DXF reopen with lifting payloads, matching extents, kept isolines and the plane kind, a reopened surface moving again, and exact undo. Not covered: lofted, revolved, swept and NURBS surfaces (a script cannot create them; an existing one is preserved and turns generic on a move), parametric history, and AutoCAD acceptance. | | Ole2Frame | **Complete** | Writable: `upper_left_corner`, `lower_right_corner`, `lock_aspect`, `is_paper_space`. Read-only: `version`, `source_application`, `ole_object_type`, `dwg_mode`. The embedded object `storage` and the frame `envelope` are opaque, excluded from the Python form, refused by the mutation check (`Ole2Frame embedded storage cannot change`) and preserved untouched. **Creation (new 2026-09-20):** `SolidOperation::EmbedPicture` and `doc.embed_picture(path, origin=(0, 0, 0), width=1, layer=None)` build a frame from a picture file with OCS's own `EmbeddedImage::from_file` and `build_embedded_ole`: PNG, JPEG and BMP are stored as read, any other format (a TIFF in the test) is re-encoded as PNG, `origin` is the bottom-left corner, `width` the frame width, and the height follows the picture's aspect. The generic `create_entity('Ole2Frame')` is refused with a message pointing at `doc.embed_picture`. Host validation requires finite corners spanning a nonzero size and `lock_aspect` of 0 or 1 for edits, and for creation a readable picture, a finite origin, a positive width and a non-empty layer. `audit_python_ole2frame_lifecycle_over_real_ipc` (shared driver) passes C/R/E/D/U/I/W/V/P through the real runner: creation from a PNG, a corner and aspect-lock transaction, canvas geometry, five rejection cases (including an attempt to write `storage`), an atomic rollback, **DWG and DXF reopen with the 119-byte embedded picture byte-for-byte intact (extracted through `extract_presentation`)**, re-edit, and three-step undo/redo. `audit_python_ole2frame_creation_rules_over_real_ipc` checks that PNG and JPEG are stored byte-for-byte, a TIFF becomes a PNG, the frame keeps the picture's 2:1 aspect with the requested origin and layer, and seven refusals (missing file, a non-image, empty layer, zero width, non-finite origin, a short origin list, and generic creation) change nothing and record no undo step. Not covered: OLE objects that are not pictures, and AutoCAD acceptance of the written frame, which cannot be tested here. | diff --git a/docs/plugin-host-model.md b/docs/plugin-host-model.md index 417a576ea..09e0f1189 100644 --- a/docs/plugin-host-model.md +++ b/docs/plugin-host-model.md @@ -125,7 +125,7 @@ as `{"type": "overflow", "dropped": n}`. Tokens are bound to the tab that asked. ## Known limits - Twelve of the 43 canvas kinds are not yet `Complete`; each has a named blocker in the - ledger. Seven are cadcodec DXF bugs whose fixes are submitted upstream but not yet adopted. + ledger. Seven are opencadcodec DXF bugs whose fixes are submitted upstream but not yet adopted. - The DXF codec also drops some table properties (text style generation flags, block descriptions, a dimension style's text-style name) and mis-scales one angle; these are pinned by canaries and listed in `cadcodec-reader-gaps.md`. diff --git a/docs/plugin-host-review-guide.md b/docs/plugin-host-review-guide.md index d242aa808..4a663ffe4 100644 --- a/docs/plugin-host-review-guide.md +++ b/docs/plugin-host-review-guide.md @@ -126,8 +126,8 @@ several kinds and features are interleaved, so read by area rather than commit b ## What is deliberately not done - **Twelve of the 43 kinds are not `Complete`** (see the ledger). Seven are blocked by DXF bugs in - cadcodec, whose fixes are submitted as [cadcodec#48](https://github.com/HakanSeven12/cadcodec/pull/48) and - [#51](https://github.com/HakanSeven12/cadcodec/pull/51). **This change still pins the unfixed cadcodec revision**, + opencadcodec, whose fixes are submitted as [opencadcodec#48](https://github.com/HakanSeven12/opencadcodec/pull/48) and + [#51](https://github.com/HakanSeven12/opencadcodec/pull/51). **This change still pins the unfixed opencadcodec revision**, so its canary tests assert the old behaviour; once those merge, the pin moves, the canaries flip and those kinds can be marked complete. The other five are held by the DWG format, a kind change on DXF save, an image-reactor structure that cannot be verified, an update-only decision, and a missing creation path. diff --git a/docs/plugin-template-v2/Cargo.toml b/docs/plugin-template-v2/Cargo.toml index 44d62f7eb..bae5e7b0b 100644 --- a/docs/plugin-template-v2/Cargo.toml +++ b/docs/plugin-template-v2/Cargo.toml @@ -12,4 +12,4 @@ crate-type = ["cdylib"] [dependencies] ocs_plugin_api = { path = "../../crates/ocs_plugin_api", features = ["host"] } -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "8a28c21" } +acadrust = { git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8a28c21" } diff --git a/docs/plugin-template/.github/workflows/release.yml b/docs/plugin-template/.github/workflows/release.yml index 3b3b1e45e..dc1a8f82a 100644 --- a/docs/plugin-template/.github/workflows/release.yml +++ b/docs/plugin-template/.github/workflows/release.yml @@ -44,7 +44,7 @@ jobs: run: | mkdir -p dist cp target/release/*.${{ matrix.ext }} "dist/${{ matrix.asset }}" - acadrust_source=$(grep -A3 '^name = "acadrust"' Cargo.lock | grep '^source' | head -1 | sed 's/^source = "//;s/"$//') + acadrust_source=$(grep -A3 '^name = "opencadcodec"' Cargo.lock | grep '^source' | head -1 | sed 's/^source = "//;s/"$//') sed \ -e "s|__RUSTC_VERSION__|$(rustc --version)|" \ -e "s|__ACADRUST_SOURCE__|$acadrust_source|" \ diff --git a/docs/tessellation.md b/docs/tessellation.md index 8a6815eba..7a5a62ab7 100644 --- a/docs/tessellation.md +++ b/docs/tessellation.md @@ -1,10 +1,10 @@ # Entity tessellation paths -How each acadrust `EntityType` becomes drawable geometry in OpenCADStudio. +How each opencadcodec `EntityType` becomes drawable geometry in OpenCADStudio. There are **three ways** an entity gets there, and they must not be conflated: -1. **Kernel B-rep mesh** — the ACIS document is lifted into a cadkernel `Body` +1. **Kernel B-rep mesh** — the ACIS document is lifted into a opencadkernel `Body` and each face triangulated in its own surface's parameter space. The only path that produces filled *mesh* triangles from analytic surfaces. 2. **Curve-sampled** — the entity's geometry is read through diff --git a/plugins/opencad-python/Cargo.lock b/plugins/opencad-python/Cargo.lock index 2d7f6767b..c2ef5c6fc 100644 --- a/plugins/opencad-python/Cargo.lock +++ b/plugins/opencad-python/Cargo.lock @@ -2,29 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "acadrust" -version = "0.5.5" -source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=1c3be0c#1c3be0ca296c99f2f126ba1a03c2be1998d7c64e" -dependencies = [ - "ahash 0.8.12", - "anyhow", - "bitflags", - "byteorder", - "encoding_rs", - "flate2", - "indexmap", - "itoa", - "memmap2", - "nom", - "once_cell", - "rayon", - "ryu", - "serde", - "thiserror 1.0.69", - "web-time", -] - [[package]] name = "adler2" version = "2.0.1" @@ -1254,13 +1231,13 @@ dependencies = [ name = "ocs_plugin_api" version = "0.2.1" dependencies = [ - "acadrust", "bincode", "cargo-lock", "getrandom 0.2.17", "interprocess", "libloading 0.8.9", "memmap2", + "opencadcodec", "rkyv", "serde", "serde-reflection", @@ -1285,6 +1262,29 @@ dependencies = [ "serde_json", ] +[[package]] +name = "opencadcodec" +version = "0.5.5" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=43a82f9#43a82f9b09ab5250fbe6a03e49c1d5a73a4152f3" +dependencies = [ + "ahash 0.8.12", + "anyhow", + "bitflags", + "byteorder", + "encoding_rs", + "flate2", + "indexmap", + "itoa", + "memmap2", + "nom", + "once_cell", + "rayon", + "ryu", + "serde", + "thiserror 1.0.69", + "web-time", +] + [[package]] name = "optional" version = "0.5.0" diff --git a/plugins/opencad-python/PLUGIN.md b/plugins/opencad-python/PLUGIN.md index ef2c33ad1..235737226 100644 --- a/plugins/opencad-python/PLUGIN.md +++ b/plugins/opencad-python/PLUGIN.md @@ -181,7 +181,7 @@ positive, and `dimtxsty` must name an existing text style. `Standard` is never renamed or deleted; a style that is current or still referenced is never deleted; a case-only rename is refused. Style operations act on the active document. -Known persistence limits (cadcodec, see `docs/cadcodec-reader-gaps.md`; the first two are fixed in +Known persistence limits (opencadcodec, see `docs/cadcodec-reader-gaps.md`; the first two are fixed in an open upstream pull request but OCS has not yet adopted it): a DXF save drops the backward/upside-down flags of a text style and does not restore a dimension style's `dimtxsty` name (the handle link survives); DWG keeps both. @@ -211,7 +211,7 @@ anonymous or externally referenced blocks, and a case-only rename. Deleting also removes the definition's contents. Records list user blocks only (`handle`, `name`, `description`, `explodable`, `scale_uniformly`, `base_point`, `insert_count`, `entities` as `{handle, kind}`). A DXF save drops the block -description (cadcodec); DWG keeps it. +description (opencadcodec); DWG keeps it. **Block contents.** Add to a definition with `create_entity(kind, block="Part", ...)`. It is validated exactly like a model-space entity and the host sets the diff --git a/plugins/opencad-python/build/generate.rs b/plugins/opencad-python/build/generate.rs index 1a84d5a10..226e7db29 100644 --- a/plugins/opencad-python/build/generate.rs +++ b/plugins/opencad-python/build/generate.rs @@ -133,7 +133,7 @@ enum Classify { /// `{ "kind": "Variant", "value": ... }` so nested geometry such as /// hatch boundary edges remains explicit and round-trippable. TaggedEnum, - /// A struct whose only field is `bits` (an acadrust bitflags newtype, + /// A struct whose only field is `bits` (an opencadcodec bitflags newtype, /// e.g. `PolylineFlags`/`VertexFlags`). Represented in Python as a plain /// integer via `.bits()`/`::from_bits(..)` — the named boolean accessors /// some of these types have (`is_closed`/`set_closed`, ...) are exposed @@ -163,7 +163,7 @@ fn classify<'a>(name: &str, info: &'a TypeInfo) -> Classify { Classify::UnitEnum } } - // acadrust `bitflags!` types serialize as a newtype over their integer + // opencadcodec `bitflags!` types serialize as a newtype over their integer // bits; they convert exactly like the hand-written `bits` structs. TypeKind::Newtype => Classify::BitFlags, other => panic!("entity_manifest.json: {name} has unsupported registry kind {other:?}"), @@ -301,12 +301,12 @@ fn native_rust_type(type_id: &str) -> &'static str { } } -/// Rust path of a traced type; most live in `acadrust::entities`. +/// Rust path of a traced type; most live in `codec::entities`. fn type_path(name: &str) -> String { match name { - "LineWeight" => "acadrust::types::LineWeight".to_owned(), - "LeaderLineBreakInfo" => "acadrust::entities::multileader::LeaderLineBreakInfo".to_owned(), - _ => format!("acadrust::entities::{name}"), + "LineWeight" => "codec::types::LineWeight".to_owned(), + "LeaderLineBreakInfo" => "codec::entities::multileader::LeaderLineBreakInfo".to_owned(), + _ => format!("codec::entities::{name}"), } } @@ -356,10 +356,10 @@ fn default_expr(type_id: &str) -> String { "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" => "0".to_string(), "bool" => "false".to_string(), "String" | "str" => "String::new()".to_string(), - "Vector3" => "acadrust::types::Vector3::ZERO".to_string(), - "Vector2" => "acadrust::types::Vector2::ZERO".to_string(), - "Handle" => "acadrust::Handle::new(0)".to_string(), - "Color" => "acadrust::types::Color::ByLayer".to_string(), + "Vector3" => "codec::types::Vector3::ZERO".to_string(), + "Vector2" => "codec::types::Vector2::ZERO".to_string(), + "Handle" => "codec::Handle::new(0)".to_string(), + "Color" => "codec::types::Color::ByLayer".to_string(), other => format!("default_{}()", snake_case(other)), } } @@ -440,7 +440,7 @@ fn leaf_from_py(registry: &TypeRegistry, type_id: &str, value_expr: &str) -> Str match type_id { "Vector3" => format!("py_to_vector3_dict({value_expr}, vm)"), "Vector2" => format!("py_to_vector2({value_expr}, vm)"), - "Handle" => format!("{value_expr}.try_into_value::(vm).map(acadrust::Handle::new)"), + "Handle" => format!("{value_expr}.try_into_value::(vm).map(codec::Handle::new)"), "Color" => format!("py_to_color_dict({value_expr}, vm)"), other => { let info = get(registry, other) @@ -697,7 +697,7 @@ fn gen_struct(name: &str, info: &TypeInfo, registry: &TypeRegistry, required: &[ if f.type_id.as_str() == "EntityCommon" { // A sub-record's own common data (layer, XDATA, handles) is not part // of the scripted model; it defaults on the way in. - let default = "acadrust::entities::EntityCommon::default()"; + let default = "codec::entities::EntityCommon::default()"; from_dict_fields.push_str(&format!(" {}: {default},\n", f.name)); default_fields.push_str(&format!(" {}: {default},\n", f.name)); continue; @@ -861,7 +861,7 @@ fn gen_entity_to_dict(manifest: &Manifest, registry: &TypeRegistry) -> String { for kind in &manifest.type_filter { if manifest.manual_kinds.contains(kind) { arms.push_str(&format!( - " acadrust::EntityType::{kind}(value) => {}_to_dict(vm, value),\n", + " codec::EntityType::{kind}(value) => {}_to_dict(vm, value),\n", snake_case(kind) )); continue; @@ -891,7 +891,7 @@ fn gen_entity_to_dict(manifest: &Manifest, registry: &TypeRegistry) -> String { } body.push_str(" Ok(dict.into())\n"); arms.push_str(&format!( - " acadrust::EntityType::{kind}({var}) => {{\n{body} }}\n" + " codec::EntityType::{kind}({var}) => {{\n{body} }}\n" )); } format!( @@ -899,7 +899,7 @@ fn gen_entity_to_dict(manifest: &Manifest, registry: &TypeRegistry) -> String { /// `"kind"` key (matching the convention every other `ocs.*` dict already /// uses, e.g. XDATA's `{{"kind": ..., "value": ...}}`), plus `"handle"` and /// `"layer"` and every entity-specific field the type registry knows about. -pub(crate) fn entity_to_dict(vm: &VirtualMachine, entity: &acadrust::EntityType) -> PyResult {{ +pub(crate) fn entity_to_dict(vm: &VirtualMachine, entity: &codec::EntityType) -> PyResult {{ match entity {{ {arms} _ => Err(vm.new_value_error( "ocs: this entity kind is not supported by the generic add/update API yet".to_owned(), @@ -925,7 +925,7 @@ fn gen_dict_to_entity(manifest: &Manifest, registry: &TypeRegistry) -> String { } if manifest.manual_kinds.contains(kind) { arms.push_str(&format!( - " \"{kind}\" => acadrust::EntityType::{kind}({}_from_dict(dict, vm)?),\n", + " \"{kind}\" => codec::EntityType::{kind}({}_from_dict(dict, vm)?),\n", snake_case(kind) )); continue; @@ -946,23 +946,23 @@ fn gen_dict_to_entity(manifest: &Manifest, registry: &TypeRegistry) -> String { let constructor = manifest.overrides.get(kind) .and_then(|override_def| override_def.rust_constructor.as_deref()) .map(str::to_owned) - .unwrap_or_else(|| format!("acadrust::entities::{kind}::default()")); + .unwrap_or_else(|| format!("codec::entities::{kind}::default()")); body.push_str(&format!(" let mut {var} = {constructor};\n")); body.push_str(&format!( - " {var}.common.handle = acadrust::Handle::new(get_opt_u64(dict, \"handle\", vm)?);\n" + " {var}.common.handle = codec::Handle::new(get_opt_u64(dict, \"handle\", vm)?);\n" )); body.push_str(&format!( " {var}.common.layer = get_opt_string(dict, \"layer\", \"0\", vm)?;\n" )); if manifest.base_fields.iter().any(|field| field == "owner_handle") { body.push_str(&format!( - " {var}.common.owner_handle = acadrust::Handle::new(get_opt_u64(dict, \"owner_handle\", vm)?);\n" + " {var}.common.owner_handle = codec::Handle::new(get_opt_u64(dict, \"owner_handle\", vm)?);\n" )); } for f in &fields { body.push_str(&format!(" {}\n", f.setter)); } - body.push_str(&format!(" acadrust::EntityType::{kind}({var})\n")); + body.push_str(&format!(" codec::EntityType::{kind}({var})\n")); arms.push_str(&format!(" \"{kind}\" => {{\n{body} }}\n")); } format!( @@ -971,8 +971,8 @@ fn gen_dict_to_entity(manifest: &Manifest, registry: &TypeRegistry) -> String { /// `"required"` (the geometry that actually defines the shape — a line's /// endpoints, a circle's center/radius, ...) error if missing rather than /// silently defaulting to zero; every other field defaults sensibly (what a -/// fresh `acadrust::entities::X::default()` would already hold). -pub(crate) fn dict_to_entity(dict: &rustpython_vm::builtins::PyDictRef, vm: &VirtualMachine) -> PyResult {{ +/// fresh `codec::entities::X::default()` would already hold). +pub(crate) fn dict_to_entity(dict: &rustpython_vm::builtins::PyDictRef, vm: &VirtualMachine) -> PyResult {{ let kind = dict.get_item("kind", vm)?.try_into_value::(vm)?; Ok(match kind.as_str() {{ {arms} other => {{ @@ -990,7 +990,7 @@ fn gen_apply_dict_to_entity(manifest: &Manifest, registry: &TypeRegistry) -> Str for kind in &manifest.type_filter { if manifest.manual_kinds.contains(kind) { arms.push_str(&format!( - " acadrust::EntityType::{kind}(existing_value) => acadrust::EntityType::{kind}({}_apply(existing_value, dict, vm)?),\n", + " codec::EntityType::{kind}(existing_value) => codec::EntityType::{kind}({}_apply(existing_value, dict, vm)?),\n", snake_case(kind) )); continue; @@ -1032,9 +1032,9 @@ fn gen_apply_dict_to_entity(manifest: &Manifest, registry: &TypeRegistry) -> Str for f in &fields { body.push_str(&format!(" {}\n", f.setter)); } - body.push_str(&format!(" acadrust::EntityType::{kind}({var})\n")); + body.push_str(&format!(" codec::EntityType::{kind}({var})\n")); arms.push_str(&format!( - " acadrust::EntityType::{kind}(existing_value) => {{\n{body} }}\n" + " codec::EntityType::{kind}(existing_value) => {{\n{body} }}\n" )); } format!( @@ -1048,10 +1048,10 @@ fn gen_apply_dict_to_entity(manifest: &Manifest, registry: &TypeRegistry) -> Str /// `"kind"` (if present at all — round-tripped from a prior `get_entity()`) /// is never consulted, since an update can't change an entity's shape. pub(crate) fn apply_dict_to_entity( - existing: &acadrust::EntityType, + existing: &codec::EntityType, dict: &rustpython_vm::builtins::PyDictRef, vm: &VirtualMachine, -) -> PyResult {{ +) -> PyResult {{ Ok(match existing {{ {arms} _ => {{ return Err(vm.new_value_error( diff --git a/plugins/opencad-python/entity_manifest.json b/plugins/opencad-python/entity_manifest.json index a714143e4..7aa92e982 100644 --- a/plugins/opencad-python/entity_manifest.json +++ b/plugins/opencad-python/entity_manifest.json @@ -142,7 +142,7 @@ }, "smooth_type": { "exclude": true, - "_comment": "acadrust defines two distinct SmoothSurfaceType enums with the same name (polyline.rs, used by Polyline2D's smooth_surface \u2014 and a separate one private to polyline3d.rs, used here); the type registry's tracer keys purely by name and collapses them, so the generically generated conversion resolves to the wrong one. Needs a hand-written override with the fully qualified acadrust::entities::polyline3d::SmoothSurfaceType path to support, not just a rename." + "_comment": "opencadcodec defines two distinct SmoothSurfaceType enums with the same name (polyline.rs, used by Polyline2D's smooth_surface \u2014 and a separate one private to polyline3d.rs, used here); the type registry's tracer keys purely by name and collapses them, so the generically generated conversion resolves to the wrong one. Needs a hand-written override with the fully qualified opencadcodec::entities::polyline3d::SmoothSurfaceType path to support, not just a rename." } } }, @@ -209,7 +209,7 @@ } }, "Solid": { - "rust_constructor": "acadrust::entities::Solid::new(acadrust::types::Vector3::ZERO, acadrust::types::Vector3::ZERO, acadrust::types::Vector3::ZERO, acadrust::types::Vector3::ZERO)", + "rust_constructor": "codec::entities::Solid::new(codec::types::Vector3::ZERO, codec::types::Vector3::ZERO, codec::types::Vector3::ZERO, codec::types::Vector3::ZERO)", "fields": { "first_corner": { "required": true @@ -226,7 +226,7 @@ } }, "Face3D": { - "rust_constructor": "acadrust::entities::Face3D::new(acadrust::types::Vector3::ZERO, acadrust::types::Vector3::ZERO, acadrust::types::Vector3::ZERO, acadrust::types::Vector3::ZERO)", + "rust_constructor": "codec::entities::Face3D::new(codec::types::Vector3::ZERO, codec::types::Vector3::ZERO, codec::types::Vector3::ZERO, codec::types::Vector3::ZERO)", "fields": { "first_corner": { "required": true @@ -275,7 +275,7 @@ "required": true } }, - "rust_constructor": "acadrust::entities::Insert::new(String::new(), acadrust::types::Vector3::ZERO)" + "rust_constructor": "codec::entities::Insert::new(String::new(), codec::types::Vector3::ZERO)" }, "Tolerance": { "fields": { @@ -391,7 +391,7 @@ }, "flags": { "rust_getter": "(VAR.flags.bits() as i64)", - "rust_setter": "if let Some(v) = dict.get_item_opt(\"flags\", vm)? { let bits = v.try_into_value::(vm)?; VAR.flags = i16::try_from(bits).ok().and_then(acadrust::entities::MLineFlags::from_bits).ok_or_else(|| vm.new_value_error(\"MLine.flags has unknown bits\".to_owned()))?; }" + "rust_setter": "if let Some(v) = dict.get_item_opt(\"flags\", vm)? { let bits = v.try_into_value::(vm)?; VAR.flags = i16::try_from(bits).ok().and_then(codec::entities::MLineFlags::from_bits).ok_or_else(|| vm.new_value_error(\"MLine.flags has unknown bits\".to_owned()))?; }" }, "start_point": { "read_only": true @@ -559,7 +559,7 @@ } }, "PolygonMesh": { - "rust_constructor": "acadrust::entities::PolygonMeshEntity::default()", + "rust_constructor": "codec::entities::PolygonMeshEntity::default()", "fields": { "vertices": { "required": true @@ -951,7 +951,7 @@ }, "raw_flags_90": { "read_only": true, - "_comment": "Semantics unknown in cadcodec; preserved as read, 0 for a new symbol." + "_comment": "Semantics unknown in opencadcodec; preserved as read, 0 for a new symbol." }, "raw_point_record_count": { "read_only": true diff --git a/plugins/opencad-python/examples/create_layout_fixture.rs b/plugins/opencad-python/examples/create_layout_fixture.rs index 26e61e341..b76e2158b 100644 --- a/plugins/opencad-python/examples/create_layout_fixture.rs +++ b/plugins/opencad-python/examples/create_layout_fixture.rs @@ -3,7 +3,7 @@ use std::io::Write; -use ocs_plugin_api::host::acadrust::{ +use ocs_plugin_api::host::codec::{ entities::{Line, Text, Viewport}, CadDocument, DwgWriter, EntityType, Vector3, }; diff --git a/plugins/opencad-python/plugin.toml b/plugins/opencad-python/plugin.toml index ca975ae86..03b168077 100644 --- a/plugins/opencad-python/plugin.toml +++ b/plugins/opencad-python/plugin.toml @@ -10,7 +10,7 @@ repository = "HakanSeven12/OpenCADStudio" [opencad] api_version = 7 # Both filled by release.yml at build time from the actual compiler and -# Cargo.lock's resolved acadrust source. Declare acadrust_source even though +# Cargo.lock's resolved opencadcodec source. Declare acadrust_source even though # it's optional: the host's compatibility check only runs at all if this # field is present, so an omitted field is silently treated as compatible # rather than rejected (see OpenCADStudio's plugin-architecture.md, diff --git a/plugins/opencad-python/src/dimension_model.rs.inc b/plugins/opencad-python/src/dimension_model.rs.inc index bc3f29923..40e0b9f66 100644 --- a/plugins/opencad-python/src/dimension_model.rs.inc +++ b/plugins/opencad-python/src/dimension_model.rs.inc @@ -22,8 +22,8 @@ const DIM_BASE_KEYS: &[&str] = &[ ]; const DIM_READ_ONLY_KEYS: &[&str] = &["subtype", "actual_measurement"]; -fn dimension_subtype_name(dim: &acadrust::entities::Dimension) -> &'static str { - use acadrust::entities::Dimension as D; +fn dimension_subtype_name(dim: &codec::entities::Dimension) -> &'static str { + use codec::entities::Dimension as D; match dim { D::Aligned(_) => "Aligned", D::Linear(_) => "Linear", @@ -38,8 +38,8 @@ fn dimension_subtype_name(dim: &acadrust::entities::Dimension) -> &'static str { } /// Subtype-specific fields in stable order. -fn dimension_fields(dim: &mut acadrust::entities::Dimension) -> Vec<(&'static str, DimField<'_>)> { - use acadrust::entities::Dimension as D; +fn dimension_fields(dim: &mut codec::entities::Dimension) -> Vec<(&'static str, DimField<'_>)> { + use codec::entities::Dimension as D; use DimField::{B, F, V}; match dim { D::Aligned(d) => vec![ @@ -120,12 +120,12 @@ fn dimension_required(subtype: &str) -> &'static [&'static str] { } } -fn dimension_base_mut(dim: &mut acadrust::entities::Dimension) -> &mut acadrust::entities::DimensionBase { +fn dimension_base_mut(dim: &mut codec::entities::Dimension) -> &mut codec::entities::DimensionBase { dim.base_mut() } -fn dim_attachment_point_name(value: acadrust::entities::AttachmentPointType) -> &'static str { - use acadrust::entities::AttachmentPointType as A; +fn dim_attachment_point_name(value: codec::entities::AttachmentPointType) -> &'static str { + use codec::entities::AttachmentPointType as A; match value { A::TopLeft => "TopLeft", A::TopCenter => "TopCenter", @@ -142,8 +142,8 @@ fn dim_attachment_point_name(value: acadrust::entities::AttachmentPointType) -> fn parse_dim_attachment_point( value: &str, vm: &VirtualMachine, -) -> PyResult { - use acadrust::entities::AttachmentPointType as A; +) -> PyResult { + use codec::entities::AttachmentPointType as A; Ok(match value { "TopLeft" => A::TopLeft, "TopCenter" => A::TopCenter, @@ -162,7 +162,7 @@ fn parse_dim_attachment_point( fn dimension_to_dict( vm: &VirtualMachine, - dimension: &acadrust::entities::Dimension, + dimension: &codec::entities::Dimension, ) -> PyResult { let mut dim = dimension.clone(); let dict = vm.ctx.new_dict(); @@ -197,7 +197,7 @@ fn dimension_to_dict( /// Merge `dict` onto `dim`. With `creating`, geometry the subtype needs must /// be present so a script cannot silently default a point to the origin. fn dimension_merge( - dim: &mut acadrust::entities::Dimension, + dim: &mut codec::entities::Dimension, dict: &rustpython_vm::builtins::PyDictRef, creating: bool, vm: &VirtualMachine, @@ -271,8 +271,8 @@ fn dimension_merge( fn dimension_from_dict( dict: &rustpython_vm::builtins::PyDictRef, vm: &VirtualMachine, -) -> PyResult { - use acadrust::entities::*; +) -> PyResult { + use codec::entities::*; let subtype = match dict.get_item_opt("subtype", vm)? { Some(v) => v.try_into_value::(vm)?, None => { @@ -299,17 +299,17 @@ fn dimension_from_dict( }; dimension_merge(&mut dim, dict, true, vm)?; let base = dimension_base_mut(&mut dim); - base.common.handle = acadrust::Handle::new(get_opt_u64(dict, "handle", vm)?); + base.common.handle = codec::Handle::new(get_opt_u64(dict, "handle", vm)?); base.common.layer = get_opt_string(dict, "layer", "0", vm)?; - base.common.owner_handle = acadrust::Handle::new(get_opt_u64(dict, "owner_handle", vm)?); + base.common.owner_handle = codec::Handle::new(get_opt_u64(dict, "owner_handle", vm)?); Ok(dim) } fn dimension_apply( - existing: &acadrust::entities::Dimension, + existing: &codec::entities::Dimension, dict: &rustpython_vm::builtins::PyDictRef, vm: &VirtualMachine, -) -> PyResult { +) -> PyResult { let mut dim = existing.clone(); dimension_merge(&mut dim, dict, false, vm)?; let base = dimension_base_mut(&mut dim); diff --git a/plugins/opencad-python/src/ocs_module.rs b/plugins/opencad-python/src/ocs_module.rs index 4039d33e1..954cad93c 100644 --- a/plugins/opencad-python/src/ocs_module.rs +++ b/plugins/opencad-python/src/ocs_module.rs @@ -13,14 +13,14 @@ mod ocs { use rustpython_vm::convert::TryFromObject; use rustpython_vm::{function::ArgIntoFloat, PyObjectRef, PyResult, VirtualMachine}; - use acadrust::entities::{ + use codec::entities::{ Arc, LwPolyline, LwVertex, MText, TableBuilder, Text, TextHorizontalAlignment, TextVerticalAlignment, }; - use acadrust::objects::ObjectType; - use acadrust::types::{Transform, Vector2, Vector3}; + use codec::objects::ObjectType; + use codec::types::{Transform, Vector2, Vector3}; use ocs_plugin_api::host::{ - acadrust, EntityType, ExtendedDataRecord, Handle, ReaderEntityKind, XDataValue, + codec, EntityType, ExtendedDataRecord, Handle, ReaderEntityKind, XDataValue, }; use crate::host_ctx; @@ -51,7 +51,7 @@ mod ocs { Ok(()) } - fn top_level_blocks(document: &acadrust::CadDocument) -> std::collections::HashSet { + fn top_level_blocks(document: &codec::CadDocument) -> std::collections::HashSet { let mut blocks: std::collections::HashSet = document .objects .values() @@ -958,7 +958,7 @@ mod ocs { options: &rustpython_vm::builtins::PyDictRef, vm: &VirtualMachine, ) -> PyResult { - use acadrust::types::{Color, LineWeight, Transparency}; + use codec::types::{Color, LineWeight, Transparency}; ensure_known_entity_keys( options, "layer", @@ -1342,7 +1342,7 @@ mod ocs { let document = host.document(); let mut inserts = std::collections::HashMap::::new(); for entity in document.entities() { - if let acadrust::entities::EntityType::Insert(insert) = entity { + if let codec::entities::EntityType::Insert(insert) = entity { *inserts.entry(insert.block_name.to_uppercase()).or_default() += 1; } } @@ -1546,7 +1546,7 @@ mod ocs { #[cfg(feature = "experimental-host-model")] #[pyfunction] fn layout_records(vm: &VirtualMachine) -> PyResult { - use acadrust::objects::ObjectType; + use codec::objects::ObjectType; let rows = host_ctx::with_host(|host| { let current = match host.system_variable("CTAB") { Some(ocs_plugin_api::host::HostSettingValue::Text(name)) => name, @@ -1558,7 +1558,7 @@ mod ocs { for entity in document.entities() { let owner = entity.common().owner_handle.value(); *owned.entry(owner).or_default() += 1; - if matches!(entity, acadrust::entities::EntityType::Viewport(_)) { + if matches!(entity, codec::entities::EntityType::Viewport(_)) { *viewports.entry(owner).or_default() += 1; } } @@ -1664,7 +1664,7 @@ mod ocs { #[cfg(feature = "experimental-host-model")] #[pyfunction] fn layer_records(vm: &VirtualMachine) -> PyResult { - use acadrust::types::{LineWeight, Transparency}; + use codec::types::{LineWeight, Transparency}; let rows = host_ctx::with_host(|host| { let document = host.document(); let mut counts = std::collections::HashMap::::new(); @@ -1858,7 +1858,7 @@ mod ocs { "ocs.export_layer_dwg: cannot create {output_path:?}: {error}" )) })?; - if let Err(error) = acadrust::DwgWriter::write_to_writer(&mut file, &snapshot) { + if let Err(error) = codec::DwgWriter::write_to_writer(&mut file, &snapshot) { drop(file); let _ = std::fs::remove_file(path); return Err( @@ -3720,7 +3720,7 @@ mod ocs { y2: ArgIntoFloat, vm: &VirtualMachine, ) -> PyResult { - let line = acadrust::Line::from_coords( + let line = codec::Line::from_coords( x1.into_float(), y1.into_float(), 0.0, @@ -3746,7 +3746,7 @@ mod ocs { vm: &VirtualMachine, ) -> PyResult { let circle = - acadrust::Circle::from_coords(x.into_float(), y.into_float(), 0.0, radius.into_float()); + codec::Circle::from_coords(x.into_float(), y.into_float(), 0.0, radius.into_float()); host_ctx::with_host(|host| { host_ctx::ensure_undo_started(host); host.add_entity(EntityType::Circle(circle)).value() @@ -3768,8 +3768,8 @@ mod ocs { end_deg: ArgIntoFloat, vm: &VirtualMachine, ) -> PyResult { - let center = acadrust::Vector3::new(x.into_float(), y.into_float(), 0.0); - let arc = acadrust::Arc::from_center_radius_angles( + let center = codec::Vector3::new(x.into_float(), y.into_float(), 0.0); + let arc = codec::Arc::from_center_radius_angles( center, radius.into_float(), start_deg.into_float().to_radians(), @@ -3915,7 +3915,7 @@ mod ocs { "ocs.add_points: each point must be [finite x, y, z]".to_owned(), )); } - entities.push(EntityType::Point(acadrust::Point::from_coords( + entities.push(EntityType::Point(codec::Point::from_coords( point[0], point[1], point[2], ))); } @@ -4123,8 +4123,8 @@ mod ocs { /// `Color` as `{"kind": "ByLayer" | "None" | "ByBlock"}`, /// `{"kind": "Index", "value": n}` or /// `{"kind": "Rgb", "value": {"r": .., "g": .., "b": ..}}`. - fn color_to_py_dict(vm: &VirtualMachine, color: &acadrust::types::Color) -> PyResult { - use acadrust::types::Color; + fn color_to_py_dict(vm: &VirtualMachine, color: &codec::types::Color) -> PyResult { + use codec::types::Color; let dict = vm.ctx.new_dict(); match color { Color::ByLayer => dict.set_item("kind", vm.new_pyobj("ByLayer"), vm)?, @@ -4146,8 +4146,8 @@ mod ocs { Ok(dict.into()) } - fn py_to_color_dict(value: PyObjectRef, vm: &VirtualMachine) -> PyResult { - use acadrust::types::Color; + fn py_to_color_dict(value: PyObjectRef, vm: &VirtualMachine) -> PyResult { + use codec::types::Color; let dict = value.try_into_value::(vm)?; let kind = dict.get_item("kind", vm)?.try_into_value::(vm)?; let byte = |v: PyObjectRef, what: &str| -> PyResult { @@ -4682,7 +4682,7 @@ mod ocs { #[test] fn unmapped_canvas_kind_accepts_only_layer_patch() { with_vm(|vm| { - let entity = EntityType::MLine(acadrust::entities::MLine::default()); + let entity = EntityType::MLine(codec::entities::MLine::default()); let patch = vm.ctx.new_dict(); patch.set_item("handle", vm.new_pyobj(1_u64), vm).unwrap(); patch @@ -4868,7 +4868,7 @@ mod ocs { fn insert_is_created_by_block_name_and_preserves_block_identity() { with_vm(|vm| { let mut insert = - acadrust::entities::Insert::new("DOOR", Vector3::new(1.0, 2.0, 0.0)); + codec::entities::Insert::new("DOOR", Vector3::new(1.0, 2.0, 0.0)); insert.set_x_scale(2.0); let original = EntityType::Insert(insert); let rendered = entity_to_dict(vm, &original) @@ -5050,7 +5050,7 @@ mod ocs { assert_eq!(definition.tag, "PART_NO"); assert_eq!(definition.text_style, "Standard"); - let mut embedded = acadrust::entities::MText::default(); + let mut embedded = codec::entities::MText::default(); embedded.value = "first\\Psecond".into(); definition.embedded_mtext = Some(Box::new(embedded)); let entity = EntityType::AttributeDefinition(definition); @@ -5123,7 +5123,7 @@ mod ocs { }; assert_eq!(attribute.common.owner_handle, Handle::new(50)); attribute.attdef_handle = Handle::new(42); - let mut embedded = acadrust::entities::MText::default(); + let mut embedded = codec::entities::MText::default(); embedded.value = "first\\Psecond".into(); attribute.embedded_mtext = Some(Box::new(embedded)); let entity = EntityType::AttributeEntity(attribute); @@ -5274,7 +5274,7 @@ mod ocs { #[test] fn phase_seven_kinds_survive_dwg_round_trip() { with_vm(|vm| { - let mut doc = acadrust::CadDocument::new(); + let mut doc = codec::CadDocument::new(); let mut handles = Vec::new(); for kind in ["Ray", "XLine", "Solid", "Face3D"] { let input = vm.ctx.new_dict(); @@ -5298,8 +5298,8 @@ mod ocs { } let path = std::env::temp_dir() .join(format!("ocs_phase_seven_{}.dwg", std::process::id())); - acadrust::DwgWriter::write_to_file(&path, &doc).unwrap(); - let reopened = acadrust::DwgReader::from_file(&path) + codec::DwgWriter::write_to_file(&path, &doc).unwrap(); + let reopened = codec::DwgReader::from_file(&path) .unwrap() .read() .unwrap(); @@ -5325,7 +5325,7 @@ mod ocs { #[test] fn text_is_in_editable_schema_and_keeps_unmentioned_fields() { with_vm(|vm| { - let mut text = acadrust::entities::Text::new(); + let mut text = codec::entities::Text::new(); text.value = "Before".into(); text.height = 3.5; text.insertion_point = Vector3::new(1.0, 2.0, 0.0); @@ -5363,7 +5363,7 @@ mod ocs { dict.set_item("radius", vm.new_pyobj(5.0), vm).unwrap(); let entity = dict_to_entity(&dict, vm).expect("dict_to_entity"); - let acadrust::EntityType::Circle(circle) = &entity else { + let codec::EntityType::Circle(circle) = &entity else { panic!("expected Circle, got {entity:?}"); }; assert_eq!(circle.common.layer, "Walls"); @@ -5462,7 +5462,7 @@ mod ocs { .unwrap(); let entity = dict_to_entity(&dict, vm).expect("dict_to_entity"); - let acadrust::EntityType::Polyline2D(polyline) = &entity else { + let codec::EntityType::Polyline2D(polyline) = &entity else { panic!("expected Polyline2D, got {entity:?}"); }; assert!(polyline.flags.is_closed()); @@ -5491,13 +5491,13 @@ mod ocs { .unwrap(); let entity = dict_to_entity(&dict, vm).expect("dict_to_entity"); - let acadrust::EntityType::MText(text) = &entity else { + let codec::EntityType::MText(text) = &entity else { panic!("expected MText, got {entity:?}"); }; assert_eq!(text.value, "hello"); assert_eq!( text.attachment_point, - acadrust::entities::AttachmentPoint::MiddleCenter + codec::entities::AttachmentPoint::MiddleCenter ); let round_tripped = entity_to_dict(vm, &entity).expect("entity_to_dict"); @@ -5539,7 +5539,7 @@ mod ocs { let updated = apply_dict_to_entity(&existing, &update_dict, vm) .expect("apply_dict_to_entity"); - let acadrust::EntityType::Line(line) = &updated else { + let codec::EntityType::Line(line) = &updated else { panic!("expected Line, got {updated:?}"); }; assert_eq!(line.common.layer, "Geometry", "layer must be preserved"); @@ -5562,7 +5562,7 @@ mod ocs { .unwrap(); let updated = apply_dict_to_entity(&updated, &update_dict, vm).expect("apply_dict_to_entity"); - let acadrust::EntityType::Line(line) = &updated else { + let codec::EntityType::Line(line) = &updated else { panic!("expected Line, got {updated:?}"); }; assert_eq!(line.common.layer, "Renamed"); @@ -5574,7 +5574,7 @@ mod ocs { /// The concrete Phase 1 acceptance test: create, read, /// edit, delete, save, and reopen a Line and an Ellipse, with common /// properties preserved — exercised against a real - /// `acadrust::CadDocument` and a real DWG round trip (not a fake + /// `codec::CadDocument` and a real DWG round trip (not a fake /// `HostApi`; `dict_to_entity`/`apply_dict_to_entity`/`entity_to_dict` /// are exactly what a live `HostApi`-backed `ocs.add`/`ocs.update`/ /// `ocs.get_entity` call into, so this covers the same logic without @@ -5582,14 +5582,14 @@ mod ocs { #[test] fn line_and_ellipse_survive_create_read_edit_delete_save_reopen() { with_vm(|vm| { - let mut doc = acadrust::CadDocument::new(); + let mut doc = codec::CadDocument::new(); // A layer must exist in the document's layer table before an // entity can reference it — same requirement `set_entity_layer` // above already enforces for a live document. Without this, // the DWG writer silently falls back the entity to layer "0" // instead of erroring, which this test caught the first time // around (it used "Geometry" without registering it first). - doc.layers.add(acadrust::Layer::new("Geometry")).unwrap(); + doc.layers.add(codec::Layer::new("Geometry")).unwrap(); // --- CREATE --- let add_line = vm.ctx.new_dict(); @@ -5643,7 +5643,7 @@ mod ocs { .expect("apply_dict_to_entity Line"); *doc.get_entity_mut(line_handle).unwrap() = updated; - let acadrust::EntityType::Line(line) = doc.get_entity(line_handle).unwrap() else { + let codec::EntityType::Line(line) = doc.get_entity(line_handle).unwrap() else { panic!("expected Line"); }; assert_eq!( @@ -5670,14 +5670,14 @@ mod ocs { "ocs_entity_crud_acceptance_test_{}.dwg", std::process::id() )); - acadrust::DwgWriter::write_to_file(&path, &doc).expect("write dwg"); - let reopened = acadrust::DwgReader::from_file(&path) + codec::DwgWriter::write_to_file(&path, &doc).expect("write dwg"); + let reopened = codec::DwgReader::from_file(&path) .expect("open dwg") .read() .expect("read dwg"); let _ = std::fs::remove_file(&path); - let acadrust::EntityType::Line(reopened_line) = reopened + let codec::EntityType::Line(reopened_line) = reopened .get_entity(line_handle) .expect("line survives save/reopen") else { @@ -5740,7 +5740,7 @@ mod tests { let mut current = None; for line in converter.lines() { let line = line.trim(); - if let Some(arm) = line.strip_prefix("acadrust::EntityType::") { + if let Some(arm) = line.strip_prefix("codec::EntityType::") { if let Some((kind, _)) = arm.split_once("(value) => {") { current = Some(kind.to_owned()); emitted.entry(kind.to_owned()).or_default(); @@ -5800,8 +5800,8 @@ mod tests { #[cfg(feature = "experimental-host-model")] #[test] fn entity_snapshot_uses_catalog_variant_name() { - use ocs_plugin_api::host::acadrust; - let hatch = acadrust::EntityType::Hatch(acadrust::entities::Hatch::default()); + use ocs_plugin_api::host::codec; + let hatch = codec::EntityType::Hatch(codec::entities::Hatch::default()); let value = serde_json::to_value(hatch).unwrap(); let kind = value.as_object().unwrap().keys().next().unwrap(); assert_eq!(kind, "Hatch"); diff --git a/plugins/opencad-python/src/patch_helpers.rs.inc b/plugins/opencad-python/src/patch_helpers.rs.inc index 21a054803..68b68bad2 100644 --- a/plugins/opencad-python/src/patch_helpers.rs.inc +++ b/plugins/opencad-python/src/patch_helpers.rs.inc @@ -3,10 +3,10 @@ /// Apply `patch` (a dict of context fields) onto `existing`, so a script can /// change one context field without resetting the rest. Unknown keys error. fn merge_multileader_context( - existing: &acadrust::entities::MultiLeaderAnnotContext, + existing: &codec::entities::MultiLeaderAnnotContext, patch: PyObjectRef, vm: &VirtualMachine, -) -> PyResult { +) -> PyResult { let patch = patch.try_into_value::(vm)?; let merged = multi_leader_annot_context_to_dict(vm, existing)? .try_into_value::(vm)?; diff --git a/plugins/opencad-python/tools/stage-bundled.sh b/plugins/opencad-python/tools/stage-bundled.sh index 65da83240..bbd586e84 100755 --- a/plugins/opencad-python/tools/stage-bundled.sh +++ b/plugins/opencad-python/tools/stage-bundled.sh @@ -55,12 +55,12 @@ def acadrust_source(path): packages = re.findall(r"(?ms)^\[\[package\]\]\n(.*?)(?=^\[\[package\]\]|\Z)", Path(path).read_text()) sources = [] for package in packages: - if re.search(r'^name = "acadrust"$', package, re.M): + if re.search(r'^name = "opencadcodec"$', package, re.M): match = re.search(r'^source = "([^"]+)"$', package, re.M) if match: sources.append(match.group(1)) if len(sources) != 1: - raise SystemExit(f"expected one acadrust source in {path}, found {len(sources)}") + raise SystemExit(f"expected one opencadcodec source in {path}, found {len(sources)}") return sources[0] manifest = Path("plugin.toml").read_text() diff --git a/src/app/annotation_data.rs b/src/app/annotation_data.rs index 725aa226f..b87342b9d 100644 --- a/src/app/annotation_data.rs +++ b/src/app/annotation_data.rs @@ -1,10 +1,10 @@ use std::collections::{BTreeMap, BTreeSet}; use std::path::{Path, PathBuf}; -use acadrust::entities::{table::CellRange, TableBuilder}; -use acadrust::objects::{ClassObject, ClassObjectData, DataLink, DataLinkCustomData, ObjectType}; -use acadrust::types::{Handle, Vector3}; -use acadrust::{CadDocument, EntityType}; +use codec::entities::{table::CellRange, TableBuilder}; +use codec::objects::{ClassObject, ClassObjectData, DataLink, DataLinkCustomData, ObjectType}; +use codec::types::{Handle, Vector3}; +use codec::{CadDocument, EntityType}; use iced::Task; use super::{Message, ModalKind, OpenCADStudio}; @@ -269,7 +269,7 @@ fn table_style_handle(doc: &CadDocument, name: &str) -> Option { }) } -fn build_table(rows: &[Vec], style: Option, title: Option<&str>) -> acadrust::entities::Table { +fn build_table(rows: &[Vec], style: Option, title: Option<&str>) -> codec::entities::Table { let columns = rows.iter().map(Vec::len).max().unwrap_or(1).max(1); let title_rows = usize::from(title.is_some_and(|value| !value.trim().is_empty())); let mut table = TableBuilder::new(rows.len().max(1) + title_rows, columns) @@ -1029,7 +1029,7 @@ impl OpenCADStudio { cell.has_linked_data = false; cell.data_link_rows = 0; cell.data_link_columns = 0; - cell.state.remove(acadrust::entities::table::CellStateFlags::LINKED | acadrust::entities::table::CellStateFlags::CONTENT_LOCKED | acadrust::entities::table::CellStateFlags::FORMAT_LOCKED); + cell.state.remove(codec::entities::table::CellStateFlags::LINKED | codec::entities::table::CellStateFlags::CONTENT_LOCKED | codec::entities::table::CellStateFlags::FORMAT_LOCKED); } } } diff --git a/src/app/automation.rs b/src/app/automation.rs index f0df43b37..d822e9465 100644 --- a/src/app/automation.rs +++ b/src/app/automation.rs @@ -156,11 +156,11 @@ fn err(msg: impl std::fmt::Display) -> Value { json!({ "ok": false, "error": msg.to_string() }) } -fn v3(v: acadrust::types::Vector3) -> Value { +fn v3(v: codec::types::Vector3) -> Value { json!([v.x, v.y, v.z]) } -pub(crate) fn entity_type_matches(entity: &acadrust::EntityType, requested: &str) -> bool { +pub(crate) fn entity_type_matches(entity: &codec::EntityType, requested: &str) -> bool { if crate::entities::names::ui_name(entity).eq_ignore_ascii_case(requested) { return true; } @@ -170,8 +170,8 @@ pub(crate) fn entity_type_matches(entity: &acadrust::EntityType, requested: &str /// One entity as JSON. Summary mode carries identity only, geometry adds the /// entity's defining values, and full also includes its world bounds. -pub(crate) fn entity_json(e: &acadrust::EntityType, detail: &str) -> Value { - use acadrust::EntityType as E; +pub(crate) fn entity_json(e: &codec::EntityType, detail: &str) -> Value { + use codec::EntityType as E; let c = e.common(); let mut obj = json!({ "handle": format!("{:X}", c.handle.value()), @@ -217,7 +217,7 @@ pub(crate) fn entity_json(e: &acadrust::EntityType, detail: &str) -> Value { map.insert("value".into(), json!(t.value)); map.insert( "text".into(), - json!(acadrust::entities::mtext_format::parse_mtext(&t.value, true) + json!(codec::entities::mtext_format::parse_mtext(&t.value, true) .to_plain_text()), ); map.insert("position".into(), v3(t.insertion_point)); @@ -252,7 +252,7 @@ pub(crate) fn entity_json(e: &acadrust::EntityType, detail: &str) -> Value { .edges .iter() .filter_map(|edge| match edge { - acadrust::entities::BoundaryEdge::Polyline(polyline) => Some( + codec::entities::BoundaryEdge::Polyline(polyline) => Some( json!(polyline .vertices .iter() @@ -305,7 +305,7 @@ fn request_point(req: &Value, key: &str) -> Option<[f64; 2]> { (x.is_finite() && y.is_finite()).then_some([x, y]) } -fn request_handle(value: &Value) -> Option { +fn request_handle(value: &Value) -> Option { value .as_str() .and_then(|value| { @@ -315,7 +315,7 @@ fn request_handle(value: &Value) -> Option { .unwrap_or(value); u64::from_str_radix(value, 16).ok() }) - .map(acadrust::Handle::new) + .map(codec::Handle::new) } fn projected_fields(mut entity: Value, fields: Option<&Vec>) -> Value { @@ -330,10 +330,10 @@ fn projected_fields(mut entity: Value, fields: Option<&Vec>) -> Value { pub(super) fn requested_save_target( req: &Value, - default_version: acadrust::DxfVersion, + default_version: codec::DxfVersion, default_is_dxf: bool, path: Option<&std::path::Path>, -) -> Result<(acadrust::DxfVersion, bool), String> { +) -> Result<(codec::DxfVersion, bool), String> { let version = match req["target_version"].as_str() { Some(value) => crate::io::parse_target_version(value)?, None => default_version, @@ -362,7 +362,7 @@ pub(super) fn requested_save_target( Ok((version, is_dxf)) } -fn document_manifest(document: &acadrust::CadDocument) -> Value { +fn document_manifest(document: &codec::CadDocument) -> Value { let mut by_type: BTreeMap = BTreeMap::new(); let mut by_layer: BTreeMap = BTreeMap::new(); let mut total = 0u64; @@ -754,7 +754,7 @@ impl OpenCADStudio { if let Ok(v) = u64::from_str_radix(h, 16) { self.tabs[i] .scene - .select_entity(acadrust::Handle::new(v), false); + .select_entity(codec::Handle::new(v), false); } } } @@ -762,7 +762,7 @@ impl OpenCADStudio { let type_filter = req["type"].as_str(); let layer_filter = req["layer"].as_str(); if type_filter.is_some() || layer_filter.is_some() { - let handles: Vec = self.tabs[i] + let handles: Vec = self.tabs[i] .scene .document .entities() @@ -922,10 +922,10 @@ impl OpenCADStudio { let Some(second_curve) = crate::entities::curve::entity_curve_xy(second_entity) else { return err("query intersections second entity is not a planar curve"); }; - let crossings = cadkernel::geom2d::intersect( + let crossings = kernel::geom2d::intersect( &first_curve, &second_curve, - cadkernel::geom2d::Tolerance::default(), + kernel::geom2d::Tolerance::default(), ); return json!({ "ok":true, @@ -1082,10 +1082,10 @@ impl OpenCADStudio { let Some(curve) = curve.as_ref().filter(|curve| curve.is_closed()) else { continue; }; - if !cadkernel::geom2d::contains( + if !kernel::geom2d::contains( std::slice::from_ref(curve), point, - cadkernel::geom2d::Tolerance::default(), + kernel::geom2d::Tolerance::default(), ) { continue; } @@ -1093,7 +1093,7 @@ impl OpenCADStudio { let nearest = near.and_then(|point| { curve .as_ref() - .map(|curve| cadkernel::geom2d::closest_point(curve, point)) + .map(|curve| kernel::geom2d::closest_point(curve, point)) }); if near.is_some() && nearest.is_none() { continue; @@ -1185,12 +1185,12 @@ impl OpenCADStudio { if !seen_handles.insert(common.handle.value()) { duplicate_handles.insert(format!("{:X}", common.handle.value())); } - if let acadrust::EntityType::Insert(insert) = entity { + if let codec::EntityType::Insert(insert) = entity { if !block_names.contains(&insert.block_name.to_ascii_uppercase()) { missing_blocks.insert(insert.block_name.clone()); } } - if matches!(entity, acadrust::EntityType::Unknown(_)) { + if matches!(entity, codec::EntityType::Unknown(_)) { unknown_entities += 1; } if let Err(error) = serde_json::to_value(entity) { @@ -1451,8 +1451,8 @@ mod tests { #[cfg(not(target_arch = "wasm32"))] #[test] fn plugin_edit_publication_is_not_repeated_at_message_boundary() { - use acadrust::entities::Point; - use acadrust::EntityType; + use codec::entities::Point; + use codec::EntityType; let mut app = super::OpenCADStudio::new_for_test(); app.automation_op(r#"{"op":"new"}"#); @@ -1525,11 +1525,11 @@ mod tests { fn explicit_target_version_parser_never_silently_defaults() { assert_eq!( crate::io::parse_target_version("R14").unwrap(), - acadrust::DxfVersion::AC1014 + codec::DxfVersion::AC1014 ); assert_eq!( crate::io::parse_target_version("AC1015").unwrap(), - acadrust::DxfVersion::AC1015 + codec::DxfVersion::AC1015 ); assert!(crate::io::parse_target_version("R12").is_err()); assert!(crate::io::parse_target_version("future").is_err()); @@ -1590,14 +1590,14 @@ mod tests { let scene = &mut app.tabs[i].scene; scene.document.add_layout("Review").unwrap(); scene.set_current_layout("Review".to_string()); - let mut viewport = acadrust::entities::Viewport::new(); + let mut viewport = codec::entities::Viewport::new(); viewport.id = 2; viewport.width = 100.0; viewport.height = 50.0; viewport.status.is_on = true; - scene.add_entity(acadrust::EntityType::Viewport(viewport)); + scene.add_entity(codec::EntityType::Viewport(viewport)); for entity in scene.document.entities_mut() { - if let acadrust::EntityType::Viewport(viewport) = entity { + if let codec::EntityType::Viewport(viewport) = entity { viewport.status.grid_on = true; } } @@ -1752,7 +1752,7 @@ mod tests { .document .entities() .filter_map(|e| match e { - acadrust::EntityType::Text(t) => Some(t.value.clone()), + codec::EntityType::Text(t) => Some(t.value.clone()), _ => None, }) .collect(); @@ -1817,8 +1817,8 @@ mod tests { #[test] fn block_reference_query_exposes_instance_attributes() { - use acadrust::entities::{AttributeEntity, EntityType, Insert}; - use acadrust::types::Vector3; + use codec::entities::{AttributeEntity, EntityType, Insert}; + use codec::types::Vector3; let mut app = OpenCADStudio::new_for_test(); app.automation_op(r#"{"op":"new"}"#); @@ -1931,11 +1931,11 @@ mod tests { let mut app = OpenCADStudio::new_for_test(); app.automation_op(r#"{"op":"new"}"#); let i = app.active_tab; - let mut line = acadrust::entities::Line::new(); - line.start = acadrust::types::Vector3::new(0.0, 0.0, 0.0); - line.end = acadrust::types::Vector3::new(10.0, 0.0, 0.0); - let handle = app.tabs[i].scene.document.add_entity(acadrust::EntityType::Line(line)).unwrap(); - let mut br = acadrust::tables::BlockRecord::new("A_CPT"); + let mut line = codec::entities::Line::new(); + line.start = codec::types::Vector3::new(0.0, 0.0, 0.0); + line.end = codec::types::Vector3::new(10.0, 0.0, 0.0); + let handle = app.tabs[i].scene.document.add_entity(codec::EntityType::Line(line)).unwrap(); + let mut br = codec::tables::BlockRecord::new("A_CPT"); br.handle = app.tabs[i].scene.document.allocate_handle(); br.entity_handles = vec![handle]; app.tabs[i].scene.document.block_records.add(br).unwrap(); @@ -2025,11 +2025,11 @@ mod tests { let mut app = OpenCADStudio::new_for_test(); app.automation_op(r#"{"op":"new"}"#); let i = app.active_tab; - let mtext = acadrust::MText::with_value( + let mtext = codec::MText::with_value( "\\A1;10.5000", - acadrust::types::Vector3::new(10.0, 20.0, 0.0), + codec::types::Vector3::new(10.0, 20.0, 0.0), ); - app.tabs[i].scene.add_entity(acadrust::EntityType::MText(mtext)); + app.tabs[i].scene.add_entity(codec::EntityType::MText(mtext)); let q = app.automation_op( r#"{"op":"query","type":"MTEXT","detail":"full","fields":["value","text","height","bounds"]}"#, @@ -2491,7 +2491,7 @@ mod tests { .document .entities() .find_map(|entity| match entity { - acadrust::EntityType::Line(line) => Some(line), + codec::EntityType::Line(line) => Some(line), _ => None, }) .expect("LINE should create one segment"); @@ -2516,7 +2516,7 @@ mod tests { .document .entities() .find_map(|entity| match entity { - acadrust::EntityType::Circle(circle) => Some(circle), + codec::EntityType::Circle(circle) => Some(circle), _ => None, }) .expect("CIRCLE should create one entity"); @@ -2772,7 +2772,7 @@ mod tests { // properties (style, height) to TEXT and MTEXT destinations, not just // the generic layer/color/linetype set. Regression for #361. use crate::command::StepInput; - use acadrust::{EntityType, MText, Text}; + use codec::{EntityType, MText, Text}; let mut app = OpenCADStudio::new_for_test(); app.automation_op(r#"{"op":"new"}"#); @@ -2991,10 +2991,10 @@ mod tests { #[test] fn open_finalizes_and_purges_like_the_ui_open_path() { let mut app = OpenCADStudio::new_for_test(); - let stale = acadrust::Handle::from(9999); + let stale = codec::Handle::from(9999); app.tabs[app.active_tab].scene.solid_models.insert( stale, - cadkernel::brep::make::cuboid([0.0; 3], [1.0; 3]).unwrap(), + kernel::brep::make::cuboid([0.0; 3], [1.0; 3]).unwrap(), ); let path = std::env::temp_dir().join(format!( "ocs_automation_finalize_test_{}.dxf", @@ -3002,16 +3002,16 @@ mod tests { )); let _ = std::fs::remove_file(&path); - let mut doc = acadrust::CadDocument::new(); - let mut good = acadrust::entities::Circle::new(); - good.center = acadrust::types::Vector3::new(5.0, 5.0, 0.0); + let mut doc = codec::CadDocument::new(); + let mut good = codec::entities::Circle::new(); + good.center = codec::types::Vector3::new(5.0, 5.0, 0.0); good.radius = 2.0; - doc.add_entity(acadrust::EntityType::Circle(good)).unwrap(); - let mut corrupt = acadrust::entities::Circle::new(); - corrupt.center = acadrust::types::Vector3::new(1.0, 1.0, 0.0); + doc.add_entity(codec::EntityType::Circle(good)).unwrap(); + let mut corrupt = codec::entities::Circle::new(); + corrupt.center = codec::types::Vector3::new(1.0, 1.0, 0.0); // An absurd radius is rejected; a zero radius is valid. corrupt.radius = 1.0e11; - doc.add_entity(acadrust::EntityType::Circle(corrupt)) + doc.add_entity(codec::EntityType::Circle(corrupt)) .unwrap(); let bytes = crate::io::save_to_bytes(&doc, "dxf", doc.version) .expect("save a document containing a corrupt entity"); @@ -3042,7 +3042,7 @@ mod tests { app.tabs[i].scene.solid_models.insert( stale, - cadkernel::brep::make::cuboid([0.0; 3], [1.0; 3]).unwrap(), + kernel::brep::make::cuboid([0.0; 3], [1.0; 3]).unwrap(), ); assert_eq!(app.automation_op(r#"{"op":"new"}"#)["ok"], true); assert!(app.tabs[i].scene.solid_models.is_empty()); diff --git a/src/app/command_driver/clipboard.rs b/src/app/command_driver/clipboard.rs index cec941d04..7a159f963 100644 --- a/src/app/command_driver/clipboard.rs +++ b/src/app/command_driver/clipboard.rs @@ -68,8 +68,8 @@ impl OpenCADStudio { pub(super) fn handle_replace_many( &mut self, - replacements: Vec<(Handle, Vec)>, - additions: Vec, + replacements: Vec<(Handle, Vec)>, + additions: Vec, ) -> Option> { let i = self.active_tab; if let Some((handle, _)) = replacements @@ -106,7 +106,7 @@ impl OpenCADStudio { pub(super) fn handle_replace_many_continue( &mut self, - replacements: Vec<(Handle, Vec)>, + replacements: Vec<(Handle, Vec)>, ) -> Option> { let i = self.active_tab; if let Some((handle, _)) = replacements @@ -141,7 +141,7 @@ impl OpenCADStudio { pub(super) fn handle_replace_entity( &mut self, handle: Handle, - new_entities: Vec, + new_entities: Vec, ) -> Option> { let i = self.active_tab; if self.reject_locked_edit(i, handle) { @@ -149,7 +149,7 @@ impl OpenCADStudio { } // Detect SPLINEDIT sentinel: a single XLine with a magic layer name. if new_entities.len() == 1 { - if let acadrust::EntityType::XLine(ref xl) = new_entities[0] { + if let codec::EntityType::XLine(ref xl) = new_entities[0] { let op = xl.common.layer.clone(); if op.starts_with("__SPLINEDIT_") { let label = self.history_label_from_active_cmd(i, "SPLINEDIT"); @@ -171,7 +171,7 @@ impl OpenCADStudio { let label = self.history_label_from_active_cmd(i, "TRIM"); self.push_undo_snapshot(i, label); self.tabs[i].scene.erase_entities(&[handle]); - let new_handles: Vec = new_entities + let new_handles: Vec = new_entities .into_iter() .map(|e| self.tabs[i].scene.add_entity(e)) .collect(); @@ -179,7 +179,7 @@ impl OpenCADStudio { for &nh in &new_handles { if matches!( self.tabs[i].scene.document.get_entity(nh), - Some(acadrust::EntityType::Dimension(_)) + Some(codec::EntityType::Dimension(_)) ) { self.tabs[i].scene.invalidate_dim_block_recorded(nh); } @@ -327,7 +327,7 @@ impl OpenCADStudio { if matches!( self.tabs[i].scene.document.get_entity(handle), Some( - acadrust::EntityType::Block(_) | acadrust::EntityType::BlockEnd(_) + codec::EntityType::Block(_) | codec::EntityType::BlockEnd(_) ) ) { needs_geometry_bump = true; @@ -358,7 +358,7 @@ impl OpenCADStudio { for &handle in by_index.iter().filter(|h| !h.is_null()) { if matches!( self.tabs[i].scene.document.get_entity(handle), - Some(acadrust::EntityType::Leader(_)) + Some(codec::EntityType::Leader(_)) ) { let _ = self.tabs[i] .scene @@ -423,15 +423,15 @@ impl OpenCADStudio { /// `sync_displayed_annotation_context`, undo/dirty/selection/echo/panels). /// See the per-helper report in `finalize_paste`. pub(crate) fn paste_entities_kernel( - doc: &mut acadrust::CadDocument, - entities: &[acadrust::EntityType], + doc: &mut codec::CadDocument, + entities: &[codec::EntityType], deps: &crate::app::ClipboardDeps, translate: Option<&crate::command::EntityTransform>, ) -> ( Vec, rustc_hash::FxHashMap, ) { - use acadrust::TableEntry; + use codec::TableEntry; // Port of `merge_dependencies`: recreate missing table records with fresh // handles from the target document. Pure `&mut CadDocument` work. for rec in &deps.layers { @@ -478,7 +478,7 @@ pub(crate) fn paste_entities_kernel( // Same `*D`-block re-point as `finalize_paste` (#290, #161): the // baked dimension geometry lives in WCS, so the paste gets its own // transformed copy of the snapshotted block. - if let acadrust::EntityType::Dimension(d) = &entity { + if let codec::EntityType::Dimension(d) = &entity { let bn = d.base().block_name.clone(); if !bn.trim().is_empty() { if let Some(subs) = deps @@ -491,7 +491,7 @@ pub(crate) fn paste_entities_kernel( crate::command::EntityTransform::Translate(glam::DVec3::ZERO), ); if let Some(new_bn) = define_transformed_block_doc(doc, &subs, &bt) { - if let acadrust::EntityType::Dimension(d) = &mut entity { + if let codec::EntityType::Dimension(d) = &mut entity { d.base_mut().block_name = new_bn; } } @@ -536,7 +536,7 @@ pub(crate) fn paste_entities_kernel( let leader_links: Vec<(Handle, Handle)> = entities .iter() .filter_map(|source| { - let acadrust::EntityType::Leader(leader) = source else { + let codec::EntityType::Leader(leader) = source else { return None; }; let copied_leader = handle_map.get(&source.common().handle).copied()?; @@ -548,7 +548,7 @@ pub(crate) fn paste_entities_kernel( }) .collect(); for (leader_handle, annotation_handle) in leader_links { - if let Some(acadrust::EntityType::Leader(leader)) = doc.get_entity_mut(leader_handle) { + if let Some(codec::EntityType::Leader(leader)) = doc.get_entity_mut(leader_handle) { leader.annotation_handle = annotation_handle; } } @@ -561,14 +561,14 @@ pub(crate) fn paste_entities_kernel( /// two lines are mirrored here), NULL top-level/owner handles, then /// `doc.add_entity`. The `entity_mode` default (Model vs paper space) and all /// tessellation/render caching stay caller-side in the Scene shell. -fn add_entity_clone_doc(doc: &mut acadrust::CadDocument, mut entity: acadrust::EntityType) -> Handle { +fn add_entity_clone_doc(doc: &mut codec::CadDocument, mut entity: codec::EntityType) -> Handle { match &mut entity { - acadrust::EntityType::Insert(ins) => { + codec::EntityType::Insert(ins) => { for att in ins.attributes.iter_mut() { att.common.handle = doc.allocate_handle(); } } - acadrust::EntityType::Polyline3D(p) => { + codec::EntityType::Polyline3D(p) => { for v in p.vertices.iter_mut() { v.handle = doc.allocate_handle(); } @@ -584,10 +584,10 @@ fn add_entity_clone_doc(doc: &mut acadrust::CadDocument, mut entity: acadrust::E /// half of `Scene::define_block_raw` (no-op when the block exists). The /// trailing `bump_geometry` stays caller-side. fn define_block_raw_doc( - doc: &mut acadrust::CadDocument, + doc: &mut codec::CadDocument, name: &str, - base_point: acadrust::types::Vector3, - entities: &[acadrust::EntityType], + base_point: codec::types::Vector3, + entities: &[codec::EntityType], ) { if name.is_empty() || doc.block_records.get(name).is_some() { return; @@ -596,21 +596,21 @@ fn define_block_raw_doc( let br_handle = Handle::new(next); let block_handle = Handle::new(next + 1); let end_handle = Handle::new(next + 2); - let mut block_record = acadrust::tables::BlockRecord::new(name); + let mut block_record = codec::tables::BlockRecord::new(name); block_record.handle = br_handle; block_record.block_entity_handle = block_handle; block_record.block_end_handle = end_handle; if doc.block_records.add(block_record).is_err() { return; } - let mut block = acadrust::entities::Block::new(name, base_point); + let mut block = codec::entities::Block::new(name, base_point); block.common.handle = block_handle; block.common.owner_handle = br_handle; - let _ = doc.add_entity(acadrust::EntityType::Block(block)); - let mut block_end = acadrust::entities::BlockEnd::new(); + let _ = doc.add_entity(codec::EntityType::Block(block)); + let mut block_end = codec::entities::BlockEnd::new(); block_end.common.handle = end_handle; block_end.common.owner_handle = br_handle; - let _ = doc.add_entity(acadrust::EntityType::BlockEnd(block_end)); + let _ = doc.add_entity(codec::EntityType::BlockEnd(block_end)); for mut entity in entities.iter().cloned() { add_entity_clone_doc_inner(doc, &mut entity, br_handle); let _ = doc.add_entity(entity); @@ -619,17 +619,17 @@ fn define_block_raw_doc( /// Shared sub-handle reset + owner stamp for block-definition members. fn add_entity_clone_doc_inner( - doc: &mut acadrust::CadDocument, - entity: &mut acadrust::EntityType, + doc: &mut codec::CadDocument, + entity: &mut codec::EntityType, owner: Handle, ) { match entity { - acadrust::EntityType::Insert(ins) => { + codec::EntityType::Insert(ins) => { for att in ins.attributes.iter_mut() { att.common.handle = doc.allocate_handle(); } } - acadrust::EntityType::Polyline3D(p) => { + codec::EntityType::Polyline3D(p) => { for v in p.vertices.iter_mut() { v.handle = doc.allocate_handle(); } @@ -645,8 +645,8 @@ fn add_entity_clone_doc_inner( /// so this is a verbatim port). Returns the new block name, or `None` when /// `subs` is empty. fn define_transformed_block_doc( - doc: &mut acadrust::CadDocument, - subs: &[acadrust::EntityType], + doc: &mut codec::CadDocument, + subs: &[codec::EntityType], t: &crate::command::EntityTransform, ) -> Option { if subs.is_empty() { @@ -664,19 +664,19 @@ fn define_transformed_block_doc( let br_handle = Handle::new(next); let block_handle = Handle::new(next + 1); let end_handle = Handle::new(next + 2); - let mut br = acadrust::tables::BlockRecord::new(&new_name); + let mut br = codec::tables::BlockRecord::new(&new_name); br.handle = br_handle; br.block_entity_handle = block_handle; br.block_end_handle = end_handle; doc.block_records.add(br).ok()?; - let mut block = acadrust::entities::Block::new(&new_name, acadrust::types::Vector3::ZERO); + let mut block = codec::entities::Block::new(&new_name, codec::types::Vector3::ZERO); block.common.handle = block_handle; block.common.owner_handle = br_handle; - doc.add_entity(acadrust::EntityType::Block(block)).ok()?; - let mut block_end = acadrust::entities::BlockEnd::new(); + doc.add_entity(codec::EntityType::Block(block)).ok()?; + let mut block_end = codec::entities::BlockEnd::new(); block_end.common.handle = end_handle; block_end.common.owner_handle = br_handle; - doc.add_entity(acadrust::EntityType::BlockEnd(block_end)) + doc.add_entity(codec::EntityType::BlockEnd(block_end)) .ok()?; for sub in subs { let mut sub = sub.clone(); @@ -693,9 +693,9 @@ fn define_transformed_block_doc( /// storage (`clipboard`, `clipboard_base`, `clipboard_deps`) and the echo stay /// caller-side in `copy_entities_to_clipboard` / `handle_copy_to_clipboard`. pub(crate) fn copy_to_clipboard_kernel( - doc: &acadrust::CadDocument, + doc: &codec::CadDocument, handles: &[Handle], -) -> (Vec, crate::app::ClipboardDeps) { +) -> (Vec, crate::app::ClipboardDeps) { let entities: Vec<_> = handles .iter() .filter_map(|&handle| doc.get_entity(handle).cloned()) @@ -705,7 +705,7 @@ pub(crate) fn copy_to_clipboard_kernel( } fn recreate_ext_subtree( - doc: &mut acadrust::CadDocument, + doc: &mut codec::CadDocument, cap: &crate::app::ClipExtObjects, entity_handle: Option, ) -> Option { @@ -733,12 +733,12 @@ fn recreate_ext_subtree( /// Replace references to a clipboard entity inside one recreated extension /// dictionary graph after its final block-owned handle becomes known. pub(crate) fn remap_ext_subtree_reference( - doc: &mut acadrust::CadDocument, + doc: &mut codec::CadDocument, root: Handle, source_entity: Handle, target_entity: Handle, ) { - use acadrust::objects::ObjectType; + use codec::objects::ObjectType; use rustc_hash::FxHashSet; use std::collections::HashMap; @@ -779,12 +779,12 @@ pub(crate) fn remap_ext_subtree_reference( /// through `remap` (a handle still in the source space stays unchanged, which is /// correct for cross-references that point outside the captured subtree). pub(crate) fn remap_object( - obj: &mut acadrust::objects::ObjectType, - new_handle: acadrust::Handle, - remap: &std::collections::HashMap, + obj: &mut codec::objects::ObjectType, + new_handle: codec::Handle, + remap: &std::collections::HashMap, ) { - use acadrust::objects::ObjectType; - let map = |h: acadrust::Handle| remap.get(&h).copied().unwrap_or(h); + use codec::objects::ObjectType; + let map = |h: codec::Handle| remap.get(&h).copied().unwrap_or(h); match obj { ObjectType::Dictionary(d) => { d.handle = new_handle; @@ -819,7 +819,7 @@ pub(crate) fn remap_object( x.handle = new_handle; x.owner = map(x.owner); for entry in &mut x.entries { - if let acadrust::objects::XRecordValue::Handle(handle) = &mut entry.value { + if let codec::objects::XRecordValue::Handle(handle) = &mut entry.value { *handle = map(*handle); } } @@ -842,13 +842,13 @@ pub(crate) fn remap_object( } context.scale = map(context.scale); match &mut context.kind { - acadrust::objects::ObjectContextKind::Dim(dimension) => { + codec::objects::ObjectContextKind::Dim(dimension) => { dimension.block = map(dimension.block); } - acadrust::objects::ObjectContextKind::HatchView(hatch) => { + codec::objects::ObjectContextKind::HatchView(hatch) => { hatch.view = map(hatch.view); } - acadrust::objects::ObjectContextKind::MTextAttribute(attribute) => { + codec::objects::ObjectContextKind::MTextAttribute(attribute) => { if let Some(embedded) = &mut attribute.context { embedded.owner_handle = map(embedded.owner_handle); for reactor in &mut embedded.reactors { @@ -860,7 +860,7 @@ pub(crate) fn remap_object( embedded.scale = map(embedded.scale); } } - acadrust::objects::ObjectContextKind::MLeader(mleader) => { + codec::objects::ObjectContextKind::MLeader(mleader) => { if let Some(handle) = &mut mleader.text_style_handle { *handle = map(*handle); } @@ -895,29 +895,29 @@ pub(crate) fn remap_object( mod clipboard_xdict_clone_tests { use super::recreate_ext_subtree; use super::remap_object; - use acadrust::Handle; + use codec::Handle; // Same re-export path `commands/blocks.rs:356` uses — this import proves // the `mod.rs` re-export still covers external callers. use super::super::remap_ext_subtree_reference; - use acadrust::objects::{ObjectType, XRecordValue}; + use codec::objects::{ObjectType, XRecordValue}; /// A dictionary root pointing at one XRecord leaf that references the /// source entity — the minimal shape of a captured XCLIP filter graph. fn xdict_capture_fixture() -> crate::app::ClipExtObjects { - let mut src = acadrust::CadDocument::new(); + let mut src = codec::CadDocument::new(); let src_entity = src.allocate_handle(); let src_root = src.allocate_handle(); let src_leaf = src.allocate_handle(); - let mut dict = acadrust::objects::Dictionary::new(); + let mut dict = codec::objects::Dictionary::new(); dict.handle = src_root; dict.owner = src_entity; dict.add_entry("XCLIP", src_leaf); - let mut xrec = acadrust::objects::XRecord::new(); + let mut xrec = codec::objects::XRecord::new(); xrec.handle = src_leaf; xrec.owner = src_root; - xrec.entries.push(acadrust::objects::XRecordEntry::new( + xrec.entries.push(codec::objects::XRecordEntry::new( 340, XRecordValue::Handle(src_entity), )); @@ -934,7 +934,7 @@ mod clipboard_xdict_clone_tests { } } - fn leaf_of(doc: &acadrust::CadDocument, root: Handle) -> Handle { + fn leaf_of(doc: &codec::CadDocument, root: Handle) -> Handle { match doc.objects.get(&root) { Some(ObjectType::Dictionary(d)) => { assert_eq!(d.entries.len(), 1); @@ -949,7 +949,7 @@ mod clipboard_xdict_clone_tests { #[test] fn cross_drawing_clone_remaps_xdict_handles() { let cap = xdict_capture_fixture(); - let mut dst = acadrust::CadDocument::new(); + let mut dst = codec::CadDocument::new(); let before: Vec = dst.objects.keys().copied().collect(); let dst_entity = dst.allocate_handle(); @@ -990,7 +990,7 @@ mod clipboard_xdict_clone_tests { #[test] fn cross_drawing_clone_two_phase_remap_resolves_reference() { let cap = xdict_capture_fixture(); - let mut dst = acadrust::CadDocument::new(); + let mut dst = codec::CadDocument::new(); let before_len = dst.objects.len(); let root = @@ -1021,7 +1021,7 @@ mod clipboard_xdict_clone_tests { #[test] fn remap_object_rewrites_dictionary_handles() { use std::collections::HashMap; - let mut scratch = acadrust::CadDocument::new(); + let mut scratch = codec::CadDocument::new(); let old_self = scratch.allocate_handle(); let old_owner = scratch.allocate_handle(); let old_child = scratch.allocate_handle(); @@ -1029,7 +1029,7 @@ mod clipboard_xdict_clone_tests { let new_owner = scratch.allocate_handle(); let new_child = scratch.allocate_handle(); - let mut dict = acadrust::objects::Dictionary::new(); + let mut dict = codec::objects::Dictionary::new(); dict.handle = old_self; dict.owner = old_owner; dict.add_entry("K", old_child); @@ -1051,17 +1051,17 @@ mod clipboard_xdict_clone_tests { mod copy_to_clipboard_kernel_tests { use super::copy_to_clipboard_kernel; - fn line_on(layer: &str) -> acadrust::EntityType { - let mut line = acadrust::entities::Line::new(); + fn line_on(layer: &str) -> codec::EntityType { + let mut line = codec::entities::Line::new(); line.common.layer = layer.to_string(); - acadrust::EntityType::Line(line) + codec::EntityType::Line(line) } #[test] fn clones_entities_and_captures_layer_deps() { - let mut doc = acadrust::CadDocument::new(); + let mut doc = codec::CadDocument::new(); doc.layers - .add(acadrust::tables::Layer::new("WALLS")) + .add(codec::tables::Layer::new("WALLS")) .expect("add layer"); let a = doc.add_entity(line_on("WALLS")).unwrap(); let b = doc.add_entity(line_on("WALLS")).unwrap(); @@ -1078,7 +1078,7 @@ mod copy_to_clipboard_kernel_tests { #[test] fn skips_handles_missing_from_the_document() { - let mut doc = acadrust::CadDocument::new(); + let mut doc = codec::CadDocument::new(); let kept = doc.add_entity(line_on("0")).unwrap(); let missing = doc.allocate_handle(); @@ -1090,7 +1090,7 @@ mod copy_to_clipboard_kernel_tests { #[test] fn empty_selection_yields_empty_payload() { - let doc = acadrust::CadDocument::new(); + let doc = codec::CadDocument::new(); let (entities, deps) = copy_to_clipboard_kernel(&doc, &[]); @@ -1103,32 +1103,32 @@ mod copy_to_clipboard_kernel_tests { #[cfg(test)] mod paste_entities_kernel_tests { use super::{copy_to_clipboard_kernel, paste_entities_kernel}; - use acadrust::Handle; + use codec::Handle; - fn line_on(layer: &str) -> acadrust::EntityType { - let mut line = acadrust::entities::Line::new(); + fn line_on(layer: &str) -> codec::EntityType { + let mut line = codec::entities::Line::new(); line.common.layer = layer.to_string(); - acadrust::EntityType::Line(line) + codec::EntityType::Line(line) } #[test] fn pastes_with_fresh_handles_and_translate() { - let mut seed = acadrust::CadDocument::new(); + let mut seed = codec::CadDocument::new(); seed.layers - .add(acadrust::tables::Layer::new("WALLS")) + .add(codec::tables::Layer::new("WALLS")) .expect("add layer"); // Position the seed line away from the origin so the translate is visible. - let mut seed_line = acadrust::entities::Line::from_points( - acadrust::types::Vector3::new(0.0, 0.0, 0.0), - acadrust::types::Vector3::new(10.0, 0.0, 0.0), + let mut seed_line = codec::entities::Line::from_points( + codec::types::Vector3::new(0.0, 0.0, 0.0), + codec::types::Vector3::new(10.0, 0.0, 0.0), ); seed_line.common.layer = "WALLS".to_string(); let seed_handle = seed - .add_entity(acadrust::EntityType::Line(seed_line)) + .add_entity(codec::EntityType::Line(seed_line)) .unwrap(); let (entities, deps) = copy_to_clipboard_kernel(&seed, &[seed_handle]); - let mut dst = acadrust::CadDocument::new(); + let mut dst = codec::CadDocument::new(); let delta = glam::DVec3::new(5.0, 7.0, 0.0); let translate = crate::command::EntityTransform::Translate(delta); let (handles, handle_map) = @@ -1140,7 +1140,7 @@ mod paste_entities_kernel_tests { assert_ne!(pasted, seed_handle, "paste must use a fresh handle"); assert_eq!(handle_map.get(&seed_handle), Some(&pasted)); let line = match dst.get_entity(pasted) { - Some(acadrust::EntityType::Line(l)) => l, + Some(codec::EntityType::Line(l)) => l, other => panic!("expected pasted Line, got {other:?}"), }; assert!((line.start.x - 5.0).abs() < 1e-9, "start.x = {}", line.start.x); @@ -1153,23 +1153,23 @@ mod paste_entities_kernel_tests { #[test] fn remaps_leader_annotation_link_to_pasted_handles() { - let mut seed = acadrust::CadDocument::new(); + let mut seed = codec::CadDocument::new(); let note = seed - .add_entity(acadrust::EntityType::MText(acadrust::entities::MText::new())) + .add_entity(codec::EntityType::MText(codec::entities::MText::new())) .unwrap(); - let mut leader = acadrust::entities::Leader::default(); + let mut leader = codec::entities::Leader::default(); leader.annotation_handle = note; let leader_handle = seed - .add_entity(acadrust::EntityType::Leader(leader)) + .add_entity(codec::EntityType::Leader(leader)) .unwrap(); let (entities, deps) = copy_to_clipboard_kernel(&seed, &[leader_handle, note]); - let mut dst = acadrust::CadDocument::new(); + let mut dst = codec::CadDocument::new(); let (handles, _) = paste_entities_kernel(&mut dst, &entities, &deps, None); assert_eq!(handles.len(), 2); let pasted_leader = match dst.get_entity(handles[0]) { - Some(acadrust::EntityType::Leader(l)) => l, + Some(codec::EntityType::Leader(l)) => l, other => panic!("expected pasted Leader, got {other:?}"), }; assert_eq!( @@ -1182,8 +1182,8 @@ mod paste_entities_kernel_tests { fn recreates_ext_subtree_and_repoints_xdictionary() { // Minimal XCLIP-shaped capture: a dictionary root over one XRecord leaf // that references the source entity (mirrors clipboard_xdict_clone_tests). - use acadrust::objects::{Dictionary, ObjectType, XRecord, XRecordEntry, XRecordValue}; - let mut seed = acadrust::CadDocument::new(); + use codec::objects::{Dictionary, ObjectType, XRecord, XRecordEntry, XRecordValue}; + let mut seed = codec::CadDocument::new(); let seed_entity = seed.add_entity(line_on("0")).unwrap(); let src_root = seed.allocate_handle(); let src_leaf = seed.allocate_handle(); @@ -1206,7 +1206,7 @@ mod paste_entities_kernel_tests { let (entities, deps) = copy_to_clipboard_kernel(&seed, &[seed_entity]); assert_eq!(deps.ext_objects.len(), 1, "capture must snapshot the xdict"); - let mut dst = acadrust::CadDocument::new(); + let mut dst = codec::CadDocument::new(); let before: Vec = dst.objects.keys().copied().collect(); let (handles, _) = paste_entities_kernel(&mut dst, &entities, &deps, None); @@ -1243,7 +1243,7 @@ mod paste_entities_kernel_tests { mod headless_reuse_proof_tests { // Task 5 reuse proof — headless only. This module constructs NO // `OpenCADStudio` value and touches no UI/scene state: only - // `acadrust::CadDocument`, entities, and the shared kernels + // `codec::CadDocument`, entities, and the shared kernels // (`copy_to_clipboard_kernel`, `paste_entities_kernel`, // `match_layer_kernel`, `match_properties_kernel` + `MatchOpts`). use super::{copy_to_clipboard_kernel, paste_entities_kernel}; @@ -1252,25 +1252,25 @@ mod headless_reuse_proof_tests { #[test] fn headless_copy_paste_then_match_kernels_share_one_path() { // Phase 1: copy in doc A, paste into doc B with a translate. - let mut doc_a = acadrust::CadDocument::new(); + let mut doc_a = codec::CadDocument::new(); doc_a .layers - .add(acadrust::tables::Layer::new("WALLS")) + .add(codec::tables::Layer::new("WALLS")) .expect("add layer"); - let mut seed_line = acadrust::entities::Line::from_points( - acadrust::types::Vector3::new(0.0, 0.0, 0.0), - acadrust::types::Vector3::new(10.0, 0.0, 0.0), + let mut seed_line = codec::entities::Line::from_points( + codec::types::Vector3::new(0.0, 0.0, 0.0), + codec::types::Vector3::new(10.0, 0.0, 0.0), ); seed_line.common.layer = "WALLS".to_string(); let seed_handle = doc_a - .add_entity(acadrust::EntityType::Line(seed_line)) + .add_entity(codec::EntityType::Line(seed_line)) .unwrap(); let (entities, deps) = copy_to_clipboard_kernel(&doc_a, &[seed_handle]); assert_eq!(entities.len(), 1); assert_eq!(entities[0].common().layer, "WALLS"); - let mut doc_b = acadrust::CadDocument::new(); + let mut doc_b = codec::CadDocument::new(); let translate = crate::command::EntityTransform::Translate(glam::DVec3::new(5.0, 7.0, 0.0)); let (handles, handle_map) = @@ -1283,7 +1283,7 @@ mod headless_reuse_proof_tests { assert_eq!(handle_map.get(&seed_handle), Some(&pasted)); assert!(doc_b.layers.contains("WALLS"), "layer dep must follow"); match doc_b.get_entity(pasted) { - Some(acadrust::EntityType::Line(l)) => { + Some(codec::EntityType::Line(l)) => { assert_eq!(l.common.layer, "WALLS"); assert!((l.start.x - 5.0).abs() < 1e-9, "start.x = {}", l.start.x); assert!((l.start.y - 7.0).abs() < 1e-9, "start.y = {}", l.start.y); @@ -1293,19 +1293,19 @@ mod headless_reuse_proof_tests { } // Phase 2: match kernels on a src/dst pair in the same headless doc. - let mut src_line = acadrust::entities::Line::new(); + let mut src_line = codec::entities::Line::new(); src_line.common.layer = "WALLS".to_string(); src_line.common.linetype_scale = 2.5; src_line.thickness = 1.25; let src = doc_b - .add_entity(acadrust::EntityType::Line(src_line)) + .add_entity(codec::EntityType::Line(src_line)) .unwrap(); - let mut dst_line = acadrust::entities::Line::new(); + let mut dst_line = codec::entities::Line::new(); dst_line.common.layer = "0".to_string(); dst_line.common.linetype_scale = 1.0; dst_line.thickness = 0.0; let dst = doc_b - .add_entity(acadrust::EntityType::Line(dst_line)) + .add_entity(codec::EntityType::Line(dst_line)) .unwrap(); let count = match_layer_kernel(&mut doc_b, &[dst], src).expect("match layer"); @@ -1322,7 +1322,7 @@ mod headless_reuse_proof_tests { match_properties_kernel(&src_clone, dst_entity, &MatchOpts::all()); } match doc_b.get_entity(dst) { - Some(acadrust::EntityType::Line(l)) => { + Some(codec::EntityType::Line(l)) => { assert_eq!(l.common.layer, "WALLS"); assert!((l.common.linetype_scale - 2.5).abs() < 1e-9); assert!((l.thickness - 1.25).abs() < 1e-9); diff --git a/src/app/command_driver/constraint.rs b/src/app/command_driver/constraint.rs index 3e7e4c5bf..7a567540d 100644 --- a/src/app/command_driver/constraint.rs +++ b/src/app/command_driver/constraint.rs @@ -253,7 +253,7 @@ impl OpenCADStudio { let resolved = { let document = &self.tabs[i].scene.document; let world = - acadrust::types::Vector3::new(pick.point.x, pick.point.y, pick.point.z); + codec::types::Vector3::new(pick.point.x, pick.point.y, pick.point.z); let reference = match pick.handle { Some(handle) => { nearest_parametric_point_on_entity(document, scope, handle, world) @@ -426,7 +426,7 @@ impl OpenCADStudio { entity.as_entity_mut().set_layer( crate::scene::parametric_constraints::DYNAMIC_DIMENSION_LAYER.to_string(), ); - entity.common_mut().color = acadrust::types::Color::Rgb { + entity.common_mut().color = codec::types::Color::Rgb { r: 103, g: 109, b: 118, @@ -614,7 +614,7 @@ impl OpenCADStudio { entity.as_entity_mut().set_layer( crate::scene::parametric_constraints::DYNAMIC_DIMENSION_LAYER.to_string(), ); - entity.common_mut().color = acadrust::types::Color::Rgb { + entity.common_mut().color = codec::types::Color::Rgb { r: 103, g: 109, b: 118, @@ -795,7 +795,7 @@ impl OpenCADStudio { entity.as_entity_mut().set_layer( crate::scene::parametric_constraints::DYNAMIC_DIMENSION_LAYER.to_string(), ); - entity.common_mut().color = acadrust::types::Color::Rgb { + entity.common_mut().color = codec::types::Color::Rgb { r: 103, g: 109, b: 118, @@ -1000,7 +1000,7 @@ impl OpenCADStudio { let scope = self.tabs[i].current_parametric_scope(); let document = &self.tabs[i].scene.document; let world = - acadrust::types::Vector3::new(pick.point.x, pick.point.y, pick.point.z); + codec::types::Vector3::new(pick.point.x, pick.point.y, pick.point.z); let resolved = match (pick.whole_curve, pick.handle) { (true, Some(handle)) => { parametric_curve_ref_for_pick(document, scope, handle, world) @@ -1059,7 +1059,7 @@ impl OpenCADStudio { let found = nearest_parametric_point( &self.tabs[i].scene.document, scope, - acadrust::types::Vector3::new(pick.point.x, pick.point.y, pick.point.z), + codec::types::Vector3::new(pick.point.x, pick.point.y, pick.point.z), None, ) .is_some(); @@ -1097,7 +1097,7 @@ impl OpenCADStudio { let axis = if vertical { "Vertical" } else { "Horizontal" }; let scope = self.tabs[i].current_parametric_scope(); let to_world = |point: glam::DVec3| { - acadrust::types::Vector3::new(point.x, point.y, point.z) + codec::types::Vector3::new(point.x, point.y, point.z) }; let (refs, initial_fixed) = match selection { HorizontalConstraintSelection::Reference(reference) => { @@ -1175,7 +1175,7 @@ impl OpenCADStudio { )); return Some(Task::none()); } - let direction = acadrust::types::Vector3::new( + let direction = codec::types::Vector3::new( direction.x / axis_length, direction.y / axis_length, 0.0, @@ -1337,7 +1337,7 @@ impl OpenCADStudio { let scope = self.tabs[i].current_parametric_scope(); let to_world = |point: glam::DVec3| { - acadrust::types::Vector3::new(point.x, point.y, point.z) + codec::types::Vector3::new(point.x, point.y, point.z) }; let resolve_point = |pick: crate::command::CoincidentPick| { if let Some(handle) = pick.handle { @@ -1692,7 +1692,7 @@ impl OpenCADStudio { unreachable!("router only routes the matching variant"); }; let scope = self.tabs[i].current_parametric_scope(); - let to_world = |p: glam::DVec3| acadrust::types::Vector3::new(p.x, p.y, p.z); + let to_world = |p: glam::DVec3| codec::types::Vector3::new(p.x, p.y, p.z); let resolve = |pick: crate::command::CoincidentPick, exclude: Option| { if pick.whole_curve { @@ -1877,7 +1877,7 @@ impl OpenCADStudio { unreachable!("router only routes the matching variant"); }; let scope = self.tabs[i].current_parametric_scope(); - let to_world = |p: glam::DVec3| acadrust::types::Vector3::new(p.x, p.y, p.z); + let to_world = |p: glam::DVec3| codec::types::Vector3::new(p.x, p.y, p.z); let resolved = crate::scene::parametric_constraints::nearest_parametric_point( &self.tabs[i].scene.document, scope, @@ -1922,7 +1922,7 @@ impl OpenCADStudio { ) -> Option> { let i = self.active_tab; let scope = self.tabs[i].current_parametric_scope(); - let to_world = |p: glam::DVec3| acadrust::types::Vector3::new(p.x, p.y, p.z); + let to_world = |p: glam::DVec3| codec::types::Vector3::new(p.x, p.y, p.z); let mut refs = Vec::with_capacity(4); for p in points { let Some(r) = crate::scene::parametric_constraints::nearest_parametric_point( diff --git a/src/app/command_driver/dim_edit.rs b/src/app/command_driver/dim_edit.rs index 169e60666..a074c8285 100644 --- a/src/app/command_driver/dim_edit.rs +++ b/src/app/command_driver/dim_edit.rs @@ -33,9 +33,9 @@ impl OpenCADStudio { let i = self.active_tab; let valid = matches!( self.tabs[i].scene.document.get_entity(dimension), - Some(acadrust::EntityType::Dimension( - acadrust::entities::Dimension::Linear(_) - | acadrust::entities::Dimension::Aligned(_) + Some(codec::EntityType::Dimension( + codec::entities::Dimension::Linear(_) + | codec::entities::Dimension::Aligned(_) )) ); if !valid { @@ -45,12 +45,12 @@ impl OpenCADStudio { } else if !self.reject_locked_edit(i, dimension) { self.push_undo_snapshot(i, "DIMJOGLINE"); let values = point.map(|point| { - use acadrust::xdata::XDataValue; + use codec::xdata::XDataValue; vec![ XDataValue::Integer16(387), XDataValue::Integer16(3), XDataValue::Integer16(389), - XDataValue::Point3D(acadrust::types::Vector3::new( + XDataValue::Point3D(codec::types::Vector3::new( point.x, point.y, point.z, )), ] @@ -124,7 +124,7 @@ fn segment_intersection_xy( first: (glam::DVec3, glam::DVec3), second: (glam::DVec3, glam::DVec3), ) -> Option<(glam::DVec3, glam::DVec3)> { - let gap = cadkernel::space::segment_break_gap_xy( + let gap = kernel::space::segment_break_gap_xy( [first.0.to_array(), first.1.to_array()], [second.0.to_array(), second.1.to_array()], 0.25, @@ -135,25 +135,25 @@ fn segment_intersection_xy( )) } -fn break_object_handle(document: &acadrust::CadDocument, dimension: Handle) -> Option { +fn break_object_handle(document: &codec::CadDocument, dimension: Handle) -> Option { document.objects.iter().find_map(|(handle, object)| { - let acadrust::objects::ObjectType::DataObject(object) = object else { + let codec::objects::ObjectType::DataObject(object) = object else { return None; }; - let acadrust::objects::DataObjectData::BreakData(data) = &object.data else { + let codec::objects::DataObjectData::BreakData(data) = &object.data else { return None; }; (data.dimension_reference == dimension).then_some(*handle) }) } -fn remove_dimension_break_data(document: &mut acadrust::CadDocument, dimension: Handle) -> bool { +fn remove_dimension_break_data(document: &mut codec::CadDocument, dimension: Handle) -> bool { let handles: Vec<_> = document .objects .iter() .filter_map(|(handle, object)| match object { - acadrust::objects::ObjectType::DataObject(object) => match &object.data { - acadrust::objects::DataObjectData::BreakData(data) + codec::objects::ObjectType::DataObject(object) => match &object.data { + codec::objects::DataObjectData::BreakData(data) if data.dimension_reference == dimension => { Some(*handle) @@ -170,7 +170,7 @@ fn remove_dimension_break_data(document: &mut acadrust::CadDocument, dimension: document.objects.remove(handle); } if let Some(dictionary_handle) = document.extension_dictionary_handle(dimension) { - if let Some(acadrust::objects::ObjectType::Dictionary(dictionary)) = + if let Some(codec::objects::ObjectType::Dictionary(dictionary)) = document.objects.get_mut(&dictionary_handle) { dictionary.entries.retain(|(name, handle)| { @@ -185,12 +185,12 @@ fn remove_dimension_break_data(document: &mut acadrust::CadDocument, dimension: } fn write_dimension_break_data( - document: &mut acadrust::CadDocument, + document: &mut codec::CadDocument, dimension: Handle, reserved_reference: Handle, - references: Vec, + references: Vec, ) -> bool { - use acadrust::objects::{BreakData, DataObject, DataObjectData, Dictionary, ObjectType}; + use codec::objects::{BreakData, DataObject, DataObjectData, Dictionary, ObjectType}; if references.is_empty() { return remove_dimension_break_data(document, dimension); @@ -252,15 +252,15 @@ fn break_reference( reference_type: i32, first: glam::DVec3, second: glam::DVec3, -) -> acadrust::objects::BreakPointReference { - acadrust::objects::BreakPointReference { +) -> codec::objects::BreakPointReference { + codec::objects::BreakPointReference { version: 0, reserved: 0, reference_type, flags: 0, identifier, - first_point: acadrust::types::Vector3::new(first.x, first.y, first.z), - second_point: acadrust::types::Vector3::new(second.x, second.y, second.z), + first_point: codec::types::Vector3::new(first.x, first.y, first.z), + second_point: codec::types::Vector3::new(second.x, second.y, second.z), trailing_version: 0, } } @@ -278,7 +278,7 @@ fn apply_dimbreak( .filter(|handle| { matches!( scene.document.get_entity(*handle), - Some(acadrust::EntityType::Dimension(_)) + Some(codec::EntityType::Dimension(_)) ) && scene.locked_layer_name(*handle).is_none() }) .collect(); @@ -326,8 +326,8 @@ fn apply_dimbreak( let existing: Vec<_> = break_object_handle(&scene.document, *dimension) .and_then(|handle| scene.document.objects.get(&handle)) .and_then(|object| match object { - acadrust::objects::ObjectType::DataObject(object) => match &object.data { - acadrust::objects::DataObjectData::BreakData(data) => { + codec::objects::ObjectType::DataObject(object) => match &object.data { + codec::objects::DataObjectData::BreakData(data) => { Some(data.point_references.clone()) } _ => None, @@ -408,9 +408,9 @@ fn apply_dimspace( others: &[Handle], requested_spacing: Option, ) -> bool { - use acadrust::entities::Dimension; + use codec::entities::Dimension; let (axis, normal, definition, auto_spacing) = match scene.document.get_entity(base_h) { - Some(acadrust::EntityType::Dimension(dimension @ Dimension::Linear(d))) => { + Some(codec::EntityType::Dimension(dimension @ Dimension::Linear(d))) => { let spacing = dimension_auto_spacing( &scene.document, dimension, @@ -427,7 +427,7 @@ fn apply_dimspace( spacing, ) } - Some(acadrust::EntityType::Dimension(dimension @ Dimension::Aligned(d))) => { + Some(codec::EntityType::Dimension(dimension @ Dimension::Aligned(d))) => { let spacing = dimension_auto_spacing( &scene.document, dimension, @@ -450,7 +450,7 @@ fn apply_dimspace( } _ => return false, }; - let Some(frame) = cadkernel::space::dimension_spacing_frame(axis, normal, definition) else { + let Some(frame) = kernel::space::dimension_spacing_frame(axis, normal, definition) else { return false; }; @@ -459,11 +459,11 @@ fn apply_dimspace( for (idx, &h) in others.iter().enumerate() { let target = frame.coordinate + effective_spacing * (idx + 1) as f64; let mut changed = false; - if let Some(acadrust::EntityType::Dimension(dim)) = scene.document.get_entity_mut(h) { - let slide = |p: &mut acadrust::types::Vector3| { + if let Some(codec::EntityType::Dimension(dim)) = scene.document.get_entity_mut(h) { + let slide = |p: &mut codec::types::Vector3| { let point = - cadkernel::space::move_to_dimension_spacing([p.x, p.y, p.z], frame, target); - *p = acadrust::types::Vector3::new(point[0], point[1], point[2]); + kernel::space::move_to_dimension_spacing([p.x, p.y, p.z], frame, target); + *p = codec::types::Vector3::new(point[0], point[1], point[2]); }; match dim { Dimension::Linear(d) => { @@ -493,8 +493,8 @@ fn apply_dimspace( } fn dimension_auto_spacing( - document: &acadrust::CadDocument, - dimension: &acadrust::entities::Dimension, + document: &codec::CadDocument, + dimension: &codec::entities::Dimension, annotation_multiplier: f64, ) -> f64 { let style = document diff --git a/src/app/command_driver/entity_commit.rs b/src/app/command_driver/entity_commit.rs index 7949ca248..588023fa4 100644 --- a/src/app/command_driver/entity_commit.rs +++ b/src/app/command_driver/entity_commit.rs @@ -3,7 +3,7 @@ use super::*; impl OpenCADStudio { pub(super) fn handle_commit_entity( &mut self, - entity: acadrust::EntityType, + entity: codec::EntityType, preserve_commit_layer: bool, ) -> Option> { let i = self.active_tab; @@ -25,10 +25,10 @@ impl OpenCADStudio { // leave the anchor untouched. (#327) if matches!( entity, - acadrust::EntityType::Line(_) - | acadrust::EntityType::Arc(_) - | acadrust::EntityType::LwPolyline(_) - | acadrust::EntityType::Polyline2D(_) + codec::EntityType::Line(_) + | codec::EntityType::Arc(_) + | codec::EntityType::LwPolyline(_) + | codec::EntityType::Polyline2D(_) ) { self.update_cont_anchor(&entity); } @@ -40,9 +40,9 @@ impl OpenCADStudio { let pending = self.begin_undo(i, label, 1, delta_safe); let is_associative_dimension = matches!( entity, - acadrust::EntityType::Dimension( - acadrust::entities::Dimension::Linear(_) - | acadrust::entities::Dimension::Aligned(_) + codec::EntityType::Dimension( + codec::entities::Dimension::Linear(_) + | codec::entities::Dimension::Aligned(_) ) ); let association_enabled = @@ -76,7 +76,7 @@ impl OpenCADStudio { pub(super) fn handle_commit_entities( &mut self, - mut entities: Vec, + mut entities: Vec, preserve_commit_style: bool, preserve_commit_layer: bool, ) -> Option> { @@ -137,7 +137,7 @@ impl OpenCADStudio { pub(super) fn handle_commit_entities_and_exit( &mut self, - mut entities: Vec, + mut entities: Vec, preserve_commit_style: bool, preserve_commit_layer: bool, ) { @@ -185,7 +185,7 @@ impl OpenCADStudio { pub(super) fn handle_commit_and_exit( &mut self, - entity: acadrust::EntityType, + entity: codec::EntityType, ) -> Option> { let i = self.active_tab; // XATTACH: the definition and its INSERT are created together @@ -200,7 +200,7 @@ impl OpenCADStudio { return Some(Task::none()); } let insert_block_name = match &entity { - acadrust::EntityType::Insert(ins) => Some(ins.block_name.clone()), + codec::EntityType::Insert(ins) => Some(ins.block_name.clone()), _ => None, }; // Record where this draw ended so ARC_CONT can continue from it @@ -211,9 +211,9 @@ impl OpenCADStudio { let pending = self.begin_undo(i, label, 1, delta_safe); let is_associative_dimension = matches!( entity, - acadrust::EntityType::Dimension( - acadrust::entities::Dimension::Linear(_) - | acadrust::entities::Dimension::Aligned(_) + codec::EntityType::Dimension( + codec::entities::Dimension::Linear(_) + | codec::entities::Dimension::Aligned(_) ) ); let association_enabled = @@ -259,7 +259,7 @@ impl OpenCADStudio { let association_mode = self.tabs[i].scene.document.header.dimension_associativity; let single_source_dimension = matches!( &entity, - acadrust::EntityType::Dimension(acadrust::entities::Dimension::Ordinate(_)) + codec::EntityType::Dimension(codec::entities::Dimension::Ordinate(_)) ); // A dimension placed on the sheet but measuring model geometry // through a viewport carries the compensation as a negative @@ -277,7 +277,7 @@ impl OpenCADStudio { let association_allowed = self.dimension_association_allowed(i); let inherited_dimension = if preserve_base_style { match &entity { - acadrust::EntityType::Dimension(dimension) => Some(( + codec::EntityType::Dimension(dimension) => Some(( dimension.base().common.layer.clone(), dimension.base().style_name.clone(), )), @@ -316,7 +316,7 @@ impl OpenCADStudio { &self.tabs[i].scene.document, &mut entity, ); - if let (Some((layer, style_name)), acadrust::EntityType::Dimension(dimension)) = + if let (Some((layer, style_name)), codec::EntityType::Dimension(dimension)) = (inherited_dimension, &mut entity) { dimension.base_mut().common.layer = layer; @@ -399,7 +399,7 @@ impl OpenCADStudio { pub(super) fn handle_commit_dimensions_and_exit( &mut self, dimensions: Vec<( - acadrust::EntityType, + codec::EntityType, crate::command::DimensionAssociationInput, )>, ) { @@ -557,12 +557,12 @@ impl OpenCADStudio { pub(super) fn handle_commit_and_edit_text( &mut self, - entity: acadrust::EntityType, + entity: codec::EntityType, ) -> Option> { let i = self.active_tab; let annotative_mleader = matches!( &entity, - acadrust::EntityType::MultiLeader(ml) if ml.enable_annotation_scale + codec::EntityType::MultiLeader(ml) if ml.enable_annotation_scale ); let label = self.history_label_from_active_cmd(i, "ENTITY"); let delta_safe = self.delta_add_safe(i, &entity) && !annotative_mleader; @@ -600,7 +600,7 @@ impl OpenCADStudio { pub(super) fn handle_commit_many_and_edit_text( &mut self, - entities: Vec, + entities: Vec, edit_index: usize, open_editor: bool, ) -> Option> { @@ -611,7 +611,7 @@ impl OpenCADStudio { let mut edit_handle = None; let mut leader_handle = None; for (idx, entity) in entities.into_iter().enumerate() { - let is_leader = matches!(entity, acadrust::EntityType::Leader(_)); + let is_leader = matches!(entity, codec::EntityType::Leader(_)); let h = self.commit_entity_handle(entity); if idx == edit_index { edit_handle = h; @@ -623,7 +623,7 @@ impl OpenCADStudio { // Link the leader to its annotation so the pair edits as a unit // (double-click on the leader resolves to the text entity). if let (Some(lh), Some(ah)) = (leader_handle, edit_handle) { - let linked = if let Some(acadrust::EntityType::Leader(l)) = + let linked = if let Some(codec::EntityType::Leader(l)) = self.tabs[i].scene.document.get_entity_mut(lh) { l.annotation_handle = ah; @@ -767,8 +767,8 @@ impl OpenCADStudio { pub(super) fn handle_commit_styled_hatch( &mut self, hatch: crate::scene::HatchModel, - color: acadrust::types::Color, - transparency: acadrust::types::Transparency, + color: codec::types::Color, + transparency: codec::types::Transparency, ) { let i = self.active_tab; let label = self.history_label_from_active_cmd(i, "HATCH"); @@ -795,8 +795,8 @@ impl OpenCADStudio { pub(super) fn handle_commit_hatch_with_boundaries( &mut self, mut hatch: crate::scene::HatchModel, - boundaries: Vec, - entity_style: Option<(acadrust::types::Color, acadrust::types::Transparency)>, + boundaries: Vec, + entity_style: Option<(codec::types::Color, codec::types::Transparency)>, ) { let i = self.active_tab; let label = self.history_label_from_active_cmd(i, "HATCH"); @@ -838,7 +838,7 @@ impl OpenCADStudio { pub(super) fn handle_commit_hatches( &mut self, hatches: Vec, - entity_style: Option<(acadrust::types::Color, acadrust::types::Transparency)>, + entity_style: Option<(codec::types::Color, codec::types::Transparency)>, ) { let i = self.active_tab; let label = self.history_label_from_active_cmd(i, "HATCH"); @@ -867,7 +867,7 @@ impl OpenCADStudio { pub(super) fn handle_update_entity_and_finish( &mut self, handle: Handle, - entity: acadrust::EntityType, + entity: codec::EntityType, ) -> Option> { let i = self.active_tab; if self.reject_locked_edit(i, handle) { @@ -876,7 +876,7 @@ impl OpenCADStudio { } let label = self.history_label_from_active_cmd(i, "EDIT"); self.push_undo_snapshot(i, label); - let is_dimension = matches!(entity, acadrust::EntityType::Dimension(_)); + let is_dimension = matches!(entity, codec::EntityType::Dimension(_)); if let Some(current) = self.tabs[i].scene.document.get_entity_mut(handle) { *current = entity; if is_dimension { @@ -934,13 +934,13 @@ impl OpenCADStudio { let i = self.active_tab; // Collect the full AttributeDefinitions owned by this block // record so each created attribute keeps its geometry (#255). - let attdefs: Vec = { + let attdefs: Vec = { let doc = &self.tabs[i].scene.document; if let Some(br) = doc.block_records.get(&block_name) { br.entity_handles .iter() .filter_map(|&h| { - if let Some(acadrust::EntityType::AttributeDefinition(ad)) = + if let Some(codec::EntityType::AttributeDefinition(ad)) = doc.get_entity(h) { Some(ad.clone()) @@ -962,7 +962,7 @@ impl OpenCADStudio { .and_then(|c| c.attreq_take_insert()); if let Some(entity) = entity { let insert_name = match &entity { - acadrust::EntityType::Insert(ins) => Some(ins.block_name.clone()), + codec::EntityType::Insert(ins) => Some(ins.block_name.clone()), _ => None, }; let label = self.history_label_from_active_cmd(i, "INSERT"); @@ -997,7 +997,7 @@ impl OpenCADStudio { None } - pub(super) fn handle_commit_live_entity(&mut self, entity: acadrust::EntityType) { + pub(super) fn handle_commit_live_entity(&mut self, entity: codec::EntityType) { let i = self.active_tab; let label = self.history_label_from_active_cmd(i, "ENTITY"); let delta_safe = self.delta_add_safe(i, &entity); @@ -1026,18 +1026,18 @@ impl OpenCADStudio { pub(super) fn handle_update_live_entity( &mut self, handle: Handle, - entity: acadrust::EntityType, + entity: codec::EntityType, finish: bool, ) { let i = self.active_tab; let tracks_draw_anchor = matches!( &entity, - acadrust::EntityType::Line(_) - | acadrust::EntityType::Arc(_) - | acadrust::EntityType::LwPolyline(_) - | acadrust::EntityType::Polyline(_) - | acadrust::EntityType::Polyline2D(_) - | acadrust::EntityType::Polyline3D(_) + codec::EntityType::Line(_) + | codec::EntityType::Arc(_) + | codec::EntityType::LwPolyline(_) + | codec::EntityType::Polyline(_) + | codec::EntityType::Polyline2D(_) + | codec::EntityType::Polyline3D(_) ); // Replace the live entity's geometry in place, preserving its // handle and layer (the fresh entity from the command carries diff --git a/src/app/command_driver/mod.rs b/src/app/command_driver/mod.rs index ed583fb7a..da3bc5197 100644 --- a/src/app/command_driver/mod.rs +++ b/src/app/command_driver/mod.rs @@ -77,7 +77,7 @@ use super::{Message, OpenCADStudio}; use crate::command::{CmdResult, SelectionEntity, StepInput}; -use acadrust::Handle; +use codec::Handle; use iced::Task; mod utilities; @@ -149,7 +149,7 @@ impl OpenCADStudio { 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 style_handle = style_handle.unwrap_or(codec::Handle::NULL); let changed = (header.multiline_scale - scale).abs() > f64::EPSILON || header.multiline_justification != justification || !header.multiline_style.eq_ignore_ascii_case(&style_name) @@ -220,17 +220,17 @@ impl OpenCADStudio { pub(crate) fn begin_table_cell_edit( &mut self, i: usize, - handle: acadrust::Handle, + handle: codec::Handle, click: glam::DVec3, ) -> crate::modules::annotate::table_cmd::TableCellEditStart { use crate::modules::annotate::table_cmd::{table_cell_at, TableCellEditStart}; - let target = if handle != acadrust::Handle::NULL { + let target = if handle != codec::Handle::NULL { self.tabs[i] .scene .document .get_entity(handle) .and_then(|entity| match entity { - acadrust::EntityType::Table(table) => Some((handle, table.clone())), + codec::EntityType::Table(table) => Some((handle, table.clone())), _ => None, }) } else { @@ -239,7 +239,7 @@ impl OpenCADStudio { // taking the last match selects the table on top if they overlap. let mut matched = None; for entity in self.tabs[i].scene.document.entities() { - if let acadrust::EntityType::Table(table) = entity { + if let codec::EntityType::Table(table) = entity { let h = table.common.handle; let style = table.table_style_handle.and_then(|style_handle| { self.tabs[i] @@ -248,7 +248,7 @@ impl OpenCADStudio { .objects .get(&style_handle) .and_then(|object| match object { - acadrust::objects::ObjectType::TableStyle(style) => Some(style), + codec::objects::ObjectType::TableStyle(style) => Some(style), _ => None, }) }); @@ -269,7 +269,7 @@ impl OpenCADStudio { .objects .get(&style_handle) .and_then(|object| match object { - acadrust::objects::ObjectType::TableStyle(style) => Some(style), + codec::objects::ObjectType::TableStyle(style) => Some(style), _ => None, }) }); @@ -353,7 +353,7 @@ impl OpenCADStudio { &mut self, tab: usize, handle: Handle, - mut entities: Vec, + mut entities: Vec, ) -> Vec { let same_type = entities.len() == 1 && self.tabs[tab] @@ -394,7 +394,7 @@ impl OpenCADStudio { for &updated in &handles { if matches!( self.tabs[tab].scene.document.get_entity(updated), - Some(acadrust::EntityType::Dimension(_)) + Some(codec::EntityType::Dimension(_)) ) { self.tabs[tab].scene.invalidate_dim_block_recorded(updated); } @@ -1350,10 +1350,10 @@ impl OpenCADStudio { self.tabs[i].scene.clear_preview_wire(); self.restore_pre_cmd_tangent(); - if handle == acadrust::Handle::NULL && self.selection_cycling { + if handle == codec::Handle::NULL && self.selection_cycling { let mut candidates = Vec::new(); for entity in self.tabs[i].scene.document.entities() { - if let acadrust::EntityType::Table(table) = entity { + if let codec::EntityType::Table(table) = entity { let h = table.common.handle; let style = table.table_style_handle.and_then(|style_handle| { self.tabs[i] @@ -1362,7 +1362,7 @@ impl OpenCADStudio { .objects .get(&style_handle) .and_then(|object| match object { - acadrust::objects::ObjectType::TableStyle(style) => Some(style), + codec::objects::ObjectType::TableStyle(style) => Some(style), _ => None, }) }); @@ -1418,7 +1418,7 @@ impl OpenCADStudio { !self.tabs[i].scene.is_layer_locked(*handle) && matches!( self.tabs[i].scene.document.get_entity(*handle), - Some(acadrust::EntityType::MultiLeader(_)) + Some(codec::EntityType::MultiLeader(_)) ) }); if !handles.is_empty() { @@ -1473,7 +1473,7 @@ impl OpenCADStudio { /// handle from the target document so it can't collide with an existing /// one. No-op for same-document pastes (the records already exist). (#129) pub(super) fn merge_dependencies(&mut self, i: usize, deps: &crate::app::ClipboardDeps) { - use acadrust::TableEntry; + use codec::TableEntry; if deps.is_empty() { return; } @@ -1538,7 +1538,7 @@ impl OpenCADStudio { /// the nearest planar curve of the edited space within a small share of /// that space's extent, the way a pick box takes the object under a click. fn entity_at_typed_point( - document: &acadrust::CadDocument, + document: &codec::CadDocument, owner: Handle, point: glam::DVec3, ) -> Option { @@ -1559,9 +1559,9 @@ fn entity_at_typed_point( // inside it picks it. None if matches!( entity, - acadrust::EntityType::Text(_) - | acadrust::EntityType::MText(_) - | acadrust::EntityType::Ellipse(_) + codec::EntityType::Text(_) + | codec::EntityType::MText(_) + | codec::EntityType::Ellipse(_) ) => { let dx = (bounds.min.x - point.x).max(point.x - bounds.max.x).max(0.0); @@ -1586,11 +1586,11 @@ fn entity_at_typed_point( fn apply_mleader_align( scene: &mut crate::scene::Scene, - handles: &[acadrust::Handle], + handles: &[codec::Handle], from: glam::DVec3, to: glam::DVec3, ) -> bool { - use cadkernel::geom2d::{closest_point, Curve, Vec2, XLine}; + use kernel::geom2d::{closest_point, Curve, Vec2, XLine}; let Some(direction) = Vec2::new(to.x - from.x, to.y - from.y).normalize() else { return false; @@ -1601,7 +1601,7 @@ fn apply_mleader_align( }); let mut changed = Vec::new(); for &handle in handles { - if let Some(acadrust::EntityType::MultiLeader(ml)) = scene.document.get_entity_mut(handle) { + if let Some(codec::EntityType::MultiLeader(ml)) = scene.document.get_entity_mut(handle) { let old = ml.context.content_base_point; let projected = closest_point(&line, [old.x, old.y]).point; let new_x = projected[0]; @@ -1632,16 +1632,16 @@ fn apply_mleader_align( fn compatible_mleader_collect_handles( scene: &crate::scene::Scene, - handles: &[acadrust::Handle], -) -> Vec { + handles: &[codec::Handle], +) -> Vec { let Some((base_block, base_style)) = handles.iter().find_map(|handle| { if scene.is_layer_locked(*handle) { return None; } - let acadrust::EntityType::MultiLeader(leader) = scene.document.get_entity(*handle)? else { + let codec::EntityType::MultiLeader(leader) = scene.document.get_entity(*handle)? else { return None; }; - (leader.content_type == acadrust::entities::LeaderContentType::Block) + (leader.content_type == codec::entities::LeaderContentType::Block) .then_some((leader.block_content_handle, leader.style_handle)) }) else { return Vec::new(); @@ -1654,8 +1654,8 @@ fn compatible_mleader_collect_handles( if !scene.is_layer_locked(handle) && matches!( scene.document.get_entity(handle), - Some(acadrust::EntityType::MultiLeader(leader)) - if leader.content_type == acadrust::entities::LeaderContentType::Block + Some(codec::EntityType::MultiLeader(leader)) + if leader.content_type == codec::entities::LeaderContentType::Block && leader.block_content_handle == base_block && leader.style_handle == base_style ) @@ -1668,7 +1668,7 @@ fn compatible_mleader_collect_handles( fn apply_mleader_collect( scene: &mut crate::scene::Scene, - handles: &[acadrust::Handle], + handles: &[codec::Handle], point: glam::DVec3, ) -> bool { if handles.len() < 2 { @@ -1676,18 +1676,18 @@ fn apply_mleader_collect( } let px = point.x; let py = point.y; - let Some(acadrust::EntityType::MultiLeader(base)) = scene.document.get_entity(handles[0]) + let Some(codec::EntityType::MultiLeader(base)) = scene.document.get_entity(handles[0]) else { return false; }; let base_block = base.block_content_handle; let base_style = base.style_handle; - let mut extra_roots: Vec = Vec::new(); + let mut extra_roots: Vec = Vec::new(); let mut merged_handles = Vec::new(); for &h in &handles[1..] { - if let Some(acadrust::EntityType::MultiLeader(ml)) = scene.document.get_entity(h) { - if ml.content_type == acadrust::entities::LeaderContentType::Block + if let Some(codec::EntityType::MultiLeader(ml)) = scene.document.get_entity(h) { + if ml.content_type == codec::entities::LeaderContentType::Block && ml.block_content_handle == base_block && ml.style_handle == base_style { @@ -1706,7 +1706,7 @@ fn apply_mleader_collect( return false; } - if let Some(acadrust::EntityType::MultiLeader(ml)) = scene.document.get_entity_mut(handles[0]) { + if let Some(codec::EntityType::MultiLeader(ml)) = scene.document.get_entity_mut(handles[0]) { let shift_x = px - ml.context.content_base_point.x; let shift_y = py - ml.context.content_base_point.y; for root in &mut ml.context.leader_roots { diff --git a/src/app/command_driver/modify.rs b/src/app/command_driver/modify.rs index 7a0393c9b..070d06c3b 100644 --- a/src/app/command_driver/modify.rs +++ b/src/app/command_driver/modify.rs @@ -300,21 +300,21 @@ impl OpenCADStudio { } if !matches!( self.tabs[i].scene.document.get_entity(handle), - Some(acadrust::EntityType::Hatch(_)) + Some(codec::EntityType::Hatch(_)) ) { self.command_line .push_error(crate::t!("HATCHEDIT: hatch entity not found.").as_ref()); } else { use crate::command::{CadCommand, HatchEditOperation}; if matches!(&operation, HatchEditOperation::BeginAssociate) { - let associative = matches!(self.tabs[i].scene.document.get_entity(handle),Some(acadrust::EntityType::Hatch(h)) if h.is_associative); + let associative = matches!(self.tabs[i].scene.document.get_entity(handle),Some(codec::EntityType::Hatch(h)) if h.is_associative); if associative { self.command_line .push_info("HATCHEDIT: hatch is already associative."); self.tabs[i].active_cmd = None; } else { let plane = match self.tabs[i].scene.document.get_entity(handle) { - Some(acadrust::EntityType::Hatch(h)) => { + Some(codec::EntityType::Hatch(h)) => { let storage = crate::entities::curve::ocs_plane(h.normal, h.elevation); crate::command::WorkingPlane::new( @@ -338,7 +338,7 @@ impl OpenCADStudio { if let HatchEditOperation::DrawOrderBoundary { above } = &operation { let references: Vec<_> = match self.tabs[i].scene.document.get_entity(handle) { - Some(acadrust::EntityType::Hatch(h)) => h + Some(codec::EntityType::Hatch(h)) => h .paths .iter() .flat_map(|p| p.boundary_handles.iter()) @@ -436,7 +436,7 @@ impl OpenCADStudio { } } } - if let Some(acadrust::EntityType::Hatch(hatch)) = + if let Some(codec::EntityType::Hatch(hatch)) = self.tabs[i].scene.document.get_entity_mut(handle) { if !name.is_empty() && name != hatch.pattern.name { @@ -467,7 +467,7 @@ impl OpenCADStudio { crate::scene::model::hatch_model::HatchPattern::Solid ); hatch.pattern_type = - acadrust::entities::HatchPatternType::Predefined; + codec::entities::HatchPatternType::Predefined; hatch.gradient_color.enabled = false; } } else { @@ -483,7 +483,7 @@ impl OpenCADStudio { hatch.pattern_scale = scale.max(1.0e-6) as f64; hatch.pattern_angle = (angle as f64).to_radians(); if let Some((x, y)) = origin { - hatch.set_pattern_origin(acadrust::types::Vector2::new(x, y)); + hatch.set_pattern_origin(codec::types::Vector2::new(x, y)); } if disassociate { for path in &mut hatch.paths { @@ -551,7 +551,7 @@ impl OpenCADStudio { } HatchEditOperation::RecreateBoundary { associate, region } => { let source = self.tabs[i].scene.document.get_entity(handle).cloned(); - if let Some(acadrust::EntityType::Hatch(source)) = source { + if let Some(codec::EntityType::Hatch(source)) = source { let storage = crate::entities::curve::ocs_plane( source.normal, source.elevation, @@ -592,10 +592,10 @@ impl OpenCADStudio { .enumerate() .map(|(outer, boundary)| { inner != outer - && cadkernel::geom2d::contains( + && kernel::geom2d::contains( boundary, seed, - cadkernel::geom2d::Tolerance::new(1e-6), + kernel::geom2d::Tolerance::new(1e-6), ) }) .collect::>() @@ -670,7 +670,7 @@ impl OpenCADStudio { } } if associate { - if let Some(acadrust::EntityType::Hatch(hatch)) = + if let Some(codec::EntityType::Hatch(hatch)) = self.tabs[i].scene.document.get_entity_mut(handle) { for (path, boundary) in @@ -692,12 +692,12 @@ impl OpenCADStudio { } HatchEditOperation::Separate => { let source = self.tabs[i].scene.document.get_entity(handle).cloned(); - if let Some(acadrust::EntityType::Hatch(hatch)) = source { + if let Some(codec::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.common.handle = codec::Handle::NULL; separated.paths = paths; separated.is_associative = separated .paths @@ -705,7 +705,7 @@ impl OpenCADStudio { .any(|path| !path.boundary_handles.is_empty()); self.tabs[i] .scene - .add_entity(acadrust::EntityType::Hatch(separated)); + .add_entity(codec::EntityType::Hatch(separated)); } self.tabs[i].scene.erase_entities(&[handle]); } diff --git a/src/app/command_driver/parameters.rs b/src/app/command_driver/parameters.rs index 584d3e02f..7308293d6 100644 --- a/src/app/command_driver/parameters.rs +++ b/src/app/command_driver/parameters.rs @@ -725,7 +725,7 @@ impl OpenCADStudio { }; if annotational { entity.as_entity_mut().set_layer(active_layer.clone()); - entity.common_mut().color = acadrust::types::Color::ByLayer; + entity.common_mut().color = codec::types::Color::ByLayer; // The dynamic form's screen-size and horizontal-text // overrides go; the style draws it. for code in [ @@ -744,7 +744,7 @@ impl OpenCADStudio { entity .as_entity_mut() .set_layer(DYNAMIC_DIMENSION_LAYER.to_string()); - entity.common_mut().color = acadrust::types::Color::Rgb { + entity.common_mut().color = codec::types::Color::Rgb { r: 103, g: 109, b: 118, @@ -899,7 +899,7 @@ impl OpenCADStudio { Task::none() } - pub(super) fn apply_continuous_constraints(&mut self, i: usize, new_handles: &[acadrust::Handle]) { + pub(super) fn apply_continuous_constraints(&mut self, i: usize, new_handles: &[codec::Handle]) { let supported_creation = self.tabs[i] .active_cmd .as_ref() @@ -924,7 +924,7 @@ impl OpenCADStudio { pub(in crate::app) fn apply_inferred_constraints( &mut self, i: usize, - changed_handles: &[acadrust::Handle], + changed_handles: &[codec::Handle], ) { if !self.constraint_infer || changed_handles.is_empty() { return; diff --git a/src/app/command_driver/property_match.rs b/src/app/command_driver/property_match.rs index 9284a48ce..fb41bc776 100644 --- a/src/app/command_driver/property_match.rs +++ b/src/app/command_driver/property_match.rs @@ -58,9 +58,9 @@ impl OpenCADStudio { // This application record is the hatch background colour. // Keep the outer Option to distinguish "source is not a // hatch" from "source hatch has no background". - let hatch_background_xdata: Option>> = + let hatch_background_xdata: Option>> = match src_clone.as_ref() { - Some(acadrust::EntityType::Hatch(h)) => Some( + Some(codec::EntityType::Hatch(h)) => Some( h.common .extended_data .get_record("HATCHBACKGROUNDCOLOR") @@ -71,12 +71,12 @@ impl OpenCADStudio { // Dimension-style overrides ride the ACAD record, identified // by a leading DSTYLE string. Matching replicates that payload // (or clears the destination when the source has none). - let dstyle_xdata: Option> = src_clone + let dstyle_xdata: Option> = src_clone .as_ref() .filter(|e| { matches!( e, - acadrust::EntityType::Dimension(_) | acadrust::EntityType::Leader(_) + codec::EntityType::Dimension(_) | codec::EntityType::Leader(_) ) }) .map(|e| crate::entities::dim_override::pairs(&e.common().extended_data)); @@ -93,8 +93,8 @@ impl OpenCADStudio { let mut is_hatch = false; if let Some(se) = &src_clone { if let Some(e) = app.tabs[i].scene.document.get_entity_mut(handle) { - is_dim = matches!(e, acadrust::EntityType::Dimension(_)); - is_hatch = matches!(e, acadrust::EntityType::Hatch(_)); + is_dim = matches!(e, codec::EntityType::Dimension(_)); + is_hatch = matches!(e, codec::EntityType::Hatch(_)); crate::entities::match_props::match_properties_kernel(se, e, &match_opts); } } @@ -117,11 +117,11 @@ impl OpenCADStudio { if match_opts.copy_dim_overrides && matches!( app.tabs[i].scene.document.get_entity(handle), - Some(acadrust::EntityType::Dimension(_) | acadrust::EntityType::Leader(_)) + Some(codec::EntityType::Dimension(_) | codec::EntityType::Leader(_)) ) && matches!( src_clone, - Some(acadrust::EntityType::Dimension(_) | acadrust::EntityType::Leader(_)) + Some(codec::EntityType::Dimension(_) | codec::EntityType::Leader(_)) ) { crate::entities::dim_override::replace( diff --git a/src/app/command_driver/solid3d.rs b/src/app/command_driver/solid3d.rs index ba6f3fa60..caac03566 100644 --- a/src/app/command_driver/solid3d.rs +++ b/src/app/command_driver/solid3d.rs @@ -115,8 +115,8 @@ impl OpenCADStudio { .flatten(); let mut surface = empty_extruded_surface(direction, taper_angle); if let ( - Some(acadrust::objects::SolidHistoryOperation::Extrusion(value)), - acadrust::EntityType::Surface(entity), + Some(codec::objects::SolidHistoryOperation::Extrusion(value)), + codec::EntityType::Surface(entity), ) = (&history, &mut surface) { if let Some(data) = @@ -227,14 +227,14 @@ impl OpenCADStudio { let mut failed = 0usize; let mut self_intersections = 0usize; for handle in handles { - let Some(acadrust::EntityType::Surface(surface)) = + let Some(codec::EntityType::Surface(surface)) = self.tabs[i].scene.document.get_entity(handle) else { failed += 1; continue; }; let kernel_distance = - if surface.kind == acadrust::entities::SurfaceKind::Revolved { + if surface.kind == codec::entities::SurfaceKind::Revolved { -distance } else { distance @@ -251,9 +251,9 @@ impl OpenCADStudio { failed += 1; continue; }; - let solid = match cadkernel::brep::thicken(&body, kernel_distance) { + let solid = match kernel::brep::thicken(&body, kernel_distance) { Ok(solid) => solid, - Err(cadkernel::brep::ThickenError::SelfIntersection) => { + Err(kernel::brep::ThickenError::SelfIntersection) => { failed += 1; self_intersections += 1; continue; @@ -388,7 +388,7 @@ impl OpenCADStudio { let result = sweep_model::revolve_history(&entity, from, to, angle, start_angle) .and_then(|history| { - cadkernel::acis::rebuild_body(&history) + kernel::acis::rebuild_body(&history) .ok() .map(|solid| (solid, history)) }) @@ -497,14 +497,14 @@ impl OpenCADStudio { for (handle, profile) in profiles { let result = path.as_ref().zip(options).and_then(|(path, options)| { let record = sweep_model::sweep_record(&profile, path, options)?; - let (_, _, closed) = cadkernel::acis::sweep_profile_geometry( + let (_, _, closed) = kernel::acis::sweep_profile_geometry( record.sweep_entity.as_ref()?, record.sweep_entity_transform, ) .ok()?; let surface = mode == ExtrudeMode::Surface || !closed; let body = - cadkernel::acis::rebuild_sweep_with_mode(&record, surface).ok()?; + kernel::acis::rebuild_sweep_with_mode(&record, surface).ok()?; Some((body, record, surface)) }); let Some((body, record, surface)) = result else { @@ -523,7 +523,7 @@ impl OpenCADStudio { self.add_solid_model( empty_solid3d(), body, - acadrust::objects::SolidHistoryOperation::Sweep(record), + codec::objects::SolidHistoryOperation::Sweep(record), ) }; if created.is_null() { @@ -617,7 +617,7 @@ impl OpenCADStudio { } else { loft_command_model::record(§ions, &guides, path, &available, mode, options) .and_then(|record| { - cadkernel::acis::rebuild_loft_with_options(&record) + kernel::acis::rebuild_loft_with_options(&record) .map(|body| (body, record)) }) }; @@ -651,7 +651,7 @@ impl OpenCADStudio { if !handle.is_null() { self.tabs[i].scene.create_solid_history( handle, - acadrust::objects::SolidHistoryOperation::Loft(record), + codec::objects::SolidHistoryOperation::Loft(record), ); } handle @@ -659,7 +659,7 @@ impl OpenCADStudio { self.add_solid_model( empty_solid3d(), body, - acadrust::objects::SolidHistoryOperation::Loft(record), + codec::objects::SolidHistoryOperation::Loft(record), ) }; if created.is_null() { @@ -751,7 +751,7 @@ impl OpenCADStudio { task } - pub(super) fn handle_slice_entities(&mut self, targets: Vec, plane: cadkernel::space::Plane, keep_point: Option) -> Task { + pub(super) fn handle_slice_entities(&mut self, targets: Vec, plane: kernel::space::Plane, keep_point: Option) -> Task { let i = self.active_tab; let task = self.solid_slice(&targets, plane, keep_point); self.tabs[i].active_cmd = None; @@ -761,7 +761,7 @@ impl OpenCADStudio { task } - pub(super) fn handle_slice_surface_entities(&mut self, targets: Vec, cutter: Box, keep_point: Option) -> Task { + pub(super) fn handle_slice_surface_entities(&mut self, targets: Vec, cutter: Box, keep_point: Option) -> Task { let i = self.active_tab; let task = self.solid_slice_surface(&targets, *cutter, keep_point); self.tabs[i].active_cmd = None; diff --git a/src/app/command_driver/tests.rs b/src/app/command_driver/tests.rs index b0e71a714..8cf929474 100644 --- a/src/app/command_driver/tests.rs +++ b/src/app/command_driver/tests.rs @@ -1,8 +1,8 @@ #[cfg(test)] mod command_replacement_tests { use super::super::*; - use acadrust::entities::{Circle, Line}; - use acadrust::types::Vector3; + use codec::entities::{Circle, Line}; + use codec::types::Vector3; #[test] fn one_to_one_edits_keep_identity_but_type_changes_do_not() { @@ -11,7 +11,7 @@ mod command_replacement_tests { let tab = app.active_tab; let handle = app.tabs[tab] .scene - .add_entity(acadrust::EntityType::Line(Line::from_points( + .add_entity(codec::EntityType::Line(Line::from_points( Vector3::ZERO, Vector3::new(1.0, 0.0, 0.0), ))); @@ -26,7 +26,7 @@ mod command_replacement_tests { let kept = app.replace_command_entity( tab, handle, - vec![acadrust::EntityType::Line(Line::from_points( + vec![codec::EntityType::Line(Line::from_points( Vector3::ZERO, Vector3::new(3.0, 0.0, 0.0), ))], @@ -34,12 +34,12 @@ mod command_replacement_tests { assert_eq!(kept, vec![handle]); let edited = app.tabs[tab].scene.document.get_entity(handle).unwrap(); assert_eq!(edited.common().owner_handle, owner); - assert!(matches!(edited, acadrust::EntityType::Line(line) if line.end.x == 3.0)); + assert!(matches!(edited, codec::EntityType::Line(line) if line.end.x == 3.0)); let allocated = app.replace_command_entity( tab, handle, - vec![acadrust::EntityType::Circle(Circle::from_coords( + vec![codec::EntityType::Circle(Circle::from_coords( 0.0, 0.0, 0.0, 2.0, ))], ); @@ -55,7 +55,7 @@ mod command_replacement_tests { let tab = app.active_tab; let handle = app.tabs[tab] .scene - .add_entity(acadrust::EntityType::Line(Line::from_points( + .add_entity(codec::EntityType::Line(Line::from_points( Vector3::ZERO, Vector3::new(1.0, 0.0, 0.0), ))); @@ -69,7 +69,7 @@ mod command_replacement_tests { let _ = app.apply_cmd_result(CmdResult::UpdateLiveEntity { handle, - entity: acadrust::EntityType::Line(Line::from_points( + entity: codec::EntityType::Line(Line::from_points( Vector3::ZERO, Vector3::new(4.0, 0.0, 0.0), )), @@ -82,7 +82,7 @@ mod command_replacement_tests { assert_eq!(updated.common().layer, expected.layer); assert_eq!(updated.common().invisible, expected.invisible); assert_eq!(updated.common().linetype_scale, expected.linetype_scale); - assert!(matches!(updated, acadrust::EntityType::Line(line) if line.end.x == 4.0)); + assert!(matches!(updated, codec::EntityType::Line(line) if line.end.x == 4.0)); } } @@ -94,17 +94,17 @@ mod parametric_constraint_undo_tests { fn add_line(app: &mut OpenCADStudio, x1: f64, y1: f64, x2: f64, y2: f64) -> Handle { app.tabs[app.active_tab] .scene - .add_entity(acadrust::EntityType::Line( - acadrust::entities::Line::from_points( - acadrust::types::Vector3::new(x1, y1, 0.0), - acadrust::types::Vector3::new(x2, y2, 0.0), + .add_entity(codec::EntityType::Line( + codec::entities::Line::from_points( + codec::types::Vector3::new(x1, y1, 0.0), + codec::types::Vector3::new(x2, y2, 0.0), ), )) } fn line_angle_deg(app: &OpenCADStudio, handle: Handle) -> f64 { match app.tabs[app.active_tab].scene.document.get_entity(handle) { - Some(acadrust::EntityType::Line(l)) => (l.end.y - l.start.y) + Some(codec::EntityType::Line(l)) => (l.end.y - l.start.y) .atan2(l.end.x - l.start.x) .to_degrees(), other => panic!("expected a Line, got {other:?}"), @@ -188,14 +188,14 @@ mod parametric_constraint_undo_tests { }); let line = |handle| match app.tabs[app.active_tab].scene.document.get_entity(handle) { - Some(acadrust::EntityType::Line(line)) => line.clone(), + Some(codec::EntityType::Line(line)) => line.clone(), other => panic!("expected a Line, got {other:?}"), }; let fixed = line(first); let moving = line(second); - assert_eq!(fixed.start, acadrust::types::Vector3::new(0.0, 0.0, 0.0)); - assert_eq!(fixed.end, acadrust::types::Vector3::new(10.0, 0.0, 0.0)); - assert!((moving.start - acadrust::types::Vector3::new(20.0, 0.0, 0.0)).length() < 1.0e-9); + assert_eq!(fixed.start, codec::types::Vector3::new(0.0, 0.0, 0.0)); + assert_eq!(fixed.end, codec::types::Vector3::new(10.0, 0.0, 0.0)); + assert!((moving.start - codec::types::Vector3::new(20.0, 0.0, 0.0)).length() < 1.0e-9); assert!((moving.length() - 10.0).abs() < 1.0e-7); assert!( (fixed.end - fixed.start) @@ -216,10 +216,10 @@ mod parametric_constraint_undo_tests { .document .get_entity(handle) { - Some(acadrust::EntityType::Line(line)) => (line.start, line.end, line.length()), + Some(codec::EntityType::Line(line)) => (line.start, line.end, line.length()), other => panic!("expected a Line, got {other:?}"), }; - let direction = acadrust::types::Vector3::new(3.0, 4.0, 0.0).normalize(); + let direction = codec::types::Vector3::new(3.0, 4.0, 0.0).normalize(); let _ = app.apply_cmd_result(CmdResult::AddHorizontalConstraint { kind: crate::scene::parametric_constraints::ConstraintKind::Horizontal, @@ -231,7 +231,7 @@ mod parametric_constraint_undo_tests { }); let line = match app.tabs[app.active_tab].scene.document.get_entity(handle) { - Some(acadrust::EntityType::Line(line)) => line, + Some(codec::EntityType::Line(line)) => line, other => panic!("expected a Line, got {other:?}"), }; let solved = (line.end - line.start).normalize(); @@ -246,7 +246,7 @@ mod parametric_constraint_undo_tests { app.undo_steps(1); let line = match app.tabs[app.active_tab].scene.document.get_entity(handle) { - Some(acadrust::EntityType::Line(line)) => line, + Some(codec::EntityType::Line(line)) => line, other => panic!("expected a Line after undo, got {other:?}"), }; assert_eq!((line.start, line.end), (original_start, original_end)); @@ -261,7 +261,7 @@ mod parametric_constraint_undo_tests { app.redo_steps(1); let line = match app.tabs[app.active_tab].scene.document.get_entity(handle) { - Some(acadrust::EntityType::Line(line)) => line, + Some(codec::EntityType::Line(line)) => line, other => panic!("expected a Line after redo, got {other:?}"), }; assert!((line.end - line.start).normalize().cross(&direction).length() < 1.0e-7); @@ -285,15 +285,15 @@ mod parametric_constraint_undo_tests { pick(first, glam::DVec3::ZERO), pick(second, glam::DVec3::new(8.0, 3.0, 0.0)), ), - direction: acadrust::types::Vector3::UNIT_X, + direction: codec::types::Vector3::UNIT_X, label: "Horizontal constraint", }); let line = |handle| match app.tabs[app.active_tab].scene.document.get_entity(handle) { - Some(acadrust::EntityType::Line(line)) => line, + Some(codec::EntityType::Line(line)) => line, other => panic!("expected a Line, got {other:?}"), }; - assert_eq!(line(first).start, acadrust::types::Vector3::ZERO); + assert_eq!(line(first).start, codec::types::Vector3::ZERO); assert!(line(second).start.y.abs() < 1.0e-8); } @@ -301,28 +301,28 @@ mod parametric_constraint_undo_tests { fn horizontal_minor_axis_rotates_an_ellipse_around_its_center() { let mut app = OpenCADStudio::new_for_test(); let _ = app.automation_op(r#"{"op":"new"}"#); - let mut ellipse = acadrust::entities::Ellipse::default(); - ellipse.center = acadrust::types::Vector3::new(3.0, 7.0, 0.0); - ellipse.major_axis = acadrust::types::Vector3::new(3.0, 4.0, 0.0); + let mut ellipse = codec::entities::Ellipse::default(); + ellipse.center = codec::types::Vector3::new(3.0, 7.0, 0.0); + ellipse.major_axis = codec::types::Vector3::new(3.0, 4.0, 0.0); ellipse.minor_axis_ratio = 0.4; let handle = app.tabs[app.active_tab] .scene - .add_entity(acadrust::EntityType::Ellipse(ellipse)); + .add_entity(codec::EntityType::Ellipse(ellipse)); let _ = app.apply_cmd_result(CmdResult::AddHorizontalConstraint { kind: crate::scene::parametric_constraints::ConstraintKind::Horizontal, selection: crate::command::HorizontalConstraintSelection::Reference( ParametricRef::ellipse_minor_axis(handle), ), - direction: acadrust::types::Vector3::UNIT_X, + direction: codec::types::Vector3::UNIT_X, label: "Horizontal constraint", }); let ellipse = match app.tabs[app.active_tab].scene.document.get_entity(handle) { - Some(acadrust::EntityType::Ellipse(ellipse)) => ellipse, + Some(codec::EntityType::Ellipse(ellipse)) => ellipse, other => panic!("expected an Ellipse, got {other:?}"), }; - assert!((ellipse.center - acadrust::types::Vector3::new(3.0, 7.0, 0.0)).length() < 1.0e-9); + assert!((ellipse.center - codec::types::Vector3::new(3.0, 7.0, 0.0)).length() < 1.0e-9); assert!(ellipse.major_axis.x.abs() < 1.0e-7); assert!((ellipse.major_axis.length() - 5.0).abs() < 1.0e-7); assert!((ellipse.minor_axis_ratio - 0.4).abs() < 1.0e-9); @@ -335,9 +335,9 @@ mod parametric_constraint_undo_tests { let _ = app.automation_op(r#"{"op":"new"}"#); let _existing = add_line(&mut app, 0.0, 0.0, 5.0, 0.0); - let new_line = acadrust::EntityType::Line(acadrust::entities::Line::from_points( - acadrust::types::Vector3::new(5.0, 0.0, 0.0), // exactly on `existing`'s end point - acadrust::types::Vector3::new(10.0, 0.0, 0.0), + let new_line = codec::EntityType::Line(codec::entities::Line::from_points( + codec::types::Vector3::new(5.0, 0.0, 0.0), // exactly on `existing`'s end point + codec::types::Vector3::new(10.0, 0.0, 0.0), )); app.tabs[app.active_tab].active_cmd = Some(Box::new( crate::modules::draw::draw::line::LineCommand::new(), @@ -489,7 +489,7 @@ mod parametric_constraint_undo_tests { Ok(8.0) ); let (start, end) = match app.tabs[app.active_tab].scene.document.get_entity(line) { - Some(acadrust::EntityType::Line(l)) => (l.start, l.end), + Some(codec::EntityType::Line(l)) => (l.start, l.end), other => panic!("expected a Line, got {other:?}"), }; let len = ((end.x - start.x).powi(2) + (end.y - start.y).powi(2)).sqrt(); @@ -502,7 +502,7 @@ mod parametric_constraint_undo_tests { app.named_parameter_editor_rows[0].formula = "3".to_string(); app.apply_named_parameter_editor_rows(); let (start, end) = match app.tabs[app.active_tab].scene.document.get_entity(line) { - Some(acadrust::EntityType::Line(l)) => (l.start, l.end), + Some(codec::EntityType::Line(l)) => (l.start, l.end), other => panic!("expected a Line, got {other:?}"), }; let len = ((end.x - start.x).powi(2) + (end.y - start.y).powi(2)).sqrt(); @@ -520,7 +520,7 @@ mod parametric_constraint_undo_tests { Ok(8.0) ); let length_after_undo = match app.tabs[app.active_tab].scene.document.get_entity(line) { - Some(acadrust::EntityType::Line(line)) => line.length(), + Some(codec::EntityType::Line(line)) => line.length(), other => panic!("expected a Line, got {other:?}"), }; assert!((length_after_undo - 8.0).abs() < 1e-6); @@ -650,7 +650,7 @@ mod parametric_constraint_undo_tests { Ok(8.0) ); let (start, end) = match app.tabs[app.active_tab].scene.document.get_entity(line) { - Some(acadrust::EntityType::Line(l)) => (l.start, l.end), + Some(codec::EntityType::Line(l)) => (l.start, l.end), other => panic!("expected a Line, got {other:?}"), }; let len = ((end.x - start.x).powi(2) + (end.y - start.y).powi(2)).sqrt(); @@ -822,18 +822,18 @@ mod parametric_constraint_undo_tests { entities.contains(&a) && entities.contains(&b), "the constraint should reference both lines" ); - let Some(acadrust::EntityType::Line(first)) = + let Some(codec::EntityType::Line(first)) = app.tabs[app.active_tab].scene.document.get_entity(a) else { panic!("expected first line"); }; - let Some(acadrust::EntityType::Line(second)) = + let Some(codec::EntityType::Line(second)) = app.tabs[app.active_tab].scene.document.get_entity(b) else { panic!("expected second line"); }; assert!( - (first.end - acadrust::types::Vector3::new(5.0, 0.0, 0.0)).length() < 1.0e-7 + (first.end - codec::types::Vector3::new(5.0, 0.0, 0.0)).length() < 1.0e-7 ); assert!((second.start - first.end).length() < 1.0e-7); assert!((second.length() - 5.0).abs() < 1.0e-7); @@ -878,21 +878,21 @@ mod parametric_constraint_undo_tests { #[cfg(all(test, not(target_arch = "wasm32")))] mod delobj_tests { use super::super::*; - use acadrust::types::Vector3; + use codec::types::Vector3; fn sweep_source_presence(value: i16, mode: crate::command::ExtrudeMode) -> (bool, bool) { let mut app = OpenCADStudio::new_for_test(); app.automation_op(r#"{"op":"new"}"#); let i = app.active_tab; - let mut circle = acadrust::Circle::new(); + let mut circle = codec::Circle::new(); circle.radius = 2.0; let profile = app.tabs[i] .scene - .add_entity(acadrust::EntityType::Circle(circle)); + .add_entity(codec::EntityType::Circle(circle)); let path = app.tabs[i] .scene - .add_entity(acadrust::EntityType::Line(acadrust::Line::from_points( + .add_entity(codec::EntityType::Line(codec::Line::from_points( Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 5.0), ))); @@ -912,10 +912,10 @@ mod delobj_tests { .entities() .any(|entity| match mode { crate::command::ExtrudeMode::Solid => { - matches!(entity, acadrust::EntityType::Solid3D(_)) + matches!(entity, codec::EntityType::Solid3D(_)) } crate::command::ExtrudeMode::Surface => { - matches!(entity, acadrust::EntityType::Surface(_)) + matches!(entity, codec::EntityType::Surface(_)) } }); assert!(created, "SWEEP must produce the requested result"); @@ -948,15 +948,15 @@ mod delobj_tests { #[cfg(all(test, not(target_arch = "wasm32")))] mod thicken_tests { use super::super::*; - use acadrust::entities::{Surface, SurfaceKind}; - use cadkernel::geom2d::{Circle, Curve, NurbsCurve}; - use cadkernel::space::{Parameterization, Plane}; + use codec::entities::{Surface, SurfaceKind}; + use kernel::geom2d::{Circle, Curve, NurbsCurve}; + use kernel::space::{Parameterization, Plane}; #[test] fn thicken_preserves_sources_and_round_trips_results_with_one_undo() { let mut app = OpenCADStudio::new_for_test(); app.automation_op(r#"{"op":"new"}"#); - let body = cadkernel::brep::planar_region( + let body = kernel::brep::planar_region( Plane::XY, &[vec![Curve::Circle(Circle { centre: [0.0; 2], @@ -965,14 +965,14 @@ mod thicken_tests { ) .unwrap(); let source = app.add_surface_model( - acadrust::EntityType::Surface(Surface::new(SurfaceKind::Plane)), + codec::EntityType::Surface(Surface::new(SurfaceKind::Plane)), body, ); assert!(!source.is_null()); let i = app.active_tab; let invalid = app.tabs[i] .scene - .add_entity(acadrust::EntityType::Surface(Surface::new( + .add_entity(codec::EntityType::Surface(Surface::new( SurfaceKind::Generic, ))); let original = @@ -993,7 +993,7 @@ mod thicken_tests { .document .entities() .find_map(|entity| { - matches!(entity, acadrust::EntityType::Solid3D(_)).then_some(entity.common().handle) + matches!(entity, codec::EntityType::Solid3D(_)).then_some(entity.common().handle) }) .unwrap(); assert!(app.tabs[i] @@ -1006,7 +1006,7 @@ mod thicken_tests { original ); let expected = - cadkernel::brep::analytic_mass_properties(&app.tabs[i].scene.solid_models[&result]) + kernel::brep::analytic_mass_properties(&app.tabs[i].scene.solid_models[&result]) .unwrap() .volume; let bytes = crate::io::save_to_bytes( @@ -1019,7 +1019,7 @@ mod thicken_tests { let mut restored = crate::scene::Scene::new(); restored.document = document; restored.restore_solid_models(&[result]); - let actual = cadkernel::brep::analytic_mass_properties(&restored.solid_models[&result]) + let actual = kernel::brep::analytic_mass_properties(&restored.solid_models[&result]) .unwrap() .volume; assert!((expected - actual).abs() < 1e-8); @@ -1057,10 +1057,10 @@ mod thicken_tests { ) .unwrap(); let body = - cadkernel::brep::extrude_surface(Plane::XY, &[Curve::Nurbs(profile)], [0.0, 0.0, 2.0]) + kernel::brep::extrude_surface(Plane::XY, &[Curve::Nurbs(profile)], [0.0, 0.0, 2.0]) .unwrap(); let source = app.add_surface_model( - acadrust::EntityType::Surface(Surface::new(SurfaceKind::Generic)), + codec::EntityType::Surface(Surface::new(SurfaceKind::Generic)), body, ); let i = app.active_tab; @@ -1075,7 +1075,7 @@ mod thicken_tests { .document .entities() .filter_map(|entity| { - matches!(entity, acadrust::EntityType::Solid3D(_)).then_some(entity.common().handle) + matches!(entity, codec::EntityType::Solid3D(_)).then_some(entity.common().handle) }) .collect::>(); assert_eq!(solids.len(), 1); @@ -1095,7 +1095,7 @@ mod thicken_tests { mod dispatched_command_task_tests { use super::super::*; use crate::command::CmdResult; - use acadrust::entities::ViewportRenderMode as Mode; + use codec::entities::ViewportRenderMode as Mode; /// `CmdResult::Dispatch` and `CmdResult::Relaunch` run the assembled line /// through `dispatch_command`, and the Task it returns must reach the diff --git a/src/app/command_driver/text_edit.rs b/src/app/command_driver/text_edit.rs index c121fec42..1bc8f0f5f 100644 --- a/src/app/command_driver/text_edit.rs +++ b/src/app/command_driver/text_edit.rs @@ -55,7 +55,7 @@ impl OpenCADStudio { pub(super) fn handle_suspend_for_text_input( &mut self, pos: glam::DVec3, - entity: acadrust::entities::Text, + entity: codec::entities::Text, ) { let i = self.active_tab; self.tabs[i].suspended_cmd = self.tabs[i].active_cmd.take(); @@ -126,7 +126,7 @@ impl OpenCADStudio { !self.tabs[i].scene.is_layer_locked(*handle) && matches!( self.tabs[i].scene.document.get_entity(*handle), - Some(acadrust::EntityType::Dimension(_)) + Some(codec::EntityType::Dimension(_)) ) }); if handles.is_empty() { @@ -135,15 +135,15 @@ impl OpenCADStudio { } else { self.push_undo_snapshot(i, "DIMEDIT"); for handle in &handles { - if let Some(acadrust::EntityType::Dimension(dimension)) = + if let Some(codec::EntityType::Dimension(dimension)) = self.tabs[i].scene.document.get_entity_mut(*handle) { use crate::command::DimensionEditOperation as Operation; match &operation { Operation::Home => { let base = dimension.base_mut(); - base.text_middle_point = acadrust::types::Vector3::ZERO; - base.insertion_point = acadrust::types::Vector3::ZERO; + base.text_middle_point = codec::types::Vector3::ZERO; + base.insertion_point = codec::types::Vector3::ZERO; base.text_user_positioned = false; base.text_rotation = 0.0; } @@ -156,10 +156,10 @@ impl OpenCADStudio { dimension.base_mut().text_rotation = degrees.to_radians(); } Operation::Oblique(degrees) => match dimension { - acadrust::entities::Dimension::Linear(value) => { + codec::entities::Dimension::Linear(value) => { value.ext_line_rotation = degrees.to_radians(); } - acadrust::entities::Dimension::Aligned(value) => { + codec::entities::Dimension::Aligned(value) => { value.ext_line_rotation = degrees.to_radians(); } _ => {} @@ -196,23 +196,23 @@ impl OpenCADStudio { let mut is_dim = false; if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) { match entity { - acadrust::EntityType::Text(t) => { + codec::EntityType::Text(t) => { t.value = new_text; updated = true; } - acadrust::EntityType::MText(t) => { + codec::EntityType::MText(t) => { t.value = new_text; updated = true; } - acadrust::EntityType::AttributeDefinition(a) => { + codec::EntityType::AttributeDefinition(a) => { a.default_value = new_text; updated = true; } - acadrust::EntityType::AttributeEntity(a) => { + codec::EntityType::AttributeEntity(a) => { a.set_value(new_text); updated = true; } - acadrust::EntityType::Dimension(d) => { + codec::EntityType::Dimension(d) => { // Empty string resets to auto-measured value; otherwise set override. let base = d.base_mut(); base.text = new_text; diff --git a/src/app/command_driver/transform.rs b/src/app/command_driver/transform.rs index a9fb68786..c31d015fd 100644 --- a/src/app/command_driver/transform.rs +++ b/src/app/command_driver/transform.rs @@ -259,7 +259,7 @@ impl OpenCADStudio { let structural = handles.iter().any(|handle| { matches!( self.tabs[i].scene.document.get_entity(*handle), - Some(acadrust::EntityType::Dimension(_)) + Some(codec::EntityType::Dimension(_)) ) }); let pending = self.begin_undo(i, "STRETCH", handles.len(), !structural); @@ -281,13 +281,13 @@ impl OpenCADStudio { // Dimensions whose points moved — their baked *D block is // stale afterwards and must be dropped (see #398 / #372). - let mut stretched_dims: Vec = Vec::new(); + let mut stretched_dims: Vec = Vec::new(); for handle in &handles { let before = self.tabs[i].scene.document.get_entity_arc(*handle); if let Some(entity) = before.as_deref() { use crate::scene::parametric_constraints::ParametricRef; match entity { - acadrust::EntityType::Line(line) => { + codec::EntityType::Line(line) => { if in_win(line.start.x, line.start.y) { driven_refs.push(ParametricRef::point(*handle, 0)); } @@ -295,7 +295,7 @@ impl OpenCADStudio { driven_refs.push(ParametricRef::point(*handle, 1)); } } - acadrust::EntityType::LwPolyline(polyline) => { + codec::EntityType::LwPolyline(polyline) => { if let Some(world) = crate::entities::curve::lwpolyline_world_xy(polyline) { @@ -309,7 +309,7 @@ impl OpenCADStudio { } } } - acadrust::EntityType::Polyline2D(polyline) => { + codec::EntityType::Polyline2D(polyline) => { for (index, vertex) in polyline.vertices.iter().enumerate() { if in_win(vertex.location.x, vertex.location.y) { driven_refs.push(ParametricRef::point( @@ -319,12 +319,12 @@ impl OpenCADStudio { } } } - acadrust::EntityType::Circle(circle) + codec::EntityType::Circle(circle) if in_win(circle.center.x, circle.center.y) => { driven_refs.push(ParametricRef::center(*handle)); } - acadrust::EntityType::Arc(arc) + codec::EntityType::Arc(arc) if in_win(arc.center.x, arc.center.y) => { driven_refs.push(ParametricRef::center(*handle)); @@ -337,7 +337,7 @@ impl OpenCADStudio { }; let mut stretched = false; match entity { - acadrust::EntityType::Line(l) => { + codec::EntityType::Line(l) => { let s_in = in_win(l.start.x, l.start.y); let e_in = in_win(l.end.x, l.end.y); if s_in { @@ -353,7 +353,7 @@ impl OpenCADStudio { stretched = true; } } - acadrust::EntityType::LwPolyline(p) => { + codec::EntityType::LwPolyline(p) => { let Some(mut world) = crate::entities::curve::lwpolyline_world_xy(p) else { continue; @@ -369,7 +369,7 @@ impl OpenCADStudio { *p = world; } } - acadrust::EntityType::Polyline2D(p) => { + codec::EntityType::Polyline2D(p) => { for v in &mut p.vertices { if in_win(v.location.x, v.location.y) { v.location.x += dx; @@ -378,7 +378,7 @@ impl OpenCADStudio { } } } - acadrust::EntityType::Polyline(p) => { + codec::EntityType::Polyline(p) => { for v in &mut p.vertices { if in_win(v.location.x, v.location.z) { v.location.x += dx; @@ -387,7 +387,7 @@ impl OpenCADStudio { } } } - acadrust::EntityType::Arc(a) => { + codec::EntityType::Arc(a) => { if in_win(a.center.x, a.center.y) { a.center.x += dx; a.center.y += dy; @@ -395,7 +395,7 @@ impl OpenCADStudio { stretched = true; } } - acadrust::EntityType::Circle(c) => { + codec::EntityType::Circle(c) => { if in_win(c.center.x, c.center.y) { c.center.x += dx; c.center.y += dy; @@ -403,7 +403,7 @@ impl OpenCADStudio { stretched = true; } } - acadrust::EntityType::Ellipse(e) => { + codec::EntityType::Ellipse(e) => { if in_win(e.center.x, e.center.y) { e.center.x += dx; e.center.y += dy; @@ -411,7 +411,7 @@ impl OpenCADStudio { stretched = true; } } - acadrust::EntityType::Insert(ins) => { + codec::EntityType::Insert(ins) => { if in_win(ins.insert_point.x, ins.insert_point.y) { ins.insert_point.x += dx; ins.insert_point.y += dy; @@ -419,7 +419,7 @@ impl OpenCADStudio { stretched = true; } } - acadrust::EntityType::Text(t) => { + codec::EntityType::Text(t) => { if in_win(t.insertion_point.x, t.insertion_point.y) { t.insertion_point.x += dx; t.insertion_point.y += dy; @@ -427,7 +427,7 @@ impl OpenCADStudio { stretched = true; } } - acadrust::EntityType::MText(t) => { + codec::EntityType::MText(t) => { if in_win(t.insertion_point.x, t.insertion_point.y) { t.insertion_point.x += dx; t.insertion_point.y += dy; @@ -435,18 +435,18 @@ impl OpenCADStudio { stretched = true; } } - acadrust::EntityType::Viewport(vp) => { + codec::EntityType::Viewport(vp) => { stretched = windows.iter().any(|(win_min, win_max)| { crate::entities::viewport::stretch(vp, *win_min, *win_max, delta) }); } - acadrust::EntityType::Dimension(dim) => { - use acadrust::entities::Dimension; + codec::EntityType::Dimension(dim) => { + use codec::entities::Dimension; // Move every definition point that falls inside the // window — the same points the grips expose — then // refresh the stored measurement so the value tracks // the stretched geometry. - let mv = |p: &mut acadrust::types::Vector3| { + let mv = |p: &mut codec::types::Vector3| { if in_win(p.x, p.y) { p.x += dx; p.y += dy; diff --git a/src/app/command_driver/utilities.rs b/src/app/command_driver/utilities.rs index 0adfde892..0667f5846 100644 --- a/src/app/command_driver/utilities.rs +++ b/src/app/command_driver/utilities.rs @@ -37,20 +37,20 @@ impl OpenCADStudio { /// UCS origin (0,0,0 in user coordinates). fn default_draw_start(&self, i: usize) -> glam::DVec3 { let tab = &self.tabs[i]; - let endpoint = |entity: &acadrust::EntityType| { + let endpoint = |entity: &codec::EntityType| { let last_grip = match entity { - acadrust::EntityType::Line(line) => { + codec::EntityType::Line(line) => { return Some(glam::DVec3::new(line.end.x, line.end.y, line.end.z)); } - acadrust::EntityType::Arc(_) => Some(2), - acadrust::EntityType::LwPolyline(polyline) => { + codec::EntityType::Arc(_) => Some(2), + codec::EntityType::LwPolyline(polyline) => { polyline.vertices.len().checked_sub(1) } - acadrust::EntityType::Polyline(polyline) => polyline.vertices.len().checked_sub(1), - acadrust::EntityType::Polyline2D(polyline) => { + codec::EntityType::Polyline(polyline) => polyline.vertices.len().checked_sub(1), + codec::EntityType::Polyline2D(polyline) => { polyline.vertices.len().checked_sub(1) } - acadrust::EntityType::Polyline3D(polyline) => { + codec::EntityType::Polyline3D(polyline) => { polyline.vertices.len().checked_sub(1) } _ => None, @@ -472,7 +472,7 @@ impl OpenCADStudio { let (kept, dropped): (Vec, Vec) = handles.into_iter().partition(|h| { matches!( self.tabs[i].scene.document.get_entity(*h), - Some(acadrust::EntityType::Insert(_)) + Some(codec::EntityType::Insert(_)) ) }); for handle in &dropped { @@ -560,10 +560,10 @@ impl OpenCADStudio { let solid_pick = matches!( self.tabs[i].scene.document.get_entity(*handle), Some( - acadrust::EntityType::Solid3D(_) - | acadrust::EntityType::Surface(_) - | acadrust::EntityType::Region(_) - | acadrust::EntityType::Body(_) + codec::EntityType::Solid3D(_) + | codec::EntityType::Surface(_) + | codec::EntityType::Region(_) + | codec::EntityType::Body(_) ) ); let direction = if solid_pick { diff --git a/src/app/command_driver/viewport.rs b/src/app/command_driver/viewport.rs index a2da1eb67..1e915b65f 100644 --- a/src/app/command_driver/viewport.rs +++ b/src/app/command_driver/viewport.rs @@ -1,7 +1,7 @@ use super::*; impl OpenCADStudio { - pub(super) fn handle_mview_create(&mut self, viewport: acadrust::entities::Viewport, preserve_view: bool) { + pub(super) fn handle_mview_create(&mut self, viewport: codec::entities::Viewport, preserve_view: bool) { let i = self.active_tab; let saved_view = preserve_view.then(|| { ( @@ -17,9 +17,9 @@ impl OpenCADStudio { }); let label = self.history_label_from_active_cmd(i, "MVIEW"); let pending = self.begin_undo(i, label, 1, true); - let handle = self.commit_entity_handle(acadrust::EntityType::Viewport(viewport)); + let handle = self.commit_entity_handle(codec::EntityType::Viewport(viewport)); if let (Some(handle), Some(saved)) = (handle, saved_view) { - if let Some(acadrust::EntityType::Viewport(viewport)) = + if let Some(codec::EntityType::Viewport(viewport)) = self.tabs[i].scene.document.get_entity_mut(handle) { viewport.view_target = saved.0; @@ -45,7 +45,7 @@ impl OpenCADStudio { pub(super) fn handle_mview_create_clipped( &mut self, - boundary: Option, + boundary: Option, boundary_handle: Handle, ) -> Option> { let i = self.active_tab; @@ -56,12 +56,12 @@ impl OpenCADStudio { .document .get_entity(boundary_handle) .is_some_and(|entity| match entity { - acadrust::EntityType::Circle(_) => true, - acadrust::EntityType::Ellipse(ellipse) => ellipse.is_full(), - acadrust::EntityType::LwPolyline(polyline) => polyline.is_closed, - acadrust::EntityType::Polyline(polyline) => polyline.is_closed(), - acadrust::EntityType::Polyline2D(polyline) => polyline.is_closed(), - acadrust::EntityType::Polyline3D(polyline) => polyline.flags.closed, + codec::EntityType::Circle(_) => true, + codec::EntityType::Ellipse(ellipse) => ellipse.is_full(), + codec::EntityType::LwPolyline(polyline) => polyline.is_closed, + codec::EntityType::Polyline(polyline) => polyline.is_closed(), + codec::EntityType::Polyline2D(polyline) => polyline.is_closed(), + codec::EntityType::Polyline3D(polyline) => polyline.flags.closed, _ => false, }); if !valid { @@ -146,14 +146,14 @@ impl OpenCADStudio { return Some(Task::none()); } - let mut viewport = acadrust::entities::Viewport::new(); + let mut viewport = codec::entities::Viewport::new(); viewport.center = - acadrust::types::Vector3::new((min_x + max_x) / 2.0, (min_y + max_y) / 2.0, 0.0); + codec::types::Vector3::new((min_x + max_x) / 2.0, (min_y + max_y) / 2.0, 0.0); viewport.width = max_x - min_x; viewport.height = max_y - min_y; viewport.id = 2; viewport.clip_boundary_handle = clip_handle; - let viewport_handle = self.commit_entity_handle(acadrust::EntityType::Viewport(viewport)); + let viewport_handle = self.commit_entity_handle(codec::EntityType::Viewport(viewport)); if let Some(viewport_handle) = viewport_handle { if !created_boundary { let before = self.tabs[i] @@ -279,7 +279,7 @@ impl OpenCADStudio { let mut thawed_count = 0usize; // Collect target viewport handles - let target_handles: Vec = if vp_handle == acadrust::Handle::NULL { + let target_handles: Vec = if vp_handle == codec::Handle::NULL { // All viewports in current layout block let block_handle = self.tabs[i].scene.current_layout_block_handle_pub(); self.tabs[i] @@ -288,7 +288,7 @@ impl OpenCADStudio { .entities() .filter(|e| { e.common().owner_handle == block_handle - && matches!(e, acadrust::EntityType::Viewport(_)) + && matches!(e, codec::EntityType::Viewport(_)) }) .map(|e| e.common().handle) .collect() @@ -297,7 +297,7 @@ impl OpenCADStudio { }; for &target_handle in &target_handles { - if let Some(acadrust::EntityType::Viewport(vp)) = + if let Some(codec::EntityType::Viewport(vp)) = self.tabs[i].scene.document.get_entity_mut(target_handle) { for h in &freeze_handles { diff --git a/src/app/commands/blocks.rs b/src/app/commands/blocks.rs index 5c7f75bb8..148af74df 100644 --- a/src/app/commands/blocks.rs +++ b/src/app/commands/blocks.rs @@ -34,7 +34,7 @@ impl OpenCADStudio { pub(in crate::app) fn copy_entities_to_clipboard( &mut self, i: usize, - handles: &[acadrust::Handle], + handles: &[codec::Handle], base: glam::DVec3, ) -> usize { // Clone + dep-capture live in the shared kernel; storage stays here. @@ -113,7 +113,7 @@ impl OpenCADStudio { .collect(); if nums.len() >= 2 { let z = nums.get(2).copied().unwrap_or(0.0); - let pt = acadrust::types::Vector3::new(nums[0], nums[1], z); + let pt = codec::types::Vector3::new(nums[0], nums[1], z); let is_paper = self.tabs[i].scene.current_layout != "Model"; self.push_undo_snapshot(i, "BASE"); if is_paper { @@ -576,7 +576,7 @@ impl OpenCADStudio { .iter() .filter_map(|h| doc.get_entity(*h)) .filter_map(|e| match e { - acadrust::EntityType::AttributeDefinition(a) => { + codec::EntityType::AttributeDefinition(a) => { Some((a.tag.clone(), a.default_value.clone())) } _ => None, @@ -589,7 +589,7 @@ impl OpenCADStudio { .document .entities() .filter_map(|entity| match entity { - acadrust::EntityType::Insert(insert) + codec::EntityType::Insert(insert) if insert.block_name.eq_ignore_ascii_case(&block) && !self.tabs[i].scene.is_layer_locked(insert.common.handle) => { @@ -607,7 +607,7 @@ impl OpenCADStudio { let mut synced = 0usize; let mut changes = Vec::new(); for handle in inserts { - let Some(acadrust::EntityType::Insert(ins)) = + let Some(codec::EntityType::Insert(ins)) = self.tabs[i].scene.document.get_entity_mut(handle) else { continue; @@ -621,7 +621,7 @@ impl OpenCADStudio { .any(|a| a.tag.eq_ignore_ascii_case(tag)) { ins.attributes - .push(acadrust::entities::AttributeEntity::new( + .push(codec::entities::AttributeEntity::new( tag.clone(), default.clone(), )); @@ -726,11 +726,11 @@ impl OpenCADStudio { "XCLIP" => { use crate::command::CadCommand; use crate::modules::insert::xclip::XclipCommand; - let inserts: Vec = self.tabs[i] + let inserts: Vec = self.tabs[i] .scene .selected_entities() .iter() - .filter(|(_, e)| matches!(e, acadrust::EntityType::Insert(_))) + .filter(|(_, e)| matches!(e, codec::EntityType::Insert(_))) .map(|(h, _)| *h) .collect(); let command = if inserts.is_empty() { @@ -1017,7 +1017,7 @@ impl OpenCADStudio { self.tabs[i].xref_unloaded.remove(key); self.tabs[i].xref_stat_cache.remove(key); } - let handles: rustc_hash::FxHashSet = self.tabs + let handles: rustc_hash::FxHashSet = self.tabs [i] .scene .document @@ -1604,7 +1604,7 @@ impl OpenCADStudio { .selected_entities() .iter() .filter_map(|(_, e)| match e { - acadrust::EntityType::Insert(ins) => Some(ins.block_name.clone()), + codec::EntityType::Insert(ins) => Some(ins.block_name.clone()), _ => None, }) .collect(); @@ -1717,13 +1717,13 @@ mod tests { let mut app = fresh_app(); app.automation_op(r#"{"op":"new"}"#); let i = app.active_tab; - let mut line = acadrust::entities::Line::new(); - line.start = acadrust::types::Vector3::ZERO; - line.end = acadrust::types::Vector3::new(10.0, 0.0, 0.0); + let mut line = codec::entities::Line::new(); + line.start = codec::types::Vector3::ZERO; + line.end = codec::types::Vector3::new(10.0, 0.0, 0.0); app.tabs[i] .scene .define_block_from_owned_entities( - vec![acadrust::EntityType::Line(line)], + vec![codec::EntityType::Line(line)], "Widget", glam::DVec3::ZERO, ) @@ -1742,13 +1742,13 @@ mod tests { app.refresh_block_palette(); assert!(app.block_palette.blocks.is_empty()); let i = app.active_tab; - let mut line = acadrust::entities::Line::new(); - line.start = acadrust::types::Vector3::ZERO; - line.end = acadrust::types::Vector3::new(10.0, 0.0, 0.0); + let mut line = codec::entities::Line::new(); + line.start = codec::types::Vector3::ZERO; + line.end = codec::types::Vector3::new(10.0, 0.0, 0.0); app.tabs[i] .scene .define_block_from_owned_entities( - vec![acadrust::EntityType::Line(line)], + vec![codec::EntityType::Line(line)], "Widget", glam::DVec3::ZERO, ) @@ -1813,7 +1813,7 @@ mod tests { fn add_dwg_xref(app: &mut OpenCADStudio, name: &str, saved: &str) { let i = app.active_tab; - let mut br = acadrust::tables::BlockRecord::new(name); + let mut br = codec::tables::BlockRecord::new(name); br.flags.is_xref = true; br.xref_path = saved.to_string(); br.handle = app.tabs[i].scene.document.allocate_handle(); @@ -1851,7 +1851,7 @@ mod tests { #[test] fn xref_overlay_on_image_errors() { - use acadrust::objects::{ImageDefinition, ObjectType}; + use codec::objects::{ImageDefinition, ObjectType}; let mut app = fresh_app(); let i = app.active_tab; let h = app.tabs[i].scene.document.allocate_handle(); @@ -1863,9 +1863,9 @@ mod tests { .objects .insert(h, ObjectType::ImageDefinition(def)); // Reference it so collect_entries lists it. - let mut img = acadrust::entities::RasterImage::new( + let mut img = codec::entities::RasterImage::new( "img.png", - acadrust::types::Vector3::ZERO, + codec::types::Vector3::ZERO, 8.0, 8.0, ); @@ -1873,7 +1873,7 @@ mod tests { app.tabs[i] .scene .document - .add_entity(acadrust::EntityType::RasterImage(img)) + .add_entity(codec::EntityType::RasterImage(img)) .unwrap(); let out = run_capture(&mut app, "XREF Overlay img.png"); assert!( @@ -1887,7 +1887,7 @@ mod tests { // CLI mirror of the palette F7 guard: reloading an image row reports // the drawing-only error, leaves its unloaded flag untouched, and // does not follow with a spurious no-match error. - use acadrust::objects::{ImageDefinition, ObjectType}; + use codec::objects::{ImageDefinition, ObjectType}; let mut app = fresh_app(); let i = app.active_tab; let h = app.tabs[i].scene.document.allocate_handle(); @@ -1898,9 +1898,9 @@ mod tests { .document .objects .insert(h, ObjectType::ImageDefinition(def)); - let mut img = acadrust::entities::RasterImage::new( + let mut img = codec::entities::RasterImage::new( "img.png", - acadrust::types::Vector3::ZERO, + codec::types::Vector3::ZERO, 8.0, 8.0, ); @@ -1908,7 +1908,7 @@ mod tests { app.tabs[i] .scene .document - .add_entity(acadrust::EntityType::RasterImage(img)) + .add_entity(codec::EntityType::RasterImage(img)) .unwrap(); app.tabs[i].current_path = Some(std::path::PathBuf::from("C:/Drawings/host.dwg")); app.tabs[i].xref_unloaded.add(h.value()); @@ -1956,7 +1956,7 @@ mod tests { #[test] fn xref_detach_nested_guard_errors() { - use acadrust::tables::BlockRecord; + use codec::tables::BlockRecord; // Host file on disk containing its own xref "INNER". let dir = std::env::temp_dir().join(format!( "ocs_xref_nested_{}_{}", @@ -1967,7 +1967,7 @@ mod tests { .as_nanos() )); std::fs::create_dir_all(&dir).unwrap(); - let mut host_doc = acadrust::CadDocument::new(); + let mut host_doc = codec::CadDocument::new(); let mut inner = BlockRecord::new("INNER"); inner.flags.is_xref = true; inner.xref_path = "inner.dwg".to_string(); @@ -1997,15 +1997,15 @@ mod tests { .as_nanos() )); std::fs::create_dir_all(&dir).unwrap(); - let mut xref_doc = acadrust::CadDocument::new(); + let mut xref_doc = codec::CadDocument::new(); xref_doc .layers - .add(acadrust::tables::Layer::new("WALLS")) + .add(codec::tables::Layer::new("WALLS")) .unwrap(); - let mut line = acadrust::entities::Line::new(); + let mut line = codec::entities::Line::new(); line.common.layer = "WALLS".to_string(); xref_doc - .add_entity(acadrust::EntityType::Line(line)) + .add_entity(codec::EntityType::Line(line)) .unwrap(); let bytes = crate::io::save_to_bytes(&xref_doc, "dwg", xref_doc.version).unwrap(); let xref_path = dir.join("plan.dwg"); @@ -2035,12 +2035,12 @@ mod tests { .as_nanos() )); std::fs::create_dir_all(&dir).unwrap(); - let mut xref_doc = acadrust::CadDocument::new(); - let mut line = acadrust::entities::Line::new(); + let mut xref_doc = codec::CadDocument::new(); + let mut line = codec::entities::Line::new(); line.common.plotstyle_flags = 0b11; line.common.plotstyle_handle = Some(xref_doc.allocate_handle()); xref_doc - .add_entity(acadrust::EntityType::Line(line)) + .add_entity(codec::EntityType::Line(line)) .unwrap(); let bytes = crate::io::save_to_bytes(&xref_doc, "dwg", xref_doc.version).unwrap(); let xref_path = dir.join("plan.dwg"); @@ -2060,7 +2060,7 @@ mod tests { #[test] fn xref_bind_pdf_errors() { - use acadrust::objects::{ObjectType, UnderlayDefinition}; + use codec::objects::{ObjectType, UnderlayDefinition}; let mut app = fresh_app(); let i = app.active_tab; let h = app.tabs[i].scene.document.allocate_handle(); @@ -2081,7 +2081,7 @@ mod tests { #[test] fn xref_bind_nested_guard_errors() { - use acadrust::tables::BlockRecord; + use codec::tables::BlockRecord; let dir = std::env::temp_dir().join(format!( "ocs_xref_bind_nested_{}_{}", std::process::id(), @@ -2091,7 +2091,7 @@ mod tests { .as_nanos() )); std::fs::create_dir_all(&dir).unwrap(); - let mut host_doc = acadrust::CadDocument::new(); + let mut host_doc = codec::CadDocument::new(); let mut inner = BlockRecord::new("INNER"); inner.flags.is_xref = true; inner.xref_path = "inner.dwg".to_string(); @@ -2275,15 +2275,15 @@ mod tests { .as_nanos() )); std::fs::create_dir_all(&dir).unwrap(); - let mut xref_doc = acadrust::CadDocument::new(); + let mut xref_doc = codec::CadDocument::new(); xref_doc .layers - .add(acadrust::tables::Layer::new("WALLS")) + .add(codec::tables::Layer::new("WALLS")) .unwrap(); - let mut line = acadrust::entities::Line::new(); + let mut line = codec::entities::Line::new(); line.common.layer = "WALLS".to_string(); xref_doc - .add_entity(acadrust::EntityType::Line(line)) + .add_entity(codec::EntityType::Line(line)) .unwrap(); let bytes = crate::io::save_to_bytes(&xref_doc, "dwg", xref_doc.version).unwrap(); let xref_path = dir.join("plan.dwg"); @@ -2333,12 +2333,12 @@ mod tests { let mut app = fresh_app(); app.automation_op(r#"{"op":"new"}"#); let i = app.active_tab; - let mut line = acadrust::entities::Line::new(); - line.end = acadrust::types::Vector3::new(10.0, 0.0, 0.0); + let mut line = codec::entities::Line::new(); + line.end = codec::types::Vector3::new(10.0, 0.0, 0.0); app.tabs[i] .scene .define_block_from_owned_entities( - vec![acadrust::EntityType::Line(line)], + vec![codec::EntityType::Line(line)], "Widget", glam::DVec3::ZERO, ) @@ -2350,12 +2350,12 @@ mod tests { app.tabs .push(crate::app::document::DocumentTab::new_drawing(99)); let other = app.tabs.len() - 1; - let mut line = acadrust::entities::Line::new(); - line.end = acadrust::types::Vector3::new(50.0, 0.0, 0.0); + let mut line = codec::entities::Line::new(); + line.end = codec::types::Vector3::new(50.0, 0.0, 0.0); app.tabs[other] .scene .define_block_from_owned_entities( - vec![acadrust::EntityType::Line(line)], + vec![codec::EntityType::Line(line)], "Widget", glam::DVec3::ZERO, ) diff --git a/src/app/commands/dim.rs b/src/app/commands/dim.rs index 6108a16cf..29fae9203 100644 --- a/src/app/commands/dim.rs +++ b/src/app/commands/dim.rs @@ -1,6 +1,6 @@ use super::*; -fn selected_edge_body(app: &OpenCADStudio, tab: usize) -> Option { +fn selected_edge_body(app: &OpenCADStudio, tab: usize) -> Option { let scene = &app.tabs.get(tab)?.scene; let selected = scene.selected_handles_in_order(); let [handle] = selected.as_slice() else { @@ -9,7 +9,7 @@ fn selected_edge_body(app: &OpenCADStudio, tab: usize) -> Option Option Vec<(acadrust::Handle, cadkernel::brep::Body)> { +) -> Vec<(codec::Handle, kernel::brep::Body)> { let handles = app.tabs[tab] .scene .document @@ -28,7 +28,7 @@ fn solid_edge_sources( .filter_map(|entity| { matches!( entity, - acadrust::EntityType::Solid3D(_) | acadrust::EntityType::Surface(_) + codec::EntityType::Solid3D(_) | codec::EntityType::Surface(_) ) .then_some(entity.common().handle) }) @@ -146,7 +146,7 @@ impl OpenCADStudio { } cmd if cmd.starts_with("JUSTIFYTEXT ") => { let opt = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase(); - let handles: Vec = self.tabs[i] + let handles: Vec = self.tabs[i] .scene .selected_entities() .iter() @@ -164,7 +164,7 @@ impl OpenCADStudio { ); return Some(Task::none()); } - use acadrust::entities::{ + use codec::entities::{ AttachmentPoint as AP, TextHorizontalAlignment as TH, TextVerticalAlignment as TV, }; @@ -213,7 +213,7 @@ impl OpenCADStudio { .find(|e| e.common().handle == *h) { match e { - acadrust::EntityType::Text(t) => { + codec::EntityType::Text(t) => { let mut changed = false; if let Some(a) = text_align { t.horizontal_alignment = a; @@ -228,7 +228,7 @@ impl OpenCADStudio { n += 1; } } - acadrust::EntityType::MText(m) => { + codec::EntityType::MText(m) => { if let Some(a) = mtext_ap { m.attachment_point = a; n += 1; @@ -269,7 +269,7 @@ impl OpenCADStudio { } cmd if cmd.starts_with("TCASE ") => { let opt = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase(); - let handles: Vec = self.tabs[i] + let handles: Vec = self.tabs[i] .scene .selected_entities() .iter() @@ -315,11 +315,11 @@ impl OpenCADStudio { .find(|e| e.common().handle == *h) { match e { - acadrust::EntityType::Text(t) => { + codec::EntityType::Text(t) => { t.value = conv(&t.value); n += 1; } - acadrust::EntityType::MText(m) => { + codec::EntityType::MText(m) => { m.value = conv(&m.value); n += 1; } @@ -353,14 +353,14 @@ impl OpenCADStudio { self.tabs[i].active_cmd = Some(Box::new(c)); } cmd if cmd.starts_with("TEXTMASK ") => { - let handles: Vec = self.tabs[i] + let handles: Vec = self.tabs[i] .scene .selected_entities() .iter() .filter(|(_, e)| { matches!( e, - acadrust::EntityType::Text(_) | acadrust::EntityType::MText(_) + codec::EntityType::Text(_) | codec::EntityType::MText(_) ) }) .map(|(h, _)| *h) @@ -388,20 +388,20 @@ impl OpenCADStudio { continue; // no geometry for this object } let pad = ((max[1] - min[1]) * 0.15).max(0.0); - let c1 = acadrust::types::Vector3::new( + let c1 = codec::types::Vector3::new( (min[0] - pad) as f64, (min[1] - pad) as f64, min[2] as f64, ); - let c2 = acadrust::types::Vector3::new( + let c2 = codec::types::Vector3::new( (max[0] + pad) as f64, (max[1] + pad) as f64, min[2] as f64, ); self.tabs[i] .scene - .add_entity_clone(acadrust::EntityType::Wipeout( - acadrust::entities::Wipeout::from_corners(c1, c2), + .add_entity_clone(codec::EntityType::Wipeout( + codec::entities::Wipeout::from_corners(c1, c2), )); n += 1; } @@ -436,11 +436,11 @@ impl OpenCADStudio { return Some(Task::none()); } }; - let handles: Vec = self.tabs[i] + let handles: Vec = self.tabs[i] .scene .selected_entities() .iter() - .filter(|(_, e)| matches!(e, acadrust::EntityType::Text(_))) + .filter(|(_, e)| matches!(e, codec::EntityType::Text(_))) .map(|(h, _)| *h) .filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle)) .collect(); @@ -464,7 +464,7 @@ impl OpenCADStudio { if cur_w <= 1e-9 { continue; } - if let Some(acadrust::EntityType::Text(t)) = self.tabs[i] + if let Some(codec::EntityType::Text(t)) = self.tabs[i] .scene .document .entities_mut() @@ -533,12 +533,12 @@ impl OpenCADStudio { } }; - let mut texts: Vec<(acadrust::Handle, f64, f64)> = self.tabs[i] + let mut texts: Vec<(codec::Handle, f64, f64)> = self.tabs[i] .scene .selected_entities() .iter() .filter_map(|(h, e)| match e { - acadrust::EntityType::Text(t) + codec::EntityType::Text(t) if !self.tabs[i].scene.is_layer_locked(*h) => { Some((*h, t.insertion_point.x, t.insertion_point.y)) @@ -569,7 +569,7 @@ impl OpenCADStudio { let mut num = start; for (h, _, _) in &texts { - if let Some(acadrust::EntityType::Text(t)) = self.tabs[i] + if let Some(codec::EntityType::Text(t)) = self.tabs[i] .scene .document .entities_mut() @@ -624,7 +624,7 @@ impl OpenCADStudio { .objects .iter() .find_map(|(handle, object)| match object { - acadrust::objects::ObjectType::MultiLeaderStyle(style) + codec::objects::ObjectType::MultiLeaderStyle(style) if style.name.eq_ignore_ascii_case(&name) => { let mut style = style.clone(); @@ -1215,7 +1215,7 @@ impl OpenCADStudio { self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { - let replacements: Vec<(acadrust::Handle, Vec)> = entities + let replacements: Vec<(codec::Handle, Vec)> = entities .iter() .filter_map(|(h, e)| { let pieces = explode_entity(e, &self.tabs[i].scene.document); @@ -1293,7 +1293,7 @@ impl OpenCADStudio { .map(|e| (h, e)) }) .collect(); - let initial_edges: Vec = self.tabs[i] + let initial_edges: Vec = self.tabs[i] .scene .selected_entities() .into_iter() @@ -1361,8 +1361,8 @@ impl OpenCADStudio { self.tabs[i].active_cmd = Some(Box::new(c)); } cmd if cmd.starts_with("ARCTEXT ") => { - use acadrust::entities::Text; - use acadrust::types::Vector3; + use codec::entities::Text; + use codec::types::Vector3; let text = cmd.strip_prefix("ARCTEXT").unwrap_or("").trim().to_string(); if text.is_empty() { self.command_line.push_info( @@ -1376,7 +1376,7 @@ impl OpenCADStudio { .selected_entities() .iter() .find_map(|(_, e)| match e { - acadrust::EntityType::Arc(a) => Some(a.clone()), + codec::EntityType::Arc(a) => Some(a.clone()), _ => None, }); let Some(arc) = arc else { @@ -1406,7 +1406,7 @@ impl OpenCADStudio { let t = Text::with_value(ch.to_string(), pos) .with_height(height) .with_rotation(ang + std::f64::consts::FRAC_PI_2); - self.tabs[i].scene.add_entity(acadrust::EntityType::Text(t)); + self.tabs[i].scene.add_entity(codec::EntityType::Text(t)); } self.tabs[i].dirty = true; self.command_line diff --git a/src/app/commands/display.rs b/src/app/commands/display.rs index 6f9bf63ee..7fddf031d 100644 --- a/src/app/commands/display.rs +++ b/src/app/commands/display.rs @@ -29,7 +29,7 @@ impl OpenCADStudio { Lock(usize, usize, bool), Block(usize, usize, String), Formula(usize, usize, String), - Field(usize, usize, acadrust::Handle), + Field(usize, usize, codec::Handle), } let words: Vec<&str> = rest.split_whitespace().collect(); let integer = |index: usize| { @@ -120,7 +120,7 @@ impl OpenCADStudio { TableAction::Field( row, column, - acadrust::Handle::new(handle), + codec::Handle::new(handle), ) }) } @@ -134,7 +134,7 @@ impl OpenCADStudio { ); return Some(Task::none()); }; - let selected_handles: Vec = self.tabs[i] + let selected_handles: Vec = self.tabs[i] .scene .selected_entities() .iter() @@ -174,7 +174,7 @@ impl OpenCADStudio { self.push_undo_snapshot(i, "TABLE EDIT"); let mut changed = false; for handle in &selected_handles { - let Some(acadrust::EntityType::Table(table)) = + let Some(codec::EntityType::Table(table)) = self.tabs[i].scene.document.get_entity_mut(*handle) else { continue; @@ -182,7 +182,7 @@ impl OpenCADStudio { match &action { TableAction::Cell(row, column, text) => { if let Some(cell) = table.cell_mut(*row, *column) { - use acadrust::entities::table::CellStateFlags; + use codec::entities::table::CellStateFlags; if !cell.state.intersects( CellStateFlags::CONTENT_LOCKED | CellStateFlags::CONTENT_READ_ONLY, @@ -229,7 +229,7 @@ impl OpenCADStudio { && *c1 < table.column_count() && *c2 < table.column_count() => { - table.merge_cells(acadrust::entities::table::CellRange::new( + table.merge_cells(codec::entities::table::CellRange::new( (*r1).min(*r2), (*c1).min(*c2), (*r1).max(*r2), @@ -253,7 +253,7 @@ impl OpenCADStudio { } TableAction::Lock(row, column, locked) => { if let Some(cell) = table.cell_mut(*row, *column) { - use acadrust::entities::table::CellStateFlags; + use codec::entities::table::CellStateFlags; cell.state.set(CellStateFlags::CONTENT_LOCKED, *locked); cell.state.set(CellStateFlags::FORMAT_LOCKED, *locked); changed = true; @@ -263,7 +263,7 @@ impl OpenCADStudio { if let (Some(handle), Some(cell)) = (block_handle, table.cell_mut(*row, *column)) { - use acadrust::entities::table::{ + use codec::entities::table::{ CellContent, CellStateFlags, CellType, }; if !cell.state.intersects( @@ -279,7 +279,7 @@ impl OpenCADStudio { } TableAction::Formula(row, column, expression) => { if let Some(cell) = table.cell_mut(*row, *column) { - use acadrust::entities::table::CellStateFlags; + use codec::entities::table::CellStateFlags; if !cell.state.intersects( CellStateFlags::CONTENT_LOCKED | CellStateFlags::CONTENT_READ_ONLY, @@ -297,7 +297,7 @@ impl OpenCADStudio { TableAction::Field(row, column, field_handle) => { let mut attached = false; if let Some(cell) = table.cell_mut(*row, *column) { - use acadrust::entities::table::{ + use codec::entities::table::{ CellContent, CellStateFlags, CellType, }; if !cell.state.intersects( @@ -371,7 +371,7 @@ impl OpenCADStudio { } let mut count = 0usize; for entity in self.tabs[i].scene.document.entities_mut() { - if let acadrust::EntityType::Viewport(vp) = entity { + if let codec::EntityType::Viewport(vp) = entity { vp.status.ucs_icon_visible = visible; if sub == "NOORIGIN" || sub == "ORIGIN" { vp.status.ucs_icon_at_origin = at_origin; @@ -391,7 +391,7 @@ impl OpenCADStudio { self.show_ucs_icon = visible; self.ribbon.set_ucs_icon(visible); for entity in self.tabs[i].scene.document.entities_mut() { - if let acadrust::EntityType::Viewport(vp) = entity { + if let codec::EntityType::Viewport(vp) = entity { vp.status.ucs_icon_visible = visible; } } @@ -611,11 +611,11 @@ impl OpenCADStudio { self.tabs[i].active_cmd = Some(Box::new(c)); } cmd if cmd.starts_with("XDATA ") => { - use acadrust::xdata::{ExtendedDataRecord, XDataValue}; + use codec::xdata::{ExtendedDataRecord, XDataValue}; let rest = cmd.trim_start_matches("XDATA").trim(); let parts: Vec<&str> = rest.splitn(3, char::is_whitespace).collect(); let sub = parts.first().map(|s| s.to_uppercase()).unwrap_or_default(); - let selected_handles: Vec = self.tabs[i] + let selected_handles: Vec = self.tabs[i] .scene .selected_entities() .iter() @@ -960,7 +960,7 @@ impl OpenCADStudio { .split_once(' ') .map(|(_, r)| r.trim().to_uppercase()) .unwrap_or_default(); - let handles: Vec = self.tabs[i] + let handles: Vec = self.tabs[i] .scene .selected_entities() .iter() @@ -977,7 +977,7 @@ impl OpenCADStudio { let mut changed = 0usize; self.push_undo_snapshot(i, "UNDERLAY"); for h in &handles { - if let Some(acadrust::EntityType::Underlay(ul)) = self.tabs[i] + if let Some(codec::EntityType::Underlay(ul)) = self.tabs[i] .scene .document .entities_mut() @@ -1007,7 +1007,7 @@ impl OpenCADStudio { "CLIP" => match arg { "ON" => { ul.flags |= - acadrust::entities::UnderlayDisplayFlags::CLIPPING; + codec::entities::UnderlayDisplayFlags::CLIPPING; changed += 1; } "OFF" => { @@ -1063,7 +1063,7 @@ impl OpenCADStudio { // annotative objects. With no preselection, gather one through the // regular selection engine before applying the action. action @ ("OBJECTSCALE ADD" | "OBJECTSCALE DELETE") => { - let selected: Vec = self.tabs[i] + let selected: Vec = self.tabs[i] .scene .selected_entities() .iter() @@ -1181,13 +1181,13 @@ impl OpenCADStudio { self.tabs[i].active_cmd = Some(Box::new(c)); } cmd if cmd.starts_with("HYPERLINK ") => { - use acadrust::xdata::XDataValue; + use codec::xdata::XDataValue; let url = cmd.strip_prefix("HYPERLINK").unwrap_or("").trim().to_string(); if url.is_empty() { self.command_line.push_info(crate::t!("Usage: HYPERLINK (select objects first)").as_ref()); return Some(Task::none()); } - let handles: Vec = self.tabs[i] + let handles: Vec = self.tabs[i] .scene .selected_entities() .iter() @@ -1242,7 +1242,7 @@ impl OpenCADStudio { let parts: Vec<&str> = rest.splitn(2, char::is_whitespace).collect(); let action = parts.first().map(|s| s.to_uppercase()).unwrap_or_default(); let arg = parts.get(1).copied().unwrap_or("").trim(); - let handles: Vec = self.tabs[i] + let handles: Vec = self.tabs[i] .scene .selected_entities() .iter() @@ -1261,7 +1261,7 @@ impl OpenCADStudio { let mut changed = 0usize; let mut changed_handles = Vec::new(); for h in &handles { - if let Some(acadrust::EntityType::RasterImage(img)) = self.tabs[i] + if let Some(codec::EntityType::RasterImage(img)) = self.tabs[i] .scene .document .entities_mut() @@ -1539,7 +1539,7 @@ impl OpenCADStudio { // Reset every alternate scale representation to the position of // the representation visible at the current annotation scale. "ANNORESET" => { - let handles: Vec = self.tabs[i] + let handles: Vec = self.tabs[i] .scene .selected_entities() .iter() @@ -1627,8 +1627,8 @@ impl OpenCADStudio { .push_error(crate::t!("DATALINK: the CSV file is empty.").as_ref()); return Some(Task::none()); } - use acadrust::entities::TableBuilder; - use acadrust::types::Vector3; + use codec::entities::TableBuilder; + use codec::types::Vector3; let mut table = TableBuilder::new(nrows, ncols) .at(Vector3::new(0.0, 0.0, 0.0)) .row_height(0.5) @@ -1643,7 +1643,7 @@ impl OpenCADStudio { let current_style = doc.header.current_table_style_name.clone(); table.table_style_handle = doc.objects.iter().find_map(|(handle, object)| { match object { - acadrust::objects::ObjectType::TableStyle(style) + codec::objects::ObjectType::TableStyle(style) if style.name.eq_ignore_ascii_case(¤t_style) => { Some(*handle) @@ -1673,12 +1673,12 @@ impl OpenCADStudio { .trim_start_matches("DATALINKUPDATE") .trim() .eq_ignore_ascii_case("WRITE"); - let selected: Vec = self.tabs[i] + let selected: Vec = self.tabs[i] .scene .selected_entities() .into_iter() .filter_map(|(handle, entity)| { - matches!(entity, acadrust::EntityType::Table(_)).then_some(handle) + matches!(entity, codec::EntityType::Table(_)).then_some(handle) }) .collect(); let table_handles = if selected.is_empty() { @@ -1687,7 +1687,7 @@ impl OpenCADStudio { .document .entities() .filter_map(|entity| { - matches!(entity, acadrust::EntityType::Table(_)) + matches!(entity, codec::EntityType::Table(_)) .then_some(entity.common().handle) }) .collect::>() @@ -1696,7 +1696,7 @@ impl OpenCADStudio { }; let mut jobs = Vec::new(); for handle in &table_handles { - let Some(acadrust::EntityType::Table(table)) = + let Some(codec::EntityType::Table(table)) = self.tabs[i].scene.document.get_entity(*handle) else { continue; @@ -1720,7 +1720,7 @@ impl OpenCADStudio { let mut written = 0usize; let mut errors = Vec::new(); for (table_handle, link_handle) in &jobs { - let Some(acadrust::EntityType::Table(table)) = + let Some(codec::EntityType::Table(table)) = self.tabs[i].scene.document.get_entity(*table_handle) else { continue; @@ -1764,11 +1764,11 @@ impl OpenCADStudio { return Some(Task::none()); } self.push_undo_snapshot(i, "DATALINKUPDATE"); - use acadrust::entities::table::CellStateFlags; + use codec::entities::table::CellStateFlags; let mut changed_handles = Vec::new(); for (table_handle, link_handle, rows) in updates { let columns = rows.iter().map(Vec::len).max().unwrap_or(0); - let Some(acadrust::EntityType::Table(table)) = + let Some(codec::EntityType::Table(table)) = self.tabs[i].scene.document.get_entity_mut(table_handle) else { continue; @@ -1843,11 +1843,11 @@ impl OpenCADStudio { } self.push_undo_snapshot(i, "LANDXMLIMPORT"); for [x, y, z] in &pts { - let mut p = acadrust::entities::Point::new(); - p.location = acadrust::types::Vector3::new(*x, *y, *z); + let mut p = codec::entities::Point::new(); + p.location = codec::types::Vector3::new(*x, *y, *z); self.tabs[i] .scene - .add_entity_clone(acadrust::EntityType::Point(p)); + .add_entity_clone(codec::EntityType::Point(p)); } self.tabs[i].dirty = true; self.command_line.push_output(crate::tf!( @@ -1949,7 +1949,7 @@ pub(crate) fn parse_csv_table(text: &str) -> Vec> { rows } -pub(crate) fn table_to_csv(table: &acadrust::entities::Table) -> String { +pub(crate) fn table_to_csv(table: &codec::entities::Table) -> String { fn escape(value: &str) -> String { if value.contains([',', '"', '\r', '\n']) { format!("\"{}\"", value.replace('"', "\"\"")) @@ -2040,7 +2040,7 @@ mod tests { app.automation_op(r#"{"op":"new"}"#); let i = app.active_tab; - let stale = acadrust::Handle::new(0xDEAD); + let stale = codec::Handle::new(0xDEAD); app.tabs[i].scene.meshes.insert(stale, stale_mesh()); let epoch_before = app.tabs[i].scene.geometry_epoch; @@ -2065,7 +2065,7 @@ mod tests { let i = app.active_tab; let seed = |app: &mut OpenCADStudio, i: usize| { - let stale = acadrust::Handle::new(0xBEEF); + let stale = codec::Handle::new(0xBEEF); app.tabs[i].scene.meshes.insert(stale, stale_mesh()); stale }; diff --git a/src/app/commands/draw.rs b/src/app/commands/draw.rs index e2f170d19..a23c1e810 100644 --- a/src/app/commands/draw.rs +++ b/src/app/commands/draw.rs @@ -23,7 +23,7 @@ impl OpenCADStudio { .objects .iter() .filter_map(|(handle, object)| match object { - acadrust::objects::ObjectType::MLineStyle(style) => { + codec::objects::ObjectType::MLineStyle(style) => { Some((*handle, style.clone())) } _ => None, @@ -110,7 +110,7 @@ impl OpenCADStudio { .trim_start_matches("ATTEDIT") .trim(); let parts: Vec<&str> = rest.splitn(2, char::is_whitespace).collect(); - let selected_handles: Vec = self.tabs[i] + let selected_handles: Vec = self.tabs[i] .scene .selected_entities() .iter() @@ -122,7 +122,7 @@ impl OpenCADStudio { } else { let mut found_any = false; for sh in &selected_handles { - if let Some(acadrust::EntityType::Insert(ins)) = self.tabs[i] + if let Some(codec::EntityType::Insert(ins)) = self.tabs[i] .scene .document .entities() @@ -166,7 +166,7 @@ impl OpenCADStudio { if self.tabs[i].scene.is_layer_locked(*sh) { continue; } - if let Some(acadrust::EntityType::Insert(ins)) = self.tabs[i] + if let Some(codec::EntityType::Insert(ins)) = self.tabs[i] .scene .document .entities_mut() @@ -225,7 +225,7 @@ impl OpenCADStudio { .document .entities() .filter_map(|entity| { - matches!(entity, acadrust::EntityType::AttributeDefinition(_)) + matches!(entity, codec::EntityType::AttributeDefinition(_)) .then_some(entity.common().handle) }) .filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle)) @@ -233,7 +233,7 @@ impl OpenCADStudio { self.push_undo_snapshot(i, "ATTDISP"); let mut count = 0usize; for handle in handles { - if let Some(acadrust::EntityType::AttributeDefinition(ad)) = + if let Some(codec::EntityType::AttributeDefinition(ad)) = self.tabs[i].scene.document.get_entity_mut(handle) { match sub.as_str() { @@ -488,7 +488,7 @@ impl OpenCADStudio { .document .entities() .filter_map(|entity| match entity { - acadrust::EntityType::Face3D(face) => { + codec::EntityType::Face3D(face) => { Some((face.common.handle, face.clone())) } _ => None, @@ -549,12 +549,12 @@ impl OpenCADStudio { .iter() .copied() .filter(|handle| { - let Some(acadrust::EntityType::Line(line)) = + let Some(codec::EntityType::Line(line)) = self.tabs[i].scene.document.get_entity(*handle) else { return false; }; - acadrust::entities::CenterMarkAssociation::read(&line.common.extended_data) + codec::entities::CenterMarkAssociation::read(&line.common.extended_data) .is_some() }) .collect(); @@ -793,7 +793,7 @@ impl OpenCADStudio { 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), + codec::types::Vector3::new(normal.x, normal.y, normal.z), elevation, ); let plane = crate::command::WorkingPlane::new( @@ -837,7 +837,7 @@ impl OpenCADStudio { ) }); let (scale, angle) = match entity { - Some(acadrust::EntityType::Hatch(hatch)) => ( + Some(codec::EntityType::Hatch(hatch)) => ( hatch.pattern_scale as f32, hatch.pattern_angle.to_degrees() as f32, ), @@ -1131,7 +1131,7 @@ impl OpenCADStudio { self.command_line.push_output("No constraints found."); return None; } - let dimensions: Vec = ids + let dimensions: Vec = ids .iter() .filter_map(|id| before.dimensions.get(id).copied()) .collect(); @@ -1456,7 +1456,7 @@ impl OpenCADStudio { annotational, decimals, ); - if let Some(acadrust::EntityType::Dimension(mut dimension)) = + if let Some(codec::EntityType::Dimension(mut dimension)) = self.tabs[i].scene.document.get_entity(handle).cloned() { let before = self.tabs[i].scene.document.get_entity_arc(handle); @@ -1472,7 +1472,7 @@ impl OpenCADStudio { dimension.base_mut().common.layer = crate::scene::parametric_constraints::DYNAMIC_DIMENSION_LAYER .to_string(); - dimension.base_mut().common.color = acadrust::types::Color::Rgb { + dimension.base_mut().common.color = codec::types::Color::Rgb { r: 103, g: 109, b: 118, @@ -1480,7 +1480,7 @@ impl OpenCADStudio { } self.tabs[i] .scene - .update_entity(acadrust::EntityType::Dimension(dimension)); + .update_entity(codec::EntityType::Dimension(dimension)); } let set = self.tabs[i].scene.parametric_constraint_set_mut(scope); let id = set.add(kind, refs, Some(DrivingValue::Named(name))); @@ -1554,7 +1554,7 @@ impl OpenCADStudio { return Some(self.apply_cmd_result(CmdResult::AddHorizontalConstraint { kind, selection: HorizontalConstraintSelection::Reference(reference), - direction: acadrust::types::Vector3::new( + direction: codec::types::Vector3::new( direction.x, direction.y, direction.z, @@ -2105,7 +2105,7 @@ impl OpenCADStudio { .filter(|handle| { matches!( self.tabs[i].scene.document.get_entity(*handle), - Some(acadrust::EntityType::Solid3D(_)) + Some(codec::EntityType::Solid3D(_)) ) }) .collect::>(); @@ -2166,12 +2166,12 @@ impl OpenCADStudio { matches!( scene.document.get_entity(*handle), Some( - acadrust::EntityType::Solid3D(_) - | acadrust::EntityType::Region(_) - | acadrust::EntityType::Surface(_) - | acadrust::EntityType::Mesh(_) - | acadrust::EntityType::PolygonMesh(_) - | acadrust::EntityType::PolyfaceMesh(_) + codec::EntityType::Solid3D(_) + | codec::EntityType::Region(_) + | codec::EntityType::Surface(_) + | codec::EntityType::Mesh(_) + | codec::EntityType::PolygonMesh(_) + | codec::EntityType::PolyfaceMesh(_) ) ) }) @@ -2180,9 +2180,9 @@ impl OpenCADStudio { matches!( scene.document.get_entity(*handle), Some( - acadrust::EntityType::Mesh(_) - | acadrust::EntityType::PolygonMesh(_) - | acadrust::EntityType::PolyfaceMesh(_) + codec::EntityType::Mesh(_) + | codec::EntityType::PolygonMesh(_) + | codec::EntityType::PolyfaceMesh(_) ) ) }); @@ -2218,12 +2218,12 @@ impl OpenCADStudio { .find(|(_, entity)| { matches!( entity, - acadrust::EntityType::Line(_) - | acadrust::EntityType::Arc(_) - | acadrust::EntityType::Circle(_) - | acadrust::EntityType::Ellipse(_) - | acadrust::EntityType::LwPolyline(_) - | acadrust::EntityType::Spline(_) + codec::EntityType::Line(_) + | codec::EntityType::Arc(_) + | codec::EntityType::Circle(_) + | codec::EntityType::Ellipse(_) + | codec::EntityType::LwPolyline(_) + | codec::EntityType::Spline(_) ) }) .map(|(handle, entity)| (handle, entity.clone())); @@ -2246,18 +2246,18 @@ impl OpenCADStudio { self.tabs[i].active_cmd = Some(Box::new(command)); return Some(iced::Task::none()); } - use acadrust::entities::Region; - use acadrust::types::Vector3; + use codec::entities::Region; + use codec::types::Vector3; let mut regions = Vec::new(); let mut sources = Vec::new(); for (handle, e) in self.tabs[i].scene.selected_entities().iter() { - if !matches!(e, acadrust::EntityType::Region(_)) { + if !matches!(e, codec::EntityType::Region(_)) { let Some((plane, loops, true)) = crate::scene::model::presspull_model::profile_geometry(e) else { continue; }; - let Some(body) = cadkernel::brep::planar_region(plane, &loops) else { + let Some(body) = kernel::brep::planar_region(plane, &loops) else { continue; }; let mut region = Region::new(); @@ -2276,7 +2276,7 @@ impl OpenCADStudio { .filter(|(handle, _)| !sources.contains(handle)) .filter_map(|(_, entity)| crate::entities::curve::entity_curve(entity)) .collect(); - let open_plane = cadkernel::space::common_curve_plane(&open_curves, 1.0e-6); + let open_plane = kernel::space::common_curve_plane(&open_curves, 1.0e-6); let open_plane_rejected = !open_curves.is_empty() && open_plane.is_none(); if let Some(plane) = open_plane { let working_plane = crate::command::WorkingPlane::new( @@ -2314,7 +2314,7 @@ impl OpenCADStudio { else { continue; }; - let Some(body) = cadkernel::brep::planar_region(plane, &[curves]) else { + let Some(body) = kernel::brep::planar_region(plane, &[curves]) else { continue; }; let mut region = Region::new(); @@ -2427,10 +2427,10 @@ impl OpenCADStudio { .filter_map(|entity| { matches!( entity, - acadrust::EntityType::Solid3D(_) - | acadrust::EntityType::Surface(_) - | acadrust::EntityType::Region(_) - | acadrust::EntityType::Body(_) + codec::EntityType::Solid3D(_) + | codec::EntityType::Surface(_) + | codec::EntityType::Region(_) + | codec::EntityType::Body(_) ) .then_some(entity.common().handle) }) @@ -2452,10 +2452,10 @@ impl OpenCADStudio { .document .entities() .filter_map(|entity| { - let acadrust::EntityType::Extended(extended) = entity else { + let codec::EntityType::Extended(extended) = entity else { return None; }; - let acadrust::entities::ExtendedEntityData::SectionObject(data) = + let codec::entities::ExtendedEntityData::SectionObject(data) = &extended.data else { return None; @@ -2583,8 +2583,8 @@ impl OpenCADStudio { matches!( scene.document.get_entity(*handle), Some( - acadrust::EntityType::Solid3D(_) - | acadrust::EntityType::Surface(_) + codec::EntityType::Solid3D(_) + | codec::EntityType::Surface(_) ) ) }) @@ -2641,7 +2641,7 @@ impl OpenCADStudio { 1 => ([0.0, v, 0.0], [0.0, 0.0, 1.0], [0.0, 1.0, 0.0]), _ => ([0.0, 0.0, v], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]), }; - let plane = cadkernel::space::Plane::orthonormal(origin, x, normal) + let plane = kernel::space::Plane::orthonormal(origin, x, normal) .expect("fixed world axes define a plane"); let side = glam::DVec3::from_array(origin) + glam::DVec3::from_array(normal) * if keep_low { -1.0 } else { 1.0 }; @@ -2680,7 +2680,7 @@ impl OpenCADStudio { let editable = (sel.len() == 1).then(|| sel[0].0).filter(|h| { self.tabs[i].scene.document.get_entity(*h).is_some_and(|e| { super::super::text_inline::read_text_field(e).is_some() - || matches!(e, acadrust::EntityType::Leader(_)) + || matches!(e, codec::EntityType::Leader(_)) }) }); if let Some(h) = editable { @@ -2759,7 +2759,7 @@ mod region_tests { .map(|entity| { ( entity.common().handle, - matches!(entity, acadrust::EntityType::Circle(_)), + matches!(entity, codec::EntityType::Circle(_)), ) }) .collect(); @@ -2780,7 +2780,7 @@ mod region_tests { .scene .document .entities() - .filter(|entity| matches!(entity, acadrust::EntityType::Region(_))) + .filter(|entity| matches!(entity, codec::EntityType::Region(_))) .count(); assert_eq!(region_count, 1); diff --git a/src/app/commands/fileops.rs b/src/app/commands/fileops.rs index e1237595b..07a7f69c0 100644 --- a/src/app/commands/fileops.rs +++ b/src/app/commands/fileops.rs @@ -173,7 +173,7 @@ impl OpenCADStudio { } } for e in self.tabs[i].scene.document.entities() { - if let acadrust::EntityType::RasterImage(img) = e { + if let codec::EntityType::RasterImage(img) = e { if !img.file_path.trim().is_empty() { deps.push(img.file_path.clone()); } diff --git a/src/app/commands/inquiry.rs b/src/app/commands/inquiry.rs index 8eb5ec360..402210691 100644 --- a/src/app/commands/inquiry.rs +++ b/src/app/commands/inquiry.rs @@ -4,7 +4,7 @@ impl OpenCADStudio { /// Align every selected object or complete group by one edge or center. fn align_selected_bounds(&mut self, i: usize, command: &str) { use crate::command::EntityTransform; - use cadkernel::space::BoundsAlignment; + use kernel::space::BoundsAlignment; use glam::DVec3; let (alignment, label) = match command { @@ -58,7 +58,7 @@ impl OpenCADStudio { } let extents: Vec<_> = bounds.iter().map(|(_, bounds)| *bounds).collect(); - let offsets = cadkernel::space::align_aabbs_2d(&extents, alignment) + let offsets = kernel::space::align_aabbs_2d(&extents, alignment) .expect("validated at least two finite bounds"); let pending = self.begin_undo(i, label, handles.len(), true); for ((unit, _), [dx, dy]) in bounds.iter().zip(offsets) { @@ -207,7 +207,7 @@ impl OpenCADStudio { *type_counts .entry(crate::entities::names::dxf_name(e)) .or_default() += 1; - if let acadrust::EntityType::Insert(ins) = e { + if let codec::EntityType::Insert(ins) = e { *block_counts.entry(ins.block_name.clone()).or_default() += 1; } } @@ -287,7 +287,7 @@ impl OpenCADStudio { use crate::modules::draw::modify::join::JoinCommand; // Pickfirst: with objects already selected, join them right // away instead of asking for a selection again. - let selected: Vec = + let selected: Vec = self.tabs[i].scene.selected.iter().copied().collect(); if selected.len() >= 2 { let task = @@ -325,8 +325,8 @@ impl OpenCADStudio { .filter_map(|e| { let h = e.common().handle.value(); match e { - acadrust::EntityType::LwPolyline(_) - | acadrust::EntityType::Polyline2D(_) => Some(( + codec::EntityType::LwPolyline(_) + | codec::EntityType::Polyline2D(_) => Some(( h, PeditTarget { is_poly: true, @@ -335,7 +335,7 @@ impl OpenCADStudio { mesh_closed: None, }, )), - acadrust::EntityType::Line(_) | acadrust::EntityType::Arc(_) => { + codec::EntityType::Line(_) | codec::EntityType::Arc(_) => { Some(( h, PeditTarget { @@ -346,7 +346,7 @@ impl OpenCADStudio { }, )) } - acadrust::EntityType::PolygonMesh(mesh) => Some(( + codec::EntityType::PolygonMesh(mesh) => Some(( h, PeditTarget { is_poly: true, @@ -364,7 +364,7 @@ impl OpenCADStudio { .collect(); // Pickfirst: an already-selected polyline (or line/arc, via // the convert prompt) skips the select step. - let preselected: Vec = + let preselected: Vec = self.tabs[i].scene.selected.iter().copied().collect(); let header = &self.tabs[i].scene.document.header; let cmd_obj = PeditCommand::new( @@ -387,12 +387,12 @@ impl OpenCADStudio { let targets = document .entities() .filter_map(|entity| { - let acadrust::EntityType::MLine(mline) = entity else { + let codec::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); + .unwrap_or_else(codec::objects::MLineStyle::standard); Some(( entity.common().handle.value(), MlineEditTarget { @@ -412,7 +412,7 @@ impl OpenCADStudio { let mut cmd_obj = SplineditCommand::new().with_delete_source(self.delete_objects != 0); let selected: Vec<_> = self.tabs[i].scene.selected.iter().copied().collect(); if let [handle] = selected.as_slice() { - if let Some(entity @ acadrust::EntityType::Spline(_)) = + if let Some(entity @ codec::EntityType::Spline(_)) = self.tabs[i].scene.document.get_entity(*handle).cloned() { if self.reject_locked_edit(i, *handle) { return Some(Task::none()); } @@ -444,7 +444,7 @@ impl OpenCADStudio { let selected: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect(); if selected.len() == 1 { - if let Some(acadrust::EntityType::Insert(_)) = + if let Some(codec::EntityType::Insert(_)) = selected.first().map(|(_, e)| e) { let handle = selected[0].0; @@ -470,7 +470,7 @@ impl OpenCADStudio { let selected: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect(); if selected.len() == 1 { - if let Some(acadrust::EntityType::Insert(_)) = + if let Some(codec::EntityType::Insert(_)) = selected.first().map(|(_, e)| e) { let handle = selected[0].0; @@ -487,7 +487,7 @@ impl OpenCADStudio { cmd if cmd.starts_with("BEDIT_BEGIN:") => { use crate::modules::draw::modify::block_edit::BlockEditSession; - use acadrust::Handle; + use codec::Handle; let handle_u64: u64 = cmd["BEDIT_BEGIN:".len()..].parse().unwrap_or(0); let insert_handle = Handle::new(handle_u64); @@ -496,7 +496,7 @@ impl OpenCADStudio { } let block_name = match self.tabs[i].scene.document.get_entity(insert_handle) { - Some(acadrust::EntityType::Insert(ins)) => ins.block_name.clone(), + Some(codec::EntityType::Insert(ins)) => ins.block_name.clone(), _ => { self.command_line .push_error(crate::t!("BEDIT: selected object is not a block reference.").as_ref()); @@ -563,7 +563,7 @@ impl OpenCADStudio { .document .entities() .filter_map(|entity| match entity { - acadrust::EntityType::Insert(reference) + codec::EntityType::Insert(reference) if reference.block_name.eq_ignore_ascii_case(&block_name) && !reference.attributes.is_empty() => { @@ -735,7 +735,7 @@ impl OpenCADStudio { let _ = self.tabs[i].scene.document.add_entity(entity); } for (handle, attributes) in session.reference_attributes { - if let Some(acadrust::EntityType::Insert(reference)) = + if let Some(codec::EntityType::Insert(reference)) = self.tabs[i].scene.document.get_entity_mut(handle) { reference.attributes = attributes; @@ -765,7 +765,7 @@ impl OpenCADStudio { use crate::modules::draw::modify::refedit::{ apply_insert_transform, RefEditSession, }; - use acadrust::Handle; + use codec::Handle; let handle_u64: u64 = cmd["REFEDIT_BEGIN:".len()..].parse().unwrap_or(0); let insert_handle = Handle::new(handle_u64); @@ -775,7 +775,7 @@ impl OpenCADStudio { // Get INSERT entity. let insert = match self.tabs[i].scene.document.get_entity(insert_handle) { - Some(acadrust::EntityType::Insert(ins)) => ins.clone(), + Some(codec::EntityType::Insert(ins)) => ins.clone(), _ => { self.command_line .push_error(crate::t!("REFEDIT: selected object is not an INSERT.").as_ref()); @@ -791,7 +791,7 @@ impl OpenCADStudio { let sz = insert.z_scale(); let forward = insert.get_transform(); let inverse = { - use acadrust::types::{Matrix3, Matrix4, Transform}; + use codec::types::{Matrix3, Matrix4, Transform}; let ocs_t = Matrix4::from_matrix3(Matrix3::arbitrary_axis(insert.normal).transpose()); let t_inv = Matrix4::translation( @@ -836,9 +836,9 @@ impl OpenCADStudio { .filter(|e| { !matches!( e, - acadrust::EntityType::Block(_) - | acadrust::EntityType::BlockEnd(_) - | acadrust::EntityType::AttributeDefinition(_) + codec::EntityType::Block(_) + | codec::EntityType::BlockEnd(_) + | codec::EntityType::AttributeDefinition(_) ) }) .collect() @@ -871,8 +871,8 @@ impl OpenCADStudio { let mut temp_handles = Vec::new(); for mut entity in block_entities { apply_insert_transform(&mut entity, &session); - entity.common_mut().handle = acadrust::Handle::NULL; - entity.common_mut().owner_handle = acadrust::Handle::NULL; + entity.common_mut().handle = codec::Handle::NULL; + entity.common_mut().owner_handle = codec::Handle::NULL; let h = self.tabs[i].scene.add_entity(entity); temp_handles.push(h); } @@ -933,7 +933,7 @@ impl OpenCADStudio { // offset entities carry handles at or above the watermark) — // those must fold into the block too, not stay behind in // model space (#423). - let working: Vec = { + let working: Vec = { let space = self.tabs[i].scene.current_layout_block_handle_pub(); let drawn = self.tabs[i].scene.document.entities().filter(|e| { let c = e.common(); @@ -950,7 +950,7 @@ impl OpenCADStudio { }; // Collect the edited temp entities. - let new_entities: Vec = working + let new_entities: Vec = working .iter() .filter_map(|h| self.tabs[i].scene.document.get_entity(*h).cloned()) .collect(); @@ -964,7 +964,7 @@ impl OpenCADStudio { .map(|mut entity| { apply_insert_inverse_transform(&mut entity, &session); let mut entity = normalize_entity_for_block(entity); - entity.common_mut().handle = acadrust::Handle::NULL; + entity.common_mut().handle = codec::Handle::NULL; entity.common_mut().owner_handle = session.br_handle; entity }) @@ -1021,7 +1021,7 @@ impl OpenCADStudio { }; // Remove the working set without modifying the block — the // temp copies plus anything created during the session (#423). - let working: Vec = { + let working: Vec = { let space = self.tabs[i].scene.current_layout_block_handle_pub(); let drawn = self.tabs[i].scene.document.entities().filter(|e| { let c = e.common(); @@ -1047,7 +1047,7 @@ impl OpenCADStudio { "ALIGN" => { use crate::modules::draw::modify::align::AlignCommand; - let selected: Vec = + let selected: Vec = self.tabs[i].scene.selected.iter().copied().collect(); let cmd = AlignCommand::with_selection(selected); @@ -1131,7 +1131,7 @@ impl OpenCADStudio { // ── FLATTEN — move selected (or all) entities to Z=0 ───────────── "FLATTEN" => { - let handles: Vec = { + let handles: Vec = { let scene = &self.tabs[i].scene; let sel = self.tabs[i].scene.selected_entities(); if sel.is_empty() { @@ -1201,7 +1201,7 @@ impl OpenCADStudio { let prop = parts.first().map(|s| s.to_uppercase()).unwrap_or_default(); let val = parts.get(1).map(|s| s.trim()).unwrap_or("").to_uppercase(); - let matched: Vec = self.tabs[i] + let matched: Vec = self.tabs[i] .scene .document .entities() @@ -1316,7 +1316,7 @@ impl OpenCADStudio { } else { let search_lc = search.to_lowercase(); let mut count = 0usize; - let handles: Vec = self.tabs[i] + let handles: Vec = self.tabs[i] .scene .document .entities() @@ -1475,7 +1475,7 @@ impl OpenCADStudio { // A dimension template also carries its dimension style, adopted // as the current DIMSTYLE so the cloned dimension matches (#239). match e { - acadrust::EntityType::Dimension(d) => Some(d.base().style_name.clone()), + codec::EntityType::Dimension(d) => Some(d.base().style_name.clone()), _ => None, }, ) @@ -1536,7 +1536,7 @@ impl OpenCADStudio { .layers .get(&layer) .map(|l| l.handle) - .unwrap_or(acadrust::types::Handle::NULL); + .unwrap_or(codec::types::Handle::NULL); let lt_handle = self.tabs[i] .scene .document @@ -1544,7 +1544,7 @@ impl OpenCADStudio { .iter() .find(|x| x.name.eq_ignore_ascii_case(&linetype)) .map(|x| x.handle) - .unwrap_or(acadrust::types::Handle::NULL); + .unwrap_or(codec::types::Handle::NULL); { let header = &mut self.tabs[i].scene.document.header; header.current_layer_name = layer.clone(); @@ -1565,7 +1565,7 @@ impl OpenCADStudio { .iter() .find(|s| s.name.eq_ignore_ascii_case(ds)) .map(|s| s.handle) - .unwrap_or(acadrust::types::Handle::NULL); + .unwrap_or(codec::types::Handle::NULL); let header = &mut self.tabs[i].scene.document.header; header.current_dimstyle_name = ds.clone(); header.current_dimstyle_handle = ds_handle; @@ -1622,8 +1622,8 @@ impl OpenCADStudio { /// Map a template entity to the draw-command verb that creates the same kind of /// object, or `None` when there is no interactive creator for it. Used by /// ADDSELECTED (issue #239). -fn add_selected_verb(entity: &acadrust::EntityType) -> Option<&'static str> { - use acadrust::EntityType; +fn add_selected_verb(entity: &codec::EntityType) -> Option<&'static str> { + use codec::EntityType; Some(match entity { EntityType::Point(_) => "POINT", EntityType::Line(_) => "LINE", @@ -1644,7 +1644,7 @@ fn add_selected_verb(entity: &acadrust::EntityType) -> Option<&'static str> { // Dimensions launch the matching dimension command by their stored type // (issue #239). Angular 2-line and 3-point both use DIMANGULAR. EntityType::Dimension(d) => { - use acadrust::entities::DimensionType; + use codec::entities::DimensionType; match d.base().dimension_type { DimensionType::Linear => "DIMLINEAR", DimensionType::Aligned => "DIMALIGNED", @@ -1660,10 +1660,10 @@ fn add_selected_verb(entity: &acadrust::EntityType) -> Option<&'static str> { }) } -fn entity_list_details(entity: &acadrust::EntityType) -> String { +fn entity_list_details(entity: &codec::EntityType) -> String { use std::f64::consts::PI; match entity { - acadrust::EntityType::Line(l) => crate::tf!( + codec::EntityType::Line(l) => crate::tf!( "from ({:.4},{:.4},{:.4}) to ({:.4},{:.4},{:.4}) len={:.4}", l.start.x, l.start.y, @@ -1676,7 +1676,7 @@ fn entity_list_details(entity: &acadrust::EntityType) -> String { + (l.end.z - l.start.z).powi(2)) .sqrt() ).into_owned(), - acadrust::EntityType::Circle(c) => crate::tf!( + codec::EntityType::Circle(c) => crate::tf!( "center ({:.4},{:.4},{:.4}) r={:.4} area={:.4}", c.center.x, c.center.y, @@ -1684,7 +1684,7 @@ fn entity_list_details(entity: &acadrust::EntityType) -> String { c.radius, PI * c.radius * c.radius ).into_owned(), - acadrust::EntityType::Arc(a) => crate::tf!( + codec::EntityType::Arc(a) => crate::tf!( "center ({:.4},{:.4},{:.4}) r={:.4} start={:.2}° end={:.2}°", a.center.x, a.center.y, @@ -1693,24 +1693,24 @@ fn entity_list_details(entity: &acadrust::EntityType) -> String { a.start_angle.to_degrees(), a.end_angle.to_degrees() ).into_owned(), - acadrust::EntityType::LwPolyline(p) => crate::tf!( + codec::EntityType::LwPolyline(p) => crate::tf!( "{} vertices closed={} elevation={:.4}", p.vertices.len(), p.is_closed, p.elevation ).into_owned(), - acadrust::EntityType::Text(t) => crate::tf!( + codec::EntityType::Text(t) => crate::tf!( "\"{}\" h={:.4} at ({:.4},{:.4})", t.value, t.height, t.insertion_point.x, t.insertion_point.y ).into_owned(), - acadrust::EntityType::MText(t) => crate::tf!( + codec::EntityType::MText(t) => crate::tf!( "\"{}\" h={:.4} at ({:.4},{:.4})", t.value.chars().take(40).collect::(), t.height, t.insertion_point.x, t.insertion_point.y ).into_owned(), - acadrust::EntityType::Insert(ins) => crate::tf!( + codec::EntityType::Insert(ins) => crate::tf!( "block=\"{}\" at ({:.4},{:.4},{:.4}) scale=({:.4},{:.4},{:.4}) rot={:.2}°", ins.block_name, ins.insert_point.x, @@ -1721,13 +1721,13 @@ fn entity_list_details(entity: &acadrust::EntityType) -> String { ins.z_scale(), ins.rotation.to_degrees() ).into_owned(), - acadrust::EntityType::Spline(s) => crate::tf!( + codec::EntityType::Spline(s) => crate::tf!( "{} ctrl pts degree={} closed={}", s.control_points.len(), s.degree, s.flags.closed ).into_owned(), - acadrust::EntityType::Ellipse(e) => crate::tf!( + codec::EntityType::Ellipse(e) => crate::tf!( "center ({:.4},{:.4}) major_len={:.4} ratio={:.4}", e.center.x, e.center.y, @@ -1738,40 +1738,40 @@ fn entity_list_details(entity: &acadrust::EntityType) -> String { } } -fn flatten_entity_z(entity: &acadrust::EntityType) -> Option { - use acadrust::types::Vector3; +fn flatten_entity_z(entity: &codec::EntityType) -> Option { + use codec::types::Vector3; let flattened = match entity { - acadrust::EntityType::Circle(_) - | acadrust::EntityType::Arc(_) - | acadrust::EntityType::Ellipse(_) - | acadrust::EntityType::LwPolyline(_) - | acadrust::EntityType::Polyline2D(_) => flatten_curve_entity(entity)?, - acadrust::EntityType::Line(source) => { + codec::EntityType::Circle(_) + | codec::EntityType::Arc(_) + | codec::EntityType::Ellipse(_) + | codec::EntityType::LwPolyline(_) + | codec::EntityType::Polyline2D(_) => flatten_curve_entity(entity)?, + codec::EntityType::Line(source) => { let mut line = source.clone(); line.start = flatten_point(line.start)?; line.end = flatten_point(line.end)?; line.thickness = 0.0; line.normal = Vector3::UNIT_Z; - acadrust::EntityType::Line(line) + codec::EntityType::Line(line) } - acadrust::EntityType::Polyline(source) => { + codec::EntityType::Polyline(source) => { let mut polyline = source.clone(); for vertex in &mut polyline.vertices { vertex.location = flatten_point(vertex.location)?; } - acadrust::EntityType::Polyline(polyline) + codec::EntityType::Polyline(polyline) } - acadrust::EntityType::Polyline3D(source) => { + codec::EntityType::Polyline3D(source) => { let mut polyline = source.clone(); polyline.elevation = 0.0; polyline.normal = Vector3::UNIT_Z; for vertex in &mut polyline.vertices { vertex.position = flatten_point(vertex.position)?; } - acadrust::EntityType::Polyline3D(polyline) + codec::EntityType::Polyline3D(polyline) } - acadrust::EntityType::Text(source) if positive_z_normal(source.normal) => { + codec::EntityType::Text(source) if positive_z_normal(source.normal) => { let mut text = source.clone(); text.insertion_point = flatten_point(text.insertion_point)?; if let Some(alignment) = text.alignment_point { @@ -1779,22 +1779,22 @@ fn flatten_entity_z(entity: &acadrust::EntityType) -> Option { + codec::EntityType::MText(source) if positive_z_normal(source.normal) => { let mut text = source.clone(); text.insertion_point = flatten_point(text.insertion_point)?; text.normal = Vector3::UNIT_Z; - acadrust::EntityType::MText(text) + codec::EntityType::MText(text) } - acadrust::EntityType::Point(source) => { + codec::EntityType::Point(source) => { let mut point = source.clone(); point.location = flatten_point(point.location)?; point.thickness = 0.0; point.normal = Vector3::UNIT_Z; - acadrust::EntityType::Point(point) + codec::EntityType::Point(point) } - acadrust::EntityType::Spline(source) => { + codec::EntityType::Spline(source) => { let mut spline = source.clone(); for point in &mut spline.control_points { *point = flatten_point(*point)?; @@ -1806,9 +1806,9 @@ fn flatten_entity_z(entity: &acadrust::EntityType) -> Option { + codec::EntityType::Solid(source) => { let corners = crate::entities::solid::wcs_corners(source); let mut solid = source.clone(); solid.first_corner = flatten_array(corners[0])?; @@ -1817,15 +1817,15 @@ fn flatten_entity_z(entity: &acadrust::EntityType) -> Option { + codec::EntityType::Face3D(source) => { let mut face = source.clone(); face.first_corner = flatten_point(face.first_corner)?; face.second_corner = flatten_point(face.second_corner)?; face.third_corner = flatten_point(face.third_corner)?; face.fourth_corner = flatten_point(face.fourth_corner)?; - acadrust::EntityType::Face3D(face) + codec::EntityType::Face3D(face) } _ => return None, }; @@ -1833,19 +1833,19 @@ fn flatten_entity_z(entity: &acadrust::EntityType) -> Option Option { - use acadrust::types::{Vector2, Vector3}; - use cadkernel::geom2d::Curve; +fn flatten_curve_entity(entity: &codec::EntityType) -> Option { + use codec::types::{Vector2, Vector3}; + use kernel::geom2d::Curve; match entity { - acadrust::EntityType::Ellipse(source) + codec::EntityType::Ellipse(source) if source.center.z == 0.0 && source.major_axis.z == 0.0 && source.normal == Vector3::UNIT_Z => { return None; } - acadrust::EntityType::LwPolyline(source) + codec::EntityType::LwPolyline(source) if !z_axis_normal(source.normal) && (source.constant_width != 0.0 || source @@ -1855,7 +1855,7 @@ fn flatten_curve_entity(entity: &acadrust::EntityType) -> Option Option { + codec::EntityType::LwPolyline(source) if source.vertices.len() < 2 => { let plane = crate::entities::curve::ocs_plane(source.normal, source.elevation); let mut polyline = source.clone(); for vertex in &mut polyline.vertices { @@ -1879,9 +1879,9 @@ fn flatten_curve_entity(entity: &acadrust::EntityType) -> Option { + codec::EntityType::Polyline2D(source) if source.vertices.len() < 2 => { let plane = crate::entities::curve::ocs_plane(source.normal, source.elevation); let mut polyline = source.clone(); for vertex in &mut polyline.vertices { @@ -1894,22 +1894,22 @@ fn flatten_curve_entity(entity: &acadrust::EntityType) -> Option {} } let curve = crate::entities::curve::entity_curve_xy(entity)?; match (entity, curve) { - (acadrust::EntityType::Circle(source), Curve::Circle(curve)) => { + (codec::EntityType::Circle(source), Curve::Circle(curve)) => { let mut circle = source.clone(); circle.center = Vector3::new(curve.centre[0], curve.centre[1], 0.0); circle.radius = curve.radius; circle.thickness = 0.0; circle.normal = Vector3::UNIT_Z; - Some(acadrust::EntityType::Circle(circle)) + Some(codec::EntityType::Circle(circle)) } - (acadrust::EntityType::Arc(source), Curve::Arc(curve)) => { + (codec::EntityType::Arc(source), Curve::Arc(curve)) => { let mut arc = source.clone(); arc.center = Vector3::new(curve.centre[0], curve.centre[1], 0.0); arc.radius = curve.radius; @@ -1917,18 +1917,18 @@ fn flatten_curve_entity(entity: &acadrust::EntityType) -> Option { + (codec::EntityType::Circle(source), Curve::Ellipse(curve)) => { flattened_ellipse(source.common.clone(), curve) } - (acadrust::EntityType::Arc(source), Curve::Ellipse(curve)) => { + (codec::EntityType::Arc(source), Curve::Ellipse(curve)) => { flattened_ellipse(source.common.clone(), curve) } - (acadrust::EntityType::Ellipse(source), Curve::Ellipse(curve)) => { + (codec::EntityType::Ellipse(source), Curve::Ellipse(curve)) => { flattened_ellipse(source.common.clone(), curve) } - (acadrust::EntityType::LwPolyline(source), Curve::Polyline(curve)) => { + (codec::EntityType::LwPolyline(source), Curve::Polyline(curve)) => { if curve.vertices.len() != source.vertices.len() { return None; } @@ -1940,9 +1940,9 @@ fn flatten_curve_entity(entity: &acadrust::EntityType) -> Option { + (codec::EntityType::Polyline2D(source), Curve::Polyline(curve)) => { if curve.vertices.len() != source.vertices.len() { return None; } @@ -1955,23 +1955,23 @@ fn flatten_curve_entity(entity: &acadrust::EntityType) -> Option None, } } fn flattened_ellipse( - common: acadrust::entities::EntityCommon, - curve: cadkernel::geom2d::EllipseArc, -) -> Option { - use acadrust::types::Vector3; + common: codec::entities::EntityCommon, + curve: kernel::geom2d::EllipseArc, +) -> Option { + use codec::types::Vector3; let geometry = curve.ellipse; if !geometry.major_radius.is_finite() || geometry.major_radius <= 0.0 { return None; } - let mut ellipse = acadrust::entities::Ellipse::new(); + let mut ellipse = codec::entities::Ellipse::new(); ellipse.common = common; ellipse.center = Vector3::new(geometry.centre[0], geometry.centre[1], 0.0); ellipse.major_axis = Vector3::new( @@ -1983,36 +1983,36 @@ fn flattened_ellipse( ellipse.start_parameter = curve.start_parameter; ellipse.end_parameter = curve.end_parameter; ellipse.normal = Vector3::UNIT_Z; - Some(acadrust::EntityType::Ellipse(ellipse)) + Some(codec::EntityType::Ellipse(ellipse)) } -fn flatten_point(point: acadrust::types::Vector3) -> Option { +fn flatten_point(point: codec::types::Vector3) -> Option { flatten_array([point.x, point.y, point.z]) } -fn flatten_array(point: [f64; 3]) -> Option { - use cadkernel::space::Plane; +fn flatten_array(point: [f64; 3]) -> Option { + use kernel::space::Plane; let point = Plane::XY.point_at(Plane::XY.project(point)?); - Some(acadrust::types::Vector3::new(point[0], point[1], point[2])) + Some(codec::types::Vector3::new(point[0], point[1], point[2])) } -fn flatten_vector(vector: acadrust::types::Vector3) -> Option { - use cadkernel::space::Plane; +fn flatten_vector(vector: codec::types::Vector3) -> Option { + use kernel::space::Plane; let vector = Plane::XY.vector_at(Plane::XY.project_vector([ vector.x, vector.y, vector.z, ])?); - Some(acadrust::types::Vector3::new( + Some(codec::types::Vector3::new( vector[0], vector[1], vector[2], )) } -fn positive_z_normal(normal: acadrust::types::Vector3) -> bool { +fn positive_z_normal(normal: codec::types::Vector3) -> bool { normal.x == 0.0 && normal.y == 0.0 && normal.z > 0.0 } -fn z_axis_normal(normal: acadrust::types::Vector3) -> bool { +fn z_axis_normal(normal: codec::types::Vector3) -> bool { normal.x == 0.0 && normal.y == 0.0 && normal.z != 0.0 } @@ -2044,7 +2044,7 @@ impl OpenCADStudio { if sel.len() == 1 { let (h, e) = sel[0]; match e { - acadrust::EntityType::Insert(ins) if !ins.attributes.is_empty() => Some(h), + codec::EntityType::Insert(ins) if !ins.attributes.is_empty() => Some(h), _ => None, } } else { @@ -2152,15 +2152,15 @@ impl ArithParser { #[cfg(test)] mod flatten_tests { use super::flatten_entity_z; - use acadrust::types::Vector3; - use acadrust::EntityType; + use codec::types::Vector3; + use codec::EntityType; #[test] fn flattens_3d_polyline_vertices() { - let mut pl = acadrust::entities::Polyline3D::new(); + let mut pl = codec::entities::Polyline3D::new(); for z in [5.0, -2.0, 7.5] { pl.vertices - .push(acadrust::entities::Vertex3DPolyline::new(Vector3::new(1.0, 2.0, z))); + .push(codec::entities::Vertex3DPolyline::new(Vector3::new(1.0, 2.0, z))); } pl.elevation = 5.0; @@ -2178,9 +2178,9 @@ mod flatten_tests { #[test] fn flattens_2d_polyline_vertices_and_elevation() { - let mut pl = acadrust::entities::Polyline2D::new(); + let mut pl = codec::entities::Polyline2D::new(); pl.vertices - .push(acadrust::entities::Vertex2D::new(Vector3::new(3.0, 4.0, 9.0))); + .push(codec::entities::Vertex2D::new(Vector3::new(3.0, 4.0, 9.0))); pl.elevation = 9.0; let entity = EntityType::Polyline2D(pl); @@ -2196,7 +2196,7 @@ mod flatten_tests { #[test] fn flattens_solid_corners() { - let solid = acadrust::entities::Solid::new( + let solid = codec::entities::Solid::new( Vector3::new(0.0, 0.0, 1.0), Vector3::new(1.0, 0.0, 2.0), Vector3::new(1.0, 1.0, 3.0), @@ -2217,7 +2217,7 @@ mod flatten_tests { #[test] fn reports_unsupported_entities_as_not_moved() { - let entity = EntityType::Ray(acadrust::entities::Ray::new( + let entity = EntityType::Ray(codec::entities::Ray::new( Vector3::new(0.0, 0.0, 5.0), Vector3::new(1.0, 0.0, 0.0), )); @@ -2226,7 +2226,7 @@ mod flatten_tests { #[test] fn still_flattens_a_line() { - let mut line = acadrust::entities::Line::new(); + let mut line = codec::entities::Line::new(); line.start = Vector3::new(0.0, 0.0, 3.0); line.end = Vector3::new(1.0, 1.0, 4.0); let entity = EntityType::Line(line); @@ -2237,7 +2237,7 @@ mod flatten_tests { #[test] fn already_flat_line_is_not_moved() { - let mut line = acadrust::entities::Line::new(); + let mut line = codec::entities::Line::new(); line.start = Vector3::new(0.0, 0.0, 0.0); line.end = Vector3::new(1.0, 1.0, 0.0); let entity = EntityType::Line(line); @@ -2246,7 +2246,7 @@ mod flatten_tests { #[test] fn tilted_circle_projects_to_ellipse() { - let mut circle = acadrust::entities::Circle::from_center_radius( + let mut circle = codec::entities::Circle::from_center_radius( Vector3::new(2.0, 3.0, 4.0), 5.0, ); @@ -2266,7 +2266,7 @@ mod flatten_tests { #[test] fn tilted_solid_uses_world_projection() { - let mut solid = acadrust::entities::Solid::new( + let mut solid = codec::entities::Solid::new( Vector3::new(0.0, 0.0, 2.0), Vector3::new(1.0, 0.0, 2.0), Vector3::new(1.0, 1.0, 2.0), @@ -2297,10 +2297,10 @@ mod flatten_tests { #[cfg(test)] mod align_selected_bounds_tests { use super::*; - use acadrust::entities::{EntityType, Line}; - use acadrust::types::Vector3; + use codec::entities::{EntityType, Line}; + use codec::types::Vector3; - fn add_line(app: &mut OpenCADStudio, x1: f64, y1: f64, x2: f64, y2: f64) -> acadrust::Handle { + fn add_line(app: &mut OpenCADStudio, x1: f64, y1: f64, x2: f64, y2: f64) -> codec::Handle { app.tabs[app.active_tab] .scene .add_entity(EntityType::Line(Line::from_points( @@ -2309,14 +2309,14 @@ mod align_selected_bounds_tests { ))) } - fn line_start_x(app: &OpenCADStudio, handle: acadrust::Handle) -> f64 { + fn line_start_x(app: &OpenCADStudio, handle: codec::Handle) -> f64 { match app.tabs[app.active_tab].scene.document.get_entity(handle) { Some(EntityType::Line(l)) => l.start.x.min(l.end.x), other => panic!("expected a Line, got {other:?}"), } } - fn line_max_y(app: &OpenCADStudio, handle: acadrust::Handle) -> f64 { + fn line_max_y(app: &OpenCADStudio, handle: codec::Handle) -> f64 { match app.tabs[app.active_tab].scene.document.get_entity(handle) { Some(EntityType::Line(l)) => l.start.y.max(l.end.y), other => panic!("expected a Line, got {other:?}"), diff --git a/src/app/commands/layerprops.rs b/src/app/commands/layerprops.rs index 34117610e..9c222b6fd 100644 --- a/src/app/commands/layerprops.rs +++ b/src/app/commands/layerprops.rs @@ -5,7 +5,7 @@ impl OpenCADStudio { match cmd { // ── LAYER management ───────────────────────────────────────── cmd if cmd == "LAYER" || cmd.starts_with("LAYER ") || cmd.starts_with("LA ") => { - use acadrust::tables::Layer; + use codec::tables::Layer; let raw_rest = if cmd.starts_with("LAYER ") { cmd.trim_start_matches("LAYER ").trim() } else if cmd.starts_with("LA ") { @@ -127,7 +127,7 @@ impl OpenCADStudio { if let Ok(idx) = color_str.parse::() { if let Some(l) = self.tabs[i].scene.document.layers.get_mut(&layer_name) { - l.color = acadrust::types::Color::from_index(idx); + l.color = codec::types::Color::from_index(idx); l.color_name = None; l.book_name = None; self.push_undo_snapshot(i, "LAYER COLOR"); @@ -186,8 +186,8 @@ impl OpenCADStudio { // ── UCS management (inline `UCS