diff --git a/Cargo.lock b/Cargo.lock index d1285d5fb..7ec62dc91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4361,7 +4361,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.6.0" -source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=ddf0fd2#ddf0fd2d7541bebce137c1bf960db54fcd12bcd2" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=8c4b9a3#8c4b9a3783cf68a1cbb383eaa61cac043066cf22" dependencies = [ "anyhow", "bitflags 2.13.1", @@ -4384,7 +4384,7 @@ dependencies = [ [[package]] name = "opencadgraph" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=45c9649#45c96493fb55d8825561bca9e9d7e5e700dcd3fa" +source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=6a2babc#6a2babc82532143500d66a92ed9fbc14d14061fa" dependencies = [ "opencadkernel", "serde_json", @@ -4393,7 +4393,7 @@ dependencies = [ [[package]] name = "opencadkernel" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=b80079a#b80079a1e61b3f3519dc759e6553761e24787f67" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=ee970e4#ee970e41decd4f96548f472e5078d8a4335f73ce" dependencies = [ "cavalier_contours", "opencadcodec", @@ -4404,7 +4404,7 @@ dependencies = [ [[package]] name = "opencadkernel-constraints" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=b80079a#b80079a1e61b3f3519dc759e6553761e24787f67" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=ee970e4#ee970e41decd4f96548f472e5078d8a4335f73ce" dependencies = [ "nalgebra", ] diff --git a/Cargo.toml b/Cargo.toml index f6e36fba7..638c98922 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,10 +37,10 @@ rfd = "0.17" clap = { version = "4", features = ["derive", "string"] } env_logger = "0.11" log = "0.4" -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "ddf0fd2", features = ["serde"] } -kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "b80079a", features = ["acis", "offset"] } -kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "b80079a" } -graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "45c9649" } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8c4b9a3", features = ["serde"] } +kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "ee970e4", features = ["acis", "offset"] } +kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "ee970e4" } +graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "6a2babc" } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } # Shared SAVE serialization dispatch + web-worker wire format: one # implementation for the inline (fallback/native) path and the worker path. diff --git a/crates/ocs_plugin_api/Cargo.toml b/crates/ocs_plugin_api/Cargo.toml index afe9c60ae..5eb68b8b3 100644 --- a/crates/ocs_plugin_api/Cargo.toml +++ b/crates/ocs_plugin_api/Cargo.toml @@ -15,7 +15,7 @@ serde_json = { version = "1", optional = true } # Pulled in only by the `host` feature, which adds the `opencadcodec`-typed # `HostApi` runtime surface. The default crate stays dependency-free so engine # crates and external tooling can depend on the manifest/ribbon contract cheaply. -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "ddf0fd2", optional = true, features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8c4b9a3", optional = true, features = ["serde"] } # Runtime IPC and serialization (host feature only). interprocess = { version = "2", optional = true } @@ -39,7 +39,7 @@ serde_json = "1" serde = { version = "1", features = ["derive"] } cargo-lock = "11" # opencadcodec is scanned at build time to generate the embedded type registry. -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "ddf0fd2", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8c4b9a3", features = ["serde"] } [dev-dependencies] serde_json = "1" diff --git a/crates/ocs_save/Cargo.toml b/crates/ocs_save/Cargo.toml index 43971aea4..f4f59e9f9 100644 --- a/crates/ocs_save/Cargo.toml +++ b/crates/ocs_save/Cargo.toml @@ -10,6 +10,6 @@ publish = false # exactly one implementation. No UI/app dependencies — only the codec + serde. [dependencies] -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "ddf0fd2", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8c4b9a3", features = ["serde"] } bincode = "1.3" serde = { version = "1", features = ["derive"] } diff --git a/crates/ocs_web_worker/Cargo.toml b/crates/ocs_web_worker/Cargo.toml index ec625d638..93b1c1627 100644 --- a/crates/ocs_web_worker/Cargo.toml +++ b/crates/ocs_web_worker/Cargo.toml @@ -8,7 +8,7 @@ publish = false crate-type = ["cdylib"] [dependencies] -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "ddf0fd2", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8c4b9a3", features = ["serde"] } bincode = "1.3" serde = { version = "1", features = ["derive"] } console_error_panic_hook = "0.1" diff --git a/src/modules/draw/modify/explode.rs b/src/modules/draw/modify/explode.rs index cf0888e56..441909933 100644 --- a/src/modules/draw/modify/explode.rs +++ b/src/modules/draw/modify/explode.rs @@ -23,8 +23,8 @@ use codec::entities::{ }; use codec::entities::{Polyline, Polyline2D}; use codec::tables::BlockRecord; -use codec::types::{Transform, Vector3}; -use codec::{CadDocument, Entity, EntityType, Handle}; +use codec::types::Vector3; +use codec::{CadDocument, EntityType, Handle}; use crate::command::{CadCommand, CmdResult, WorkingPlane}; use crate::entities::curve::lwpolyline_world_xy; @@ -98,68 +98,11 @@ pub fn explode_entity(entity: &EntityType, document: &CadDocument) -> Vec { - let explodable = document - .block_records - .get(&ins.block_name) - .map_or(true, |br| br.explodable); - if !explodable { - vec![] - } else { - // The codec applies only `Insert::get_transform`, which does - // not know the block's base point, so shift the definition's - // contents by -base first: get_transform(P - base) is exactly - // world = insert_point + Rot*Scale*(P - base), the placement - // the render path uses. MINSERT cells and nested references - // get the same treatment because the shift happens on the - // contents, not on the insert. - let base = crate::scene::render_graph::block_base_point(document, &ins.block_name); - let to_block = Transform::from_translation(Vector3::new(-base.x, -base.y, -base.z)); - let mut attdefs: Vec = Vec::new(); - let entities: Vec = document - .entities_in_block(&ins.block_name) - .map(|entity| { - let mut shifted = entity.clone(); - Entity::apply_transform(shifted.as_entity_mut(), &to_block); - if let EntityType::AttributeDefinition(_) = &shifted { - attdefs.push(shifted.clone()); - } - shifted - }) - .collect(); - let mut pieces: Vec = ins - .explode(&entities) - .into_iter() - .map(normalize_insert_entity) - .collect(); - - // Exploding an INSERT drops attribute values (ATTRIBs) - // and the attribute definitions (ATTDEF tags) reappear as ATTDEF entities. - // The codec's `ins.explode` skips `AttributeDefinition` entities, so we - // transform and append them here. - if !attdefs.is_empty() { - let insert_xform = ins.get_transform(); - for ad_ent in &attdefs { - let mut exploded_ad = ad_ent.clone(); - Entity::apply_transform(exploded_ad.as_entity_mut(), &insert_xform); - // Resolve common properties (layer "0", ByBlock color / line weight) - let common = exploded_ad.common_mut(); - if common.layer == "0" { - common.layer = ins.common.layer.clone(); - } - if common.color == codec::types::Color::ByBlock { - common.color = ins.common.color; - } - if common.line_weight == codec::types::LineWeight::ByBlock { - common.line_weight = ins.common.line_weight; - } - pieces.push(exploded_ad); - } - } - - pieces - } - } + EntityType::Insert(ins) => ins + .explode_from_document(document) + .into_iter() + .map(normalize_insert_entity) + .collect(), EntityType::MLine(ml) => explode_mline(ml), EntityType::Dimension(dim) => explode_dimension(dim, document), EntityType::MText(t) => crate::entities::mtext::explode_mtext(t, document), diff --git a/src/scene/block_base_point_tests.rs b/src/scene/block_base_point_tests.rs index d3501771c..230eb8da3 100644 --- a/src/scene/block_base_point_tests.rs +++ b/src/scene/block_base_point_tests.rs @@ -671,9 +671,8 @@ fn locked_block_definition_dialog_prefills_the_existing_definition_base_point() #[test] fn locked_explode_places_geometry_at_convention_positions() { - // base-point: `explode_entity` shifts the definition's contents by -base before - // handing them to the codec's explode, so the pieces land where the render - // path puts them (see the module header for the derivation). + // The document-aware codec explosion uses the definition's base point, + // so the pieces land where the render path puts them. let mut scene = scene_with_bp_test(); let insert = placed_instance(&mut scene); let pieces = crate::modules::draw::modify::explode::explode_entity( @@ -739,6 +738,62 @@ fn locked_explode_places_geometry_at_convention_positions() { ); } +#[test] +fn locked_minsert_explode_restores_attdefs_in_each_rotated_cell() { + let scene = scene_with_bp_test(); + let mut insert = Insert::new(BP_TEST, v(100.0, 100.0, 0.0)) + .with_uniform_scale(2.0) + .with_rotation(std::f64::consts::FRAC_PI_2) + .with_array(2, 2, 10.0, 20.0); + insert + .attributes + .push(AttributeEntity::new("TAG1".into(), "Instance value".into())); + let pieces = crate::modules::draw::modify::explode::explode_entity( + &EntityType::Insert(insert), + &scene.document, + ); + let definitions: Vec<_> = pieces + .iter() + .filter_map(|entity| match entity { + EntityType::AttributeDefinition(definition) => Some(definition), + _ => None, + }) + .collect(); + assert_eq!(definitions.len(), 4); + for (definition, expected) in definitions.iter().zip([ + [94.0, 104.0, 0.0], + [94.0, 114.0, 0.0], + [74.0, 104.0, 0.0], + [74.0, 114.0, 0.0], + ]) { + assert_close(definition.insertion_point, expected, 1e-6, "array ATTDEF"); + assert_eq!(definition.tag, "TAG1"); + assert_eq!(definition.default_value, ""); + } + let lines: Vec<_> = pieces + .iter() + .filter_map(|entity| match entity { + EntityType::Line(line) => Some(line), + _ => None, + }) + .collect(); + assert_eq!(lines.len(), 4); + for (line, expected) in lines.iter().zip([ + [100.0, 100.0, 0.0], + [100.0, 110.0, 0.0], + [80.0, 100.0, 0.0], + [80.0, 110.0, 0.0], + ]) { + assert_close(line.start, expected, 1e-6, "array LINE"); + } + assert_close( + block_attdefs(&scene.document, BP_TEST)[0].insertion_point, + [12.0, 8.0, 0.0], + 1e-6, + "source ATTDEF stays in definition coordinates", + ); +} + #[test] fn locked_xclip_boundary_round_trips_through_the_base_point_convention() { // base-point follow-up fix: the XCLIP ring is stored as WCS points plus the