From 632540efff8d6ab068d94a0ed7d821e5f70bdd7e Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:33:47 +0300 Subject: [PATCH] Sweep history records in the reference layout; kernel and codec revs New sweep records store the source and placed profile frames, the default mitred joint, groups 292-294 and a 3D polyline path as a wire body, so the reference re-evaluates them. Builds against the sweep history codec and kernel revisions. Co-Authored-By: Claude Opus 5.5 --- Cargo.lock | 4 +- Cargo.toml | 6 +++ src/app/command_driver/solid3d.rs | 5 ++- src/entities/object_data.rs | 6 +-- src/scene/model/solid_history.rs | 2 +- src/scene/model/sweep_command_model.rs | 51 +++++++++++++++++++++++--- 6 files changed, 61 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d079feccb..a2036b8d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4205,7 +4205,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.6.0" -source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=1ca7d33#1ca7d3360a34384b59297ffe8aa18b157d2701d9" +source = "git+https://github.com/ramox81/cadcodec.git?rev=08b2296#08b229649d5afc5ed948726088df15d72f404c29" dependencies = [ "anyhow", "bitflags 2.13.1", @@ -4237,7 +4237,7 @@ dependencies = [ [[package]] name = "opencadkernel" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=7fb0abb#7fb0abb4ee8e4266a770eb673ac7757c71321513" +source = "git+https://github.com/ramox81/cadkernel.git?rev=a259e57#a259e570fc727fca273ac0b266616045744ad408" dependencies = [ "cavalier_contours", "opencadcodec", diff --git a/Cargo.toml b/Cargo.toml index a1638dc46..89bb8ba86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,12 @@ objc2-foundation = { version = "0.2", features = ["NSArray", "NSURL", "NSString" iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" } iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" } +[patch."https://github.com/HakanSeven12/opencadcodec.git"] +opencadcodec = { git = "https://github.com/ramox81/cadcodec.git", rev = "08b2296" } + +[patch."https://github.com/HakanSeven12/opencadkernel.git"] +opencadkernel = { git = "https://github.com/ramox81/cadkernel.git", rev = "a259e57" } + [target.'cfg(not(target_arch = "wasm32"))'.dependencies] getrandom = "0.3" ocs_plugin_api = { path = "crates/ocs_plugin_api", features = ["host"] } diff --git a/src/app/command_driver/solid3d.rs b/src/app/command_driver/solid3d.rs index caac03566..ccaa8de3c 100644 --- a/src/app/command_driver/solid3d.rs +++ b/src/app/command_driver/solid3d.rs @@ -486,6 +486,8 @@ impl OpenCADStudio { .iter() .map(|(_, entity)| entity.clone()) .collect::>(); + // Group 294: a base point the user picked. + let picked_base = options.base_point.is_some(); let options = sweep_model::sweep_selection_options(&selection, options); let pending = if profiles.is_empty() { None @@ -496,7 +498,8 @@ impl OpenCADStudio { let mut consumed = Vec::new(); 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 mut record = sweep_model::sweep_record(&profile, path, options)?; + record.flags_294_296[0] |= picked_base; let (_, _, closed) = kernel::acis::sweep_profile_geometry( record.sweep_entity.as_ref()?, record.sweep_entity_transform, diff --git a/src/entities/object_data.rs b/src/entities/object_data.rs index bb1bb7e95..3270bc545 100644 --- a/src/entities/object_data.rs +++ b/src/entities/object_data.rs @@ -315,7 +315,7 @@ fn history_operation_text(operation: &SolidHistoryOperation) -> String { value.base_face ), SolidHistoryOperation::Sweep(value) | SolidHistoryOperation::Extrusion(value) => format!( - "{}; sweep {}; path {}; direction {:.6},{:.6},{:.6}; draft {:.6}; scale {:.6}; twist {:.6}; align {:.6}; bank {}; intersections {}", + "{}; sweep {}; path {}; direction {:.6},{:.6},{:.6}; draft {:.6}; scale {:.6}; twist {:.6}; align {:.6}; align start {}; bank {}", history_base_text(&value.base), embedded_name(value.sweep_entity.as_ref()), embedded_name(value.path_entity.as_ref()), @@ -326,8 +326,8 @@ fn history_operation_text(operation: &SolidHistoryOperation) -> String { value.scale_factor, value.twist_angle, value.align_angle, - value.bank, - value.check_intersections + value.align_start, + value.bank ), SolidHistoryOperation::Loft(value) => format!( "{}; cross sections {}; guides {}", diff --git a/src/scene/model/solid_history.rs b/src/scene/model/solid_history.rs index bca6763f6..e8a1598d5 100644 --- a/src/scene/model/solid_history.rs +++ b/src/scene/model/solid_history.rs @@ -651,7 +651,7 @@ pub fn extrusion_surface_data( path_entity_transform: value.path_entity_transform, is_solid: false, sweep_alignment_flags: value.align_option as i16, - align_start: value.has_align_start, + align_start: value.align_start, bank: value.bank, base_point_set: true, reference_vector: value.reference_point, diff --git a/src/scene/model/sweep_command_model.rs b/src/scene/model/sweep_command_model.rs index 5a9671728..142d78e4d 100644 --- a/src/scene/model/sweep_command_model.rs +++ b/src/scene/model/sweep_command_model.rs @@ -17,6 +17,24 @@ fn embedded_sweep_profile(entity: &EntityType) -> Option<(codec::entities::Embed } } +/// The sweep path as the reference records it: a 3D polyline as a wire +/// body, anything else as its embedded entity. +fn embedded_sweep_path(path: &EntityType) -> Option { + let EntityType::Polyline3D(value) = path else { + return embedded_path(path); + }; + let points = value.vertices.iter() + .map(|vertex| [vertex.position.x, vertex.position.y, vertex.position.z]) + .collect::>(); + let mut document = codec::entities::acis::SatDocument::new(); + kernel::acis::append_polyline_wire(&points, value.is_closed(), &mut document).ok()?; + Some(codec::entities::EmbeddedEntity::Body { + // The 3D polyline object type. + type_code: 16, + acis_data: codec::entities::AcisData::from_sat(&document.to_sat_string()), + }) +} + pub fn is_sweep_profile(entity: &EntityType) -> bool { embedded_sweep_profile(entity).is_some_and(|(profile, transform)| { kernel::acis::sweep_profile_geometry(&profile, transform).is_ok() @@ -64,10 +82,11 @@ pub fn sweep_record(profile: &EntityType, path: &EntityType, options: SweepOptio base, operation_major: 1, sweep_entity: Some(sweep_entity), - path_entity: Some(embedded_path(path)?), + path_entity: Some(embedded_sweep_path(path)?), scale_factor: options.scale, twist_angle: options.twist_angle, - align_option: u8::from(options.align), + // 1 aligns the profile to the path; 2 only moves it to the path start. + align_option: if options.align { 1 } else { 2 }, has_align_start: true, bank: options.bank, sweep_entity_transform, @@ -100,12 +119,32 @@ fn placed_sweep_record(profile: &EntityType, mut record: SolidHistorySweep) -> O [0.0, 0.0, 0.0, 1.0], ], }); + // The record's matrices are the source profile frame (at the base point, + // in the profile plane) and the frame it is placed in at the path start; + // the reference re-places the profile from them when it re-evaluates. + let (plane, _, _) = kernel::acis::sweep_profile_geometry( + record.sweep_entity.as_ref()?, + record.sweep_entity_transform, + ).ok()?; + let x_axis = glam::DVec3::from_array(plane.x_axis).try_normalize()?; + let normal = glam::DVec3::from_array(plane.normal()?); + let base = glam::DVec3::new(record.reference_point.x, record.reference_point.y, record.reference_point.z); + let source_frame = glam::DMat4::from_cols( + x_axis.extend(0.0), + normal.cross(x_axis).extend(0.0), + normal.extend(0.0), + base.extend(1.0), + ); let mut moved = profile.clone(); crate::scene::view::dispatch::apply_transform(&mut moved, &crate::command::EntityTransform::Affine(transform)); - let (sweep_entity, sweep_entity_transform) = embedded_sweep_profile(&moved)?; + let (sweep_entity, _) = embedded_sweep_profile(&moved)?; record.sweep_entity = Some(sweep_entity); - record.sweep_entity_transform = sweep_entity_transform; - record.flags_294_296 = [false, true, true]; + record.sweep_entity_transform = source_frame.to_cols_array(); + record.path_entity_transform = (map * source_frame).to_cols_array(); + // Group 294 marks a profile only moved to the path, not turned onto it. + record.flags_294_296 = [record.align_option != 1, true, true]; + // The reference records its default mitred joint. + record.miter_option = 2; record.reference_point = Vector3::new(0.0, 0.0, 0.0); Some(record) } @@ -135,7 +174,7 @@ pub fn swept_surface_entity(record: &SolidHistorySweep) -> EntityType { sweep_entity_transform: record.sweep_entity_transform, path_entity_transform: record.path_entity_transform, sweep_alignment_flags: record.align_option as i16, - align_start: record.has_align_start, + align_start: record.align_start, bank: record.bank, base_point_set: true, reference_vector: record.reference_point,