forked from 1b-agent/opencadstudio-upstream
Restore loft section grip editing
This commit is contained in:
1 parent
bde9cc2715
commit
7fbde18151
5 files changed
+164
-7
No files matched your search
Generated
+1
-1
@@ -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
@@ -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"] }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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(§ions)
|
||||
}
|
||||
|
||||
/// 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,
|
||||
|
||||
Reference in new issue
Block a user