Restore loft section grip editing

This commit is contained in:
ramox81 committed 2026-09-02 16:26:14 +03:00
1 parent bde9cc2715
commit 7fbde18151
5 files changed
+164 -7

No files matched your search

Generated
+1 -1
View File
@@ -878,7 +878,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "cadkernel"
version = "0.1.0"
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=a809cb32cc6d90fd0e9c4199c3b4977cd56b7fa8#a809cb32cc6d90fd0e9c4199c3b4977cd56b7fa8"
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=258c69086683c93595cad16320fbc0f72354fa5a#258c69086683c93595cad16320fbc0f72354fa5a"
dependencies = [
"acadrust",
"cavalier_contours",
+1 -1
View File
@@ -28,7 +28,7 @@ rfd = "0.17"
clap = { version = "4", features = ["derive"] }
env_logger = "0.11"
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "a0f7d444f1607bc4b2c881060cbe7ea1014253cb", features = ["serde"] }
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "a809cb32cc6d90fd0e9c4199c3b4977cd56b7fa8", features = ["acis", "offset"] }
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "258c69086683c93595cad16320fbc0f72354fa5a", features = ["acis", "offset"] }
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
flate2 = "1"
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
+13 -2
View File
@@ -3666,9 +3666,20 @@ impl OpenCADStudio {
.iter()
.filter_map(|handle| self.tabs[i].scene.document.get_entity(*handle).cloned())
.collect();
if let Some(solid) = sweep_model::lofted(&profiles) {
let result = sweep_model::loft_history(&profiles)
.and_then(|history| {
cadkernel::acis::rebuild_body(&history)
.ok()
.map(|solid| (solid, history))
})
.or_else(|| {
sweep_model::lofted(&profiles).map(|solid| {
let history = solid_history::brep_op(&solid);
(solid, history)
})
});
if let Some((solid, history)) = result {
let pending = self.begin_undo(i, "LOFT", 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;
+128 -2
View File
@@ -1,12 +1,14 @@
use acadrust::entities::Solid3D;
use acadrust::entities::{EmbeddedEntity, Solid3D};
use acadrust::objects::{
DynamicBlockData, ObjectType, SolidHistoryBox, SolidHistoryBrep, SolidHistoryCone,
SolidHistoryCylinder, SolidHistoryNodeBase, SolidHistoryOperation,
SolidHistoryCylinder, SolidHistoryLoft, 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,
};
@@ -23,6 +25,7 @@ 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_LOFT_SECTION_FIRST: usize = 40_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,121 @@ fn local_point(transform: [f64; 16], point: glam::DVec3) -> Option<glam::DVec3>
Some(matrix(transform)?.inverse().transform_point3(point))
}
fn embedded_entity(value: &EmbeddedEntity) -> Option<EntityType> {
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<EmbeddedEntity> {
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 loft_section_grips(value: &SolidHistoryLoft) -> Vec<GripDef> {
if !value.guides.is_empty() {
return Vec::new();
}
let Some(transform) = matrix(value.base.transform) else {
return Vec::new();
};
let mut result = Vec::new();
for section in &value.cross_sections {
let Some(entity) = embedded_entity(section) else {
continue;
};
for source in entity.grips() {
let world = transform.transform_point3(source.world);
if !world.is_finite() {
continue;
}
let transform_vector = |vector: glam::DVec3| {
let vector = transform.transform_vector3(vector);
(vector.length_squared() > 1e-12).then(|| vector.normalize())
};
result.push(GripDef {
id: GRIP_LOFT_SECTION_FIRST + result.len(),
world,
is_midpoint: source.is_midpoint,
shape: source.shape,
dir: source.dir.and_then(transform_vector),
axis: source.axis.and_then(transform_vector),
});
}
}
result
}
fn apply_loft_section_grip(
value: &mut SolidHistoryLoft,
mut index: usize,
apply: GripApply,
) -> bool {
if !value.guides.is_empty() {
return false;
}
let Some(transform) = matrix(value.base.transform) else {
return false;
};
let inverse = transform.inverse();
if !inverse.is_finite() {
return false;
}
let local_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)
}
};
for section in &mut value.cross_sections {
let Some(mut entity) = embedded_entity(section) else {
continue;
};
let grips = entity.grips();
if index >= grips.len() {
index -= grips.len();
continue;
}
let source = grips[index].clone();
entity.apply_grip(source.id, local_apply);
let Some(entity) = into_embedded_entity(entity) else {
return false;
};
*section = entity;
return true;
}
false
}
fn grip(
id: usize,
world: glam::DVec3,
@@ -1873,6 +1991,9 @@ pub fn primitive_grips(
GripShape::Square,
None,
),
SolidHistoryOperation::Loft(value) => {
grips.extend(loft_section_grips(value));
}
_ => {}
}
grips
@@ -1883,6 +2004,11 @@ pub fn apply_primitive_grip(
grip_id: usize,
apply: GripApply,
) -> bool {
if let SolidHistoryOperation::Loft(value) = operation {
if let Some(index) = grip_id.checked_sub(GRIP_LOFT_SECTION_FIRST) {
return apply_loft_section_grip(value, index, apply);
}
}
let GripApply::Absolute(world) = apply else {
return false;
};
+21 -1
View File
@@ -5,7 +5,7 @@ use cadkernel::geom2d::{Arc, Curve, EllipseArc, Line};
use cadkernel::space::{PlanarCurve, Plane, Vec3};
use acadrust::entities::{EmbeddedEntity, LwPolyline, LwVertex};
use acadrust::objects::{
SolidHistoryNodeBase, SolidHistoryOperation, SolidHistorySweep,
SolidHistoryLoft, SolidHistoryNodeBase, SolidHistoryOperation, SolidHistorySweep,
};
use acadrust::types::{Vector2, Vector3};
use acadrust::EntityType;
@@ -167,6 +167,26 @@ pub fn lofted(profiles: &[EntityType]) -> Option<Body> {
brep::loft(&sections)
}
/// Stores the source sections required to rebuild and edit a loft.
pub fn loft_history(profiles: &[EntityType]) -> Option<SolidHistoryOperation> {
let cross_sections = profiles
.iter()
.map(embedded_path)
.collect::<Option<Vec<_>>>()?;
if cross_sections.len() < 2 {
return None;
}
let mut base = SolidHistoryNodeBase::new(1);
base.transform = glam::DMat4::IDENTITY.to_cols_array();
Some(SolidHistoryOperation::Loft(SolidHistoryLoft {
base,
operation_major: 1,
cross_sections,
guides: Vec::new(),
..SolidHistoryLoft::default()
}))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PolysolidJustification {
Left,