From e2472c68b8a4dd3d9b5c9a07ce65779b2edab8ca Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Wed, 2 Sep 2026 15:58:36 +0300 Subject: [PATCH] Restore sweep construction grips --- src/app/command_driver.rs | 17 ++- src/scene/model/solid_history.rs | 187 ++++++++++++++++++++++++++----- src/scene/model/sweep_model.rs | 24 ++++ 3 files changed, 194 insertions(+), 34 deletions(-) diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index 150fd1381..d7b16e2f1 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -3623,13 +3623,20 @@ impl OpenCADStudio { .get_entity(profile_handle) .cloned(); let path_ent = self.tabs[i].scene.document.get_entity(path_handle).cloned(); - let result = profile_ent - .zip(path_ent) - .and_then(|(profile, path)| sweep_model::swept(&profile, &path)); + let result = profile_ent.zip(path_ent).and_then(|(profile, path)| { + let reference_point = sweep_model::profile_of(&profile)?.plane.origin; + let solid = sweep_model::swept(&profile, &path)?; + let history = sweep_model::sweep_history( + &profile, + &path, + reference_point, + ) + .unwrap_or_else(|| solid_history::brep_op(&solid)); + Some((solid, history)) + }); - if let Some(solid) = result { + if let Some((solid, history)) = result { let pending = self.begin_undo(i, "SWEEP", 1, true); - let history = solid_history::brep_op(&solid); let created = self.add_solid_model(empty_solid3d(), solid, history); if !created.is_null() { self.tabs[i].dirty = true; diff --git a/src/scene/model/solid_history.rs b/src/scene/model/solid_history.rs index 3542bf6dd..ab07530c6 100644 --- a/src/scene/model/solid_history.rs +++ b/src/scene/model/solid_history.rs @@ -1,12 +1,14 @@ -use acadrust::entities::Solid3D; +use acadrust::entities::{EmbeddedEntity, Solid3D}; use acadrust::objects::{ DynamicBlockData, ObjectType, SolidHistoryBox, SolidHistoryBrep, SolidHistoryCone, SolidHistoryCylinder, SolidHistoryNodeBase, SolidHistoryOperation, SolidHistoryPyramid, SolidHistorySphere, SolidHistorySweep, SolidHistoryTorus, }; +use acadrust::EntityType; use cadkernel::brep::Body; use crate::command::EntityTransform; +use crate::entities::traits::EntityTypeOps; use crate::scene::model::object::{ GripApply, GripDef, GripShape, PropSection, PropValue, Property, }; @@ -22,7 +24,8 @@ pub const GRIP_SIDES: usize = 10_007; pub const GRIP_MAJOR_RADIUS: usize = 10_008; pub const GRIP_MINOR_RADIUS: usize = 10_009; pub const GRIP_TOP_RADIUS: usize = 10_010; -pub const GRIP_POSITION: usize = 10_011; +pub const GRIP_SWEEP_PROFILE_FIRST: usize = 20_000; +pub const GRIP_SWEEP_PATH_FIRST: usize = 30_000; pub const GRIP_BOX_CORNER_FIRST: usize = 10_100; pub const GRIP_BOX_FACE_X_MIN: usize = 10_110; pub const GRIP_BOX_FACE_X_MAX: usize = 10_111; @@ -1641,6 +1644,122 @@ fn local_point(transform: [f64; 16], point: glam::DVec3) -> Option Some(matrix(transform)?.inverse().transform_point3(point)) } +fn embedded_entity(value: &EmbeddedEntity) -> Option { + Some(match value { + EmbeddedEntity::Point(value) => EntityType::Point(value.clone()), + EmbeddedEntity::Line(value) => EntityType::Line(value.clone()), + EmbeddedEntity::Arc(value) => EntityType::Arc(value.clone()), + EmbeddedEntity::Circle(value) => EntityType::Circle(value.clone()), + EmbeddedEntity::Ellipse(value) => EntityType::Ellipse(value.clone()), + EmbeddedEntity::Spline(value) => EntityType::Spline(value.clone()), + EmbeddedEntity::LwPolyline(value) => EntityType::LwPolyline(value.clone()), + EmbeddedEntity::Ray(value) => EntityType::Ray(value.clone()), + EmbeddedEntity::XLine(value) => EntityType::XLine(value.clone()), + EmbeddedEntity::Unknown { .. } => return None, + }) +} + +fn into_embedded_entity(value: EntityType) -> Option { + Some(match value { + EntityType::Point(value) => EmbeddedEntity::Point(value), + EntityType::Line(value) => EmbeddedEntity::Line(value), + EntityType::Arc(value) => EmbeddedEntity::Arc(value), + EntityType::Circle(value) => EmbeddedEntity::Circle(value), + EntityType::Ellipse(value) => EmbeddedEntity::Ellipse(value), + EntityType::Spline(value) => EmbeddedEntity::Spline(value), + EntityType::LwPolyline(value) => EmbeddedEntity::LwPolyline(value), + EntityType::Ray(value) => EmbeddedEntity::Ray(value), + EntityType::XLine(value) => EmbeddedEntity::XLine(value), + _ => return None, + }) +} + +fn embedded_grips( + value: Option<&EmbeddedEntity>, + base_transform: [f64; 16], + entity_transform: [f64; 16], + first_id: usize, +) -> Vec { + let Some(entity) = value.and_then(embedded_entity) else { + return Vec::new(); + }; + let (Some(base), Some(entity_placement)) = + (matrix(base_transform), matrix(entity_transform)) + else { + return Vec::new(); + }; + let transform = base * entity_placement; + entity + .grips() + .into_iter() + .enumerate() + .filter_map(|(index, grip)| { + let world = transform.transform_point3(grip.world); + if !world.is_finite() { + return None; + } + let transform_vector = |vector: glam::DVec3| { + let vector = transform.transform_vector3(vector); + (vector.length_squared() > 1e-12).then(|| vector.normalize()) + }; + Some(GripDef { + id: first_id + index, + world, + is_midpoint: grip.is_midpoint, + shape: grip.shape, + dir: grip.dir.and_then(transform_vector), + axis: grip.axis.and_then(transform_vector), + }) + }) + .collect() +} + +fn apply_embedded_grip( + value: &mut Option, + base_transform: [f64; 16], + entity_transform: [f64; 16], + index: usize, + apply: GripApply, +) -> bool { + let Some(mut entity) = value.as_ref().and_then(embedded_entity) else { + return false; + }; + let Some(source_grip) = entity.grips().get(index).cloned() else { + return false; + }; + let (Some(base), Some(entity_placement)) = + (matrix(base_transform), matrix(entity_transform)) + else { + return false; + }; + let inverse = (base * entity_placement).inverse(); + if !inverse.is_finite() { + return false; + } + let apply = match apply { + GripApply::Absolute(world) => { + let local = inverse.transform_point3(world); + if !local.is_finite() { + return false; + } + GripApply::Absolute(local) + } + GripApply::Translate(world_delta) => { + let local_delta = inverse.transform_vector3(world_delta); + if !local_delta.is_finite() || local_delta.length_squared() <= 1e-24 { + return false; + } + GripApply::Translate(local_delta) + } + }; + entity.apply_grip(source_grip.id, apply); + let Some(entity) = into_embedded_entity(entity) else { + return false; + }; + *value = Some(entity); + true +} + fn grip( id: usize, world: glam::DVec3, @@ -1862,17 +1981,20 @@ pub fn primitive_grips( None, ); } - SolidHistoryOperation::Sweep(value) => add( - GRIP_POSITION, - value.base.transform, - [ - value.reference_point.x, - value.reference_point.y, - value.reference_point.z, - ], - GripShape::Square, - None, - ), + SolidHistoryOperation::Sweep(value) => { + grips.extend(embedded_grips( + value.sweep_entity.as_ref(), + value.base.transform, + value.sweep_entity_transform, + GRIP_SWEEP_PROFILE_FIRST, + )); + grips.extend(embedded_grips( + value.path_entity.as_ref(), + value.base.transform, + value.path_entity_transform, + GRIP_SWEEP_PATH_FIRST, + )); + } _ => {} } grips @@ -1883,6 +2005,29 @@ pub fn apply_primitive_grip( grip_id: usize, apply: GripApply, ) -> bool { + if let SolidHistoryOperation::Sweep(value) = operation { + if let Some(index) = grip_id.checked_sub(GRIP_SWEEP_PATH_FIRST) { + return apply_embedded_grip( + &mut value.path_entity, + value.base.transform, + value.path_entity_transform, + index, + apply, + ); + } + if let Some(index) = grip_id + .checked_sub(GRIP_SWEEP_PROFILE_FIRST) + .filter(|_| grip_id < GRIP_SWEEP_PATH_FIRST) + { + return apply_embedded_grip( + &mut value.sweep_entity, + value.base.transform, + value.sweep_entity_transform, + index, + apply, + ); + } + } let GripApply::Absolute(world) = apply else { return false; }; @@ -2016,22 +2161,6 @@ pub fn apply_primitive_grip( } _ => return false, }, - SolidHistoryOperation::Sweep(value) if grip_id == GRIP_POSITION => { - let Some(current) = matrix(value.base.transform) else { - return false; - }; - let reference = current.transform_point3(glam::DVec3::new( - value.reference_point.x, - value.reference_point.y, - value.reference_point.z, - )); - let delta = world - reference; - if !delta.is_finite() || delta.length_squared() <= 1e-24 { - return false; - } - value.base.transform = - (glam::DMat4::from_translation(delta) * current).to_cols_array(); - } _ => return false, } true diff --git a/src/scene/model/sweep_model.rs b/src/scene/model/sweep_model.rs index 247a8e5d1..95e51fc1d 100644 --- a/src/scene/model/sweep_model.rs +++ b/src/scene/model/sweep_model.rs @@ -186,6 +186,30 @@ fn embedded_path(entity: &EntityType) -> Option { } } +pub fn sweep_history( + profile: &EntityType, + path: &EntityType, + reference_point: [f64; 3], +) -> Option { + let mut base = SolidHistoryNodeBase::new(1); + base.transform = glam::DMat4::IDENTITY.to_cols_array(); + Some(SolidHistoryOperation::Sweep(SolidHistorySweep { + base, + operation_major: 1, + sweep_entity: Some(embedded_path(profile)?), + path_entity: Some(embedded_path(path)?), + scale_factor: 1.0, + sweep_entity_transform: glam::DMat4::IDENTITY.to_cols_array(), + path_entity_transform: glam::DMat4::IDENTITY.to_cols_array(), + reference_point: Vector3::new( + reference_point[0], + reference_point[1], + reference_point[2], + ), + ..SolidHistorySweep::default() + })) +} + /// Builds a history-backed wall solid along a supported planar curve. pub fn polysolid( entity: &EntityType,