diff --git a/Cargo.lock b/Cargo.lock index 7d7c355af..0879a4843 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=76d2a1c#76d2a1c66702d9aaf3ef8d4ff7dc06f4a025da82" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=bff95f5#bff95f5115dd9cc0c4983ba890c32f890948dfed" dependencies = [ "anyhow", "bitflags 2.13.1", @@ -4228,7 +4228,7 @@ dependencies = [ [[package]] name = "opencadgraph" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=f8bbf40#f8bbf40282bce73e7a24ef4f242adf7617d6608e" +source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=9635ba7#9635ba7cdbf3b607548b1b2c30e2c10e2e2684d1" dependencies = [ "opencadkernel", "serde_json", @@ -4237,7 +4237,7 @@ dependencies = [ [[package]] name = "opencadkernel" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=e13f37f#e13f37f88f4a24084aefceccca74a65500fec0ac" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=6146d31#6146d31e7ea6f240538af760b91c8640054d7466" dependencies = [ "cavalier_contours", "opencadcodec", @@ -4248,7 +4248,7 @@ dependencies = [ [[package]] name = "opencadkernel-constraints" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=e13f37f#e13f37f88f4a24084aefceccca74a65500fec0ac" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=6146d31#6146d31e7ea6f240538af760b91c8640054d7466" dependencies = [ "nalgebra", ] diff --git a/Cargo.toml b/Cargo.toml index bbab9bf40..013ce24a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,10 +34,10 @@ rfd = "0.17" clap = { version = "4", features = ["derive", "string"] } env_logger = "0.11" log = "0.4" -codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "76d2a1c", features = ["serde"] } -kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "e13f37f", features = ["acis", "offset"] } -kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "e13f37f" } -graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "f8bbf40" } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "bff95f5", features = ["serde"] } +kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "6146d31", features = ["acis", "offset"] } +kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "6146d31" } +graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "9635ba7" } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } flate2 = "1" image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] } diff --git a/crates/ocs_plugin_api/Cargo.toml b/crates/ocs_plugin_api/Cargo.toml index 58e17f2b9..2c35b554f 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 = "76d2a1c", optional = true, features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "bff95f5", 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 = "76d2a1c", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "bff95f5", features = ["serde"] } [dev-dependencies] serde_json = "1" diff --git a/crates/ocs_web_worker/Cargo.toml b/crates/ocs_web_worker/Cargo.toml index 3da241eb1..75496a65c 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 = "76d2a1c", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "bff95f5", features = ["serde"] } bincode = "1.3" serde = { version = "1", features = ["derive"] } console_error_panic_hook = "0.1" diff --git a/plugins/opencad-python/Cargo.lock b/plugins/opencad-python/Cargo.lock index 007c79458..d731bf763 100644 --- a/plugins/opencad-python/Cargo.lock +++ b/plugins/opencad-python/Cargo.lock @@ -1264,7 +1264,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.6.0" -source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=76d2a1c#76d2a1c66702d9aaf3ef8d4ff7dc06f4a025da82" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=bff95f5#bff95f5115dd9cc0c4983ba890c32f890948dfed" dependencies = [ "anyhow", "bitflags", 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,