diff --git a/Cargo.lock b/Cargo.lock index 51294fb85..aed64f835 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4206,7 +4206,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.5.5" -source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=d96e3fa#d96e3fa2fe5acbeac966f1db4c01142618bf9c79" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=8d23408#8d2340876caef34f68436f62e2c9402be0aabb28" dependencies = [ "ahash 0.8.12", "anyhow", @@ -4229,7 +4229,7 @@ dependencies = [ [[package]] name = "opencadgraph" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=10d0858#10d0858a7252f85df07be4dfa00946658b6979e6" +source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=9b5ae39#9b5ae39ea35567e67faf8bc8d20b40e2bbab1177" dependencies = [ "opencadkernel", "serde_json", @@ -4238,7 +4238,7 @@ dependencies = [ [[package]] name = "opencadkernel" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=9cdd36d#9cdd36dc527216ba7929d8cfcdc1b7712a2d4cb8" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=d22a270#d22a270a0af79db9f10428a2d941152f6bb28158" dependencies = [ "cavalier_contours", "opencadcodec", @@ -4249,7 +4249,7 @@ dependencies = [ [[package]] name = "opencadkernel-constraints" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=9cdd36d#9cdd36dc527216ba7929d8cfcdc1b7712a2d4cb8" +source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=d22a270#d22a270a0af79db9f10428a2d941152f6bb28158" dependencies = [ "nalgebra", ] diff --git a/Cargo.toml b/Cargo.toml index 85cc56ce7..bc0264dda 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 = "d96e3fa", features = ["serde"] } -kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "9cdd36d", features = ["acis", "offset"] } -kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "9cdd36d" } -graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "10d0858" } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8d23408", features = ["serde"] } +kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "d22a270", features = ["acis", "offset"] } +kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "d22a270" } +graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "9b5ae39" } 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 8ac76adc1..1edabd809 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 = "d96e3fa", optional = true, features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8d23408", 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 = "d96e3fa", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8d23408", features = ["serde"] } [dev-dependencies] serde_json = "1" diff --git a/crates/ocs_plugin_api/build.rs b/crates/ocs_plugin_api/build.rs index 382754826..dda5ba997 100644 --- a/crates/ocs_plugin_api/build.rs +++ b/crates/ocs_plugin_api/build.rs @@ -162,6 +162,10 @@ fn generate_type_registry(out_dir: &Path) { "AssocConstraintNodeData", trace_simple::, ), + ( + "AssocCurveValue", + trace_simple::, + ), ( "AssocEvalValue", trace_simple::, diff --git a/crates/ocs_web_worker/Cargo.toml b/crates/ocs_web_worker/Cargo.toml index 91ed6035f..7635cc5d7 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 = "d96e3fa", features = ["serde"] } +codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "8d23408", 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 ac619efe5..b03373f59 100644 --- a/plugins/opencad-python/Cargo.lock +++ b/plugins/opencad-python/Cargo.lock @@ -1265,7 +1265,7 @@ dependencies = [ [[package]] name = "opencadcodec" version = "0.5.5" -source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=d96e3fa#d96e3fa2fe5acbeac966f1db4c01142618bf9c79" +source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=8d23408#8d2340876caef34f68436f62e2c9402be0aabb28" dependencies = [ "ahash 0.8.12", "anyhow", diff --git a/src/entities/object_data.rs b/src/entities/object_data.rs index 1568dc78e..b0056a369 100644 --- a/src/entities/object_data.rs +++ b/src/entities/object_data.rs @@ -228,6 +228,8 @@ fn embedded_name(value: Option<&codec::entities::EmbeddedEntity>) -> &'static st Some(codec::entities::EmbeddedEntity::Ellipse(_)) => "Ellipse", Some(codec::entities::EmbeddedEntity::Spline(_)) => "Spline", Some(codec::entities::EmbeddedEntity::LwPolyline(_)) => "Polyline", + // A 2D / 3D polyline profile the modeler keeps as a wire body. + Some(codec::entities::EmbeddedEntity::Body { .. }) => "Polyline", Some(codec::entities::EmbeddedEntity::Region(_)) => "Region", Some(codec::entities::EmbeddedEntity::Ray(_)) => "Ray", Some(codec::entities::EmbeddedEntity::XLine(_)) => "XLine", diff --git a/src/entities/solid3d.rs b/src/entities/solid3d.rs index b8721f0fe..e2e7c3add 100644 --- a/src/entities/solid3d.rs +++ b/src/entities/solid3d.rs @@ -447,6 +447,8 @@ fn embedded_name(entity: Option<&codec::entities::EmbeddedEntity>) -> &'static s Some(codec::entities::EmbeddedEntity::Ellipse(_)) => "Ellipse", Some(codec::entities::EmbeddedEntity::Spline(_)) => "Spline", Some(codec::entities::EmbeddedEntity::LwPolyline(_)) => "Polyline", + // A 2D / 3D polyline profile the modeler keeps as a wire body. + Some(codec::entities::EmbeddedEntity::Body { .. }) => "Polyline", Some(codec::entities::EmbeddedEntity::Region(_)) => "Region", Some(codec::entities::EmbeddedEntity::Ray(_)) => "Ray", Some(codec::entities::EmbeddedEntity::XLine(_)) => "XLine", diff --git a/src/modules/model/primitive_cmd.rs b/src/modules/model/primitive_cmd.rs index 016640de0..cca272072 100644 --- a/src/modules/model/primitive_cmd.rs +++ b/src/modules/model/primitive_cmd.rs @@ -1894,7 +1894,7 @@ impl PrimitiveCommand { Some(( placed, solid_history::box_op( - self.history_transform_axes(origin, x_axis, y_axis), + self.history_transform_axes(box_centre(origin, x_axis, y_axis, length, width, height), x_axis, y_axis), length, width, height, @@ -2024,7 +2024,7 @@ impl PrimitiveCommand { Some(( placed, solid_history::wedge_op( - self.history_transform_axes(origin, x_axis, y_axis), + self.history_transform_axes(box_centre(origin, x_axis, y_axis, length, width, height), x_axis, y_axis), length, width, height, @@ -3714,3 +3714,9 @@ fn wire(name: &str, points: Vec<[f32; 3]>) -> WireModel { ..Default::default() } } + +/// Box and wedge histories are framed at the centre of their bounding box, +/// as the reference stores them (lengths stay positive). +fn box_centre(origin: DVec3, x_axis: DVec3, y_axis: DVec3, length: f64, width: f64, height: f64) -> DVec3 { + origin + x_axis * (length * 0.5) + y_axis * (width * 0.5) + DVec3::Z * (height * 0.5) +} diff --git a/src/scene/convert/acis_kernel.rs b/src/scene/convert/acis_kernel.rs index d4ede7699..d1affae13 100644 --- a/src/scene/convert/acis_kernel.rs +++ b/src/scene/convert/acis_kernel.rs @@ -31,23 +31,14 @@ pub fn tessellate_sat( if bodies.is_empty() { return None; } + // The kernel's lift already applies each body's transform record; its + // scale still sets the tessellation tolerance. let mut placed_bodies = Vec::with_capacity(bodies.len()); for body in bodies { let source = body.provenance.source()?; let transform = body_transform(document, source.index() as usize).ok()?; let placement_scale = transform.map_or(1.0, |(_, _, scale)| scale.abs()); - let placed = if let Some((matrix, translation, scale)) = transform { - let placement = brep::Placement { - x_axis: [scale * matrix[0], scale * matrix[1], scale * matrix[2]], - y_axis: [scale * matrix[3], scale * matrix[4], scale * matrix[5]], - z_axis: [scale * matrix[6], scale * matrix[7], scale * matrix[8]], - origin: translation, - }; - brep::transform(&body, &placement)? - } else { - body - }; - placed_bodies.push((placed, placement_scale)); + placed_bodies.push((body, placement_scale)); } let bodies = placed_bodies; let resolution = if facet_res.is_finite() && facet_res > 0.0 { diff --git a/src/scene/convert/solid3d_tess.rs b/src/scene/convert/solid3d_tess.rs index 987b920a7..1bda680d5 100644 --- a/src/scene/convert/solid3d_tess.rs +++ b/src/scene/convert/solid3d_tess.rs @@ -1,6 +1,6 @@ use codec::entities::acis::{SabReader, SatBody, SatDocument}; use codec::entities::{Body, Region, Solid3D, Surface}; -use kernel::brep::{self, Body as KernelBody}; +use kernel::brep::Body as KernelBody; use crate::scene::model::mesh_model::{MeshLodSet, MeshModel}; @@ -179,21 +179,8 @@ pub(crate) fn kernel_acis_body(acis: &codec::entities::AcisData) -> Option Option match operation { SolidHistoryOperation::Box(value) | SolidHistoryOperation::Wedge(value) => world_point( value.base.transform, - [value.length * 0.5, value.width * 0.5, 0.0], + [0.0, 0.0, -value.height * 0.5], ), SolidHistoryOperation::Sphere(value) => { world_point(value.base.transform, [0.0, 0.0, 0.0]) @@ -1615,9 +1615,10 @@ pub fn primitive_properties( ) { return Vec::new(); } - let Some(operation) = primitive_property_operation(document, handle) else { + let Some(mut operation) = primitive_property_operation(document, handle) else { return brep_properties(document, handle); }; + corner_frame(&mut operation); match &operation { SolidHistoryOperation::Box(value) => { rectangular_properties(document, handle, value, "Box") @@ -2414,6 +2415,17 @@ pub fn apply_primitive_property( operation: &mut SolidHistoryOperation, field: &str, value: &str, +) -> bool { + corner_frame(operation); + let applied = apply_primitive_property_at_corner(operation, field, value); + centre_frame(operation); + applied +} + +fn apply_primitive_property_at_corner( + operation: &mut SolidHistoryOperation, + field: &str, + value: &str, ) -> bool { if let SolidHistoryOperation::Brep(brep_value) = operation { if let Some(applied) = apply_brep_position_property(brep_value, field, value) { @@ -2757,7 +2769,7 @@ fn embedded_entity(value: &EmbeddedEntity) -> Option { EmbeddedEntity::Region(value) => EntityType::Region(value.clone()), EmbeddedEntity::Ray(value) => EntityType::Ray(value.clone()), EmbeddedEntity::XLine(value) => EntityType::XLine(value.clone()), - EmbeddedEntity::Unknown { .. } => return None, + EmbeddedEntity::Body { .. } | EmbeddedEntity::Unknown { .. } => return None, }) } @@ -3460,6 +3472,9 @@ pub fn primitive_grips( let Some(operation) = document.solid_history_operation(handle) else { return Vec::new(); }; + let mut operation = operation.clone(); + corner_frame(&mut operation); + let operation = &operation; if let SolidHistoryOperation::Fillet(value) = operation { let Some(radius) = value.radii.first().copied() else { return Vec::new(); @@ -3743,6 +3758,17 @@ pub fn apply_primitive_grip( operation: &mut SolidHistoryOperation, grip_id: usize, apply: GripApply, +) -> bool { + corner_frame(operation); + let applied = apply_primitive_grip_at_corner(operation, grip_id, apply); + centre_frame(operation); + applied +} + +fn apply_primitive_grip_at_corner( + operation: &mut SolidHistoryOperation, + grip_id: usize, + apply: GripApply, ) -> bool { if let SolidHistoryOperation::Revolve(value) = operation { if let Some(index) = grip_id.checked_sub(GRIP_REVOLVE_PROFILE_FIRST) { @@ -4138,3 +4164,23 @@ pub fn chamfer_op( ..SolidHistoryChamfer::default() }) } + +/// Box and wedge histories are framed at the centre of their bounding box, +/// as the reference stores them; the editing code here works in the frame at +/// the base corner. These move a box or wedge between the two frames. +fn corner_frame(operation: &mut SolidHistoryOperation) { + shift_rectangular_frame(operation, -0.5); +} + +fn centre_frame(operation: &mut SolidHistoryOperation) { + shift_rectangular_frame(operation, 0.5); +} + +fn shift_rectangular_frame(operation: &mut SolidHistoryOperation, factor: f64) { + if let SolidHistoryOperation::Box(value) | SolidHistoryOperation::Wedge(value) = operation { + if let Some(current) = matrix(value.base.transform) { + let half = glam::DVec3::new(value.length, value.width, value.height) * factor; + value.base.transform = (current * glam::DMat4::from_translation(half)).to_cols_array(); + } + } +}