forked from 1b-agent/opencadstudio-upstream
Complete SWEEP command options and kernel integration
This commit is contained in:
1 parent
44c94d1da1
commit
ad47b17603
13 files changed
+709
-116
No files matched your search
@@ -11,5 +11,6 @@ pub mod mesh_model;
|
||||
pub mod solid_model;
|
||||
pub mod solid_history;
|
||||
pub mod sweep_model;
|
||||
mod sweep_command_model;
|
||||
pub mod visual_style_model;
|
||||
pub mod object;
|
||||
@@ -212,7 +212,9 @@ pub fn reference_point(operation: &SolidHistoryOperation) -> Option<glam::DVec3>
|
||||
}
|
||||
SolidHistoryOperation::Cone(value) => world_point(value.base.transform, [0.0; 3]),
|
||||
SolidHistoryOperation::Cylinder(value) => world_point(value.base.transform, [0.0; 3]),
|
||||
SolidHistoryOperation::Sweep(value) | SolidHistoryOperation::Extrusion(value) => world_point(
|
||||
SolidHistoryOperation::Sweep(value) => cadkernel::acis::sweep_history_reference_point(value)
|
||||
.ok().map(glam::DVec3::from_array),
|
||||
SolidHistoryOperation::Extrusion(value) => world_point(
|
||||
value.base.transform,
|
||||
[
|
||||
value.reference_point.x,
|
||||
@@ -444,7 +446,7 @@ fn sweep_properties(
|
||||
let (show_history, show_history_editable) =
|
||||
displayed_history_state(object_show_history, show_history_mode);
|
||||
let show_value = if show_history { "Yes" } else { "No" };
|
||||
let length = sweep_path_length(value)
|
||||
let length = cadkernel::acis::sweep_history_path_length(value).ok()
|
||||
.map(crate::entities::common::format_length)
|
||||
.unwrap_or_default();
|
||||
vec![
|
||||
@@ -497,7 +499,7 @@ fn sweep_properties(
|
||||
field: PROP_HISTORY,
|
||||
value: PropValue::Choice {
|
||||
selected: if record_history { "Record" } else { "None" }.to_string(),
|
||||
options: vec!["None".to_string(), "Record".to_string()],
|
||||
options: vec!["Record".to_string(), "None".to_string()],
|
||||
},
|
||||
},
|
||||
Property {
|
||||
@@ -506,7 +508,7 @@ fn sweep_properties(
|
||||
value: if show_history_editable {
|
||||
PropValue::Choice {
|
||||
selected: show_value.to_string(),
|
||||
options: vec!["No".to_string(), "Yes".to_string()],
|
||||
options: vec!["Yes".to_string(), "No".to_string()],
|
||||
}
|
||||
} else {
|
||||
PropValue::ReadOnly(show_value.to_string())
|
||||
@@ -2179,6 +2181,7 @@ fn embedded_entity(value: &EmbeddedEntity) -> Option<EntityType> {
|
||||
EmbeddedEntity::Ellipse(value) => EntityType::Ellipse(value.clone()),
|
||||
EmbeddedEntity::Spline(value) => EntityType::Spline(value.clone()),
|
||||
EmbeddedEntity::LwPolyline(value) => EntityType::LwPolyline(value.clone()),
|
||||
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,
|
||||
@@ -2194,6 +2197,7 @@ fn into_embedded_entity(value: EntityType) -> Option<EmbeddedEntity> {
|
||||
EntityType::Ellipse(value) => EmbeddedEntity::Ellipse(value),
|
||||
EntityType::Spline(value) => EmbeddedEntity::Spline(value),
|
||||
EntityType::LwPolyline(value) => EmbeddedEntity::LwPolyline(value),
|
||||
EntityType::Region(value) => EmbeddedEntity::Region(value),
|
||||
EntityType::Ray(value) => EmbeddedEntity::Ray(value),
|
||||
EntityType::XLine(value) => EmbeddedEntity::XLine(value),
|
||||
_ => return None,
|
||||
@@ -2288,21 +2292,27 @@ fn apply_extrusion_profile_grip(
|
||||
true
|
||||
}
|
||||
|
||||
fn sweep_placement_matrices(value: &SolidHistorySweep) -> Option<(glam::DMat4, glam::DMat4)> {
|
||||
let (profile, path) = cadkernel::acis::sweep_history_placements(value).ok()?;
|
||||
let matrix = |place: cadkernel::brep::Placement| {
|
||||
glam::DMat4::from_cols(
|
||||
glam::DVec3::from_array(place.x_axis).extend(0.0),
|
||||
glam::DVec3::from_array(place.y_axis).extend(0.0),
|
||||
glam::DVec3::from_array(place.z_axis).extend(0.0),
|
||||
glam::DVec3::from_array(place.origin).extend(1.0),
|
||||
)
|
||||
};
|
||||
Some((matrix(profile), matrix(path)))
|
||||
}
|
||||
|
||||
fn sweep_embedded_grips(
|
||||
value: Option<&EmbeddedEntity>,
|
||||
base_transform: [f64; 16],
|
||||
entity_transform: [f64; 16],
|
||||
transform: glam::DMat4,
|
||||
first_id: usize,
|
||||
) -> Vec<GripDef> {
|
||||
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()
|
||||
@@ -2330,8 +2340,7 @@ fn sweep_embedded_grips(
|
||||
|
||||
fn apply_sweep_embedded_grip(
|
||||
value: &mut Option<EmbeddedEntity>,
|
||||
base_transform: [f64; 16],
|
||||
entity_transform: [f64; 16],
|
||||
transform: glam::DMat4,
|
||||
index: usize,
|
||||
apply: GripApply,
|
||||
) -> bool {
|
||||
@@ -2341,12 +2350,7 @@ fn apply_sweep_embedded_grip(
|
||||
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();
|
||||
let inverse = transform.inverse();
|
||||
if !inverse.is_finite() {
|
||||
return false;
|
||||
}
|
||||
@@ -2891,18 +2895,14 @@ pub fn primitive_grips(
|
||||
);
|
||||
}
|
||||
SolidHistoryOperation::Sweep(value) => {
|
||||
grips.extend(sweep_embedded_grips(
|
||||
value.sweep_entity.as_ref(),
|
||||
value.base.transform,
|
||||
value.sweep_entity_transform,
|
||||
GRIP_SWEEP_PROFILE_FIRST,
|
||||
));
|
||||
grips.extend(sweep_embedded_grips(
|
||||
value.path_entity.as_ref(),
|
||||
value.base.transform,
|
||||
value.path_entity_transform,
|
||||
GRIP_SWEEP_PATH_FIRST,
|
||||
));
|
||||
if let Some((profile, path)) = sweep_placement_matrices(value) {
|
||||
grips.extend(sweep_embedded_grips(
|
||||
value.sweep_entity.as_ref(), profile, GRIP_SWEEP_PROFILE_FIRST,
|
||||
));
|
||||
grips.extend(sweep_embedded_grips(
|
||||
value.path_entity.as_ref(), path, GRIP_SWEEP_PATH_FIRST,
|
||||
));
|
||||
}
|
||||
}
|
||||
SolidHistoryOperation::Loft(value) => grips.extend(loft_section_grips(value)),
|
||||
SolidHistoryOperation::Revolve(value) => {
|
||||
@@ -2962,11 +2962,13 @@ pub fn apply_primitive_grip(
|
||||
}
|
||||
}
|
||||
if let SolidHistoryOperation::Sweep(value) = operation {
|
||||
let Some((profile, path)) = sweep_placement_matrices(value) else {
|
||||
return false;
|
||||
};
|
||||
if let Some(index) = grip_id.checked_sub(GRIP_SWEEP_PATH_FIRST) {
|
||||
return apply_sweep_embedded_grip(
|
||||
&mut value.path_entity,
|
||||
value.base.transform,
|
||||
value.path_entity_transform,
|
||||
path,
|
||||
index,
|
||||
apply,
|
||||
);
|
||||
@@ -2977,8 +2979,7 @@ pub fn apply_primitive_grip(
|
||||
{
|
||||
return apply_sweep_embedded_grip(
|
||||
&mut value.sweep_entity,
|
||||
value.base.transform,
|
||||
value.sweep_entity_transform,
|
||||
profile,
|
||||
index,
|
||||
apply,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
//! SWEEP command data mapped to the shared kernel history reconstruction.
|
||||
|
||||
use acadrust::entities::{Surface, SurfaceData, SurfaceKind, SurfaceSweepOptions};
|
||||
use acadrust::objects::{SolidHistoryNodeBase, SolidHistorySweep};
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use cadkernel::brep::Body;
|
||||
|
||||
use crate::command::{ExtrudeMode, SweepOptions};
|
||||
use super::sweep_model::{embedded_path, embedded_revolve_profile};
|
||||
|
||||
fn embedded_sweep_profile(entity: &EntityType) -> Option<(acadrust::entities::EmbeddedEntity, [f64; 16])> {
|
||||
if let EntityType::Region(region) = entity {
|
||||
Some((acadrust::entities::EmbeddedEntity::Region(region.clone()), glam::DMat4::IDENTITY.to_cols_array()))
|
||||
} else {
|
||||
embedded_revolve_profile(entity)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_sweep_profile(entity: &EntityType) -> bool {
|
||||
embedded_sweep_profile(entity).is_some_and(|(profile, transform)| {
|
||||
cadkernel::acis::sweep_profile_geometry(&profile, transform).is_ok()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_sweep_path(entity: &EntityType) -> bool {
|
||||
match embedded_path(entity) {
|
||||
Some(acadrust::entities::EmbeddedEntity::Spline(value)) => {
|
||||
value.degree > 0 && (value.control_points.len() > value.degree as usize
|
||||
|| value.fit_points.len() >= 2)
|
||||
}
|
||||
Some(_) => crate::entities::curve::entity_curve(entity)
|
||||
.is_some_and(|curve| curve.curve.length().is_finite() && curve.curve.length() > 1e-9),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// All selected profiles use one base point, preserving their relative offsets.
|
||||
pub fn sweep_selection_options(profiles: &[EntityType], mut options: SweepOptions) -> Option<SweepOptions> {
|
||||
if options.base_point.is_some() {
|
||||
return Some(options);
|
||||
}
|
||||
let geometry = profiles.iter().map(|profile| {
|
||||
let (entity, transform) = embedded_sweep_profile(profile)?;
|
||||
let (plane, wires, _) = cadkernel::acis::sweep_profile_geometry(&entity, transform).ok()?;
|
||||
Some((plane, wires))
|
||||
}).collect::<Option<Vec<_>>>()?;
|
||||
options.base_point = Some(glam::DVec3::from_array(
|
||||
cadkernel::brep::sweep_profile_group_base(&geometry)?,
|
||||
));
|
||||
Some(options)
|
||||
}
|
||||
|
||||
pub fn sweep_record(profile: &EntityType, path: &EntityType, options: SweepOptions) -> Option<SolidHistorySweep> {
|
||||
let (sweep_entity, sweep_entity_transform) = embedded_sweep_profile(profile)?;
|
||||
let (plane, wires, _) = cadkernel::acis::sweep_profile_geometry(&sweep_entity, sweep_entity_transform).ok()?;
|
||||
let base_point = match options.base_point {
|
||||
Some(point) => point.to_array(),
|
||||
None => cadkernel::brep::sweep_profile_base(plane, &wires)?,
|
||||
};
|
||||
let mut base = SolidHistoryNodeBase::new(1);
|
||||
base.transform = glam::DMat4::IDENTITY.to_cols_array();
|
||||
Some(SolidHistorySweep {
|
||||
base,
|
||||
operation_major: 1,
|
||||
sweep_entity: Some(sweep_entity),
|
||||
path_entity: Some(embedded_path(path)?),
|
||||
scale_factor: options.scale,
|
||||
twist_angle: options.twist_angle,
|
||||
align_option: u8::from(options.align),
|
||||
has_align_start: true,
|
||||
bank: options.bank,
|
||||
sweep_entity_transform,
|
||||
path_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
||||
reference_point: Vector3::new(base_point[0], base_point[1], base_point[2]),
|
||||
..SolidHistorySweep::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn swept_with_options(profile: &EntityType, path: &EntityType, mode: ExtrudeMode, options: SweepOptions) -> Option<Body> {
|
||||
let record = sweep_record(profile, path, options)?;
|
||||
cadkernel::acis::rebuild_sweep_with_mode(&record, mode == ExtrudeMode::Surface).ok()
|
||||
}
|
||||
|
||||
/// Preserve native construction parameters alongside the sheet's saved B-rep.
|
||||
pub fn swept_surface_entity(record: &SolidHistorySweep) -> EntityType {
|
||||
let mut surface = Surface::new(SurfaceKind::Swept);
|
||||
if let Ok(point) = cadkernel::acis::sweep_history_reference_point(record) {
|
||||
surface.point_of_reference = Vector3::new(point[0], point[1], point[2]);
|
||||
}
|
||||
surface.surface_data = SurfaceData::Swept {
|
||||
class_version: 0,
|
||||
sweep_entity: record.sweep_entity.clone(),
|
||||
path_entity: record.path_entity.clone(),
|
||||
sweep_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
||||
path_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
||||
options: SurfaceSweepOptions {
|
||||
draft_angle: record.draft_angle,
|
||||
twist_angle: record.twist_angle,
|
||||
scale_factor: record.scale_factor,
|
||||
align_angle: record.align_angle,
|
||||
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,
|
||||
bank: record.bank,
|
||||
base_point_set: true,
|
||||
reference_vector: record.reference_point,
|
||||
..SurfaceSweepOptions::default()
|
||||
},
|
||||
};
|
||||
EntityType::Surface(surface)
|
||||
}
|
||||
@@ -12,6 +12,9 @@ use acadrust::types::{Vector2, Vector3};
|
||||
use acadrust::EntityType;
|
||||
|
||||
use crate::entities::curve::entity_curve;
|
||||
pub use super::sweep_command_model::{
|
||||
is_sweep_path, is_sweep_profile, sweep_record, sweep_selection_options, swept_surface_entity, swept_with_options,
|
||||
};
|
||||
|
||||
/// A drawn profile, as the kernel wants it: the plane it lies in and the
|
||||
/// chain of pieces closing a loop in that plane.
|
||||
@@ -591,18 +594,32 @@ pub fn embedded_path(entity: &EntityType) -> Option<EmbeddedEntity> {
|
||||
EntityType::Circle(value) => Some(EmbeddedEntity::Circle(value.clone())),
|
||||
EntityType::Ellipse(value) => Some(EmbeddedEntity::Ellipse(value.clone())),
|
||||
EntityType::LwPolyline(value) => Some(EmbeddedEntity::LwPolyline(value.clone())),
|
||||
EntityType::Polyline2D(value) => {
|
||||
let mut polyline = LwPolyline::new();
|
||||
polyline.elevation = value.elevation;
|
||||
polyline.normal = value.normal;
|
||||
polyline.is_closed = value.is_closed();
|
||||
polyline.vertices = value.vertices.iter().map(|vertex| {
|
||||
let mut converted = LwVertex::new(Vector2::new(vertex.location.x, vertex.location.y));
|
||||
converted.bulge = vertex.bulge;
|
||||
converted
|
||||
}).collect();
|
||||
Some(EmbeddedEntity::LwPolyline(polyline))
|
||||
}
|
||||
EntityType::Spline(value) => Some(EmbeddedEntity::Spline(value.clone())),
|
||||
EntityType::Polyline3D(value) if !value.is_closed() && value.vertices.len() >= 2 => {
|
||||
EntityType::Helix(value) => Some(EmbeddedEntity::Spline(value.spline.clone())),
|
||||
EntityType::Polyline3D(value) if value.vertices.len() >= 2 => {
|
||||
let mut points = value.vertices.iter().map(|vertex| vertex.position).collect::<Vec<_>>();
|
||||
if value.is_closed() && points.first() != points.last() {
|
||||
points.push(*points.first()?);
|
||||
}
|
||||
let mut spline = Spline::from_control_points(
|
||||
1,
|
||||
value
|
||||
.vertices
|
||||
.iter()
|
||||
.map(|vertex| vertex.position)
|
||||
.collect(),
|
||||
points,
|
||||
);
|
||||
spline.flags.linear = true;
|
||||
spline.flags.planar = false;
|
||||
spline.flags.closed = value.is_closed();
|
||||
Some(EmbeddedEntity::Spline(spline))
|
||||
}
|
||||
_ => None,
|
||||
|
||||
Reference in new issue
Block a user