Complete revolve workflow and properties

This commit is contained in:
ramox81 committed 2026-09-03 13:40:23 +03:00
1 parent e6429d5712
commit 2e112f9aeb
9 files changed
+913 -94

No files matched your search

+210
View File
@@ -68,6 +68,13 @@ pub const PROP_SCALE_ALONG_PATH: &str = "solid_history_scale_along_path";
pub const PROP_SWEEP_LENGTH: &str = "solid_history_sweep_length";
pub const PROP_EXTRUSION_HEIGHT: &str = "solid_history_extrusion_height";
pub const PROP_TAPER_ANGLE: &str = "solid_history_taper_angle";
pub const PROP_REVOLVE_ANGLE: &str = "solid_history_revolve_angle";
pub const PROP_AXIS_POSITION_X: &str = "solid_history_axis_position_x";
pub const PROP_AXIS_POSITION_Y: &str = "solid_history_axis_position_y";
pub const PROP_AXIS_POSITION_Z: &str = "solid_history_axis_position_z";
pub const PROP_AXIS_DIRECTION_X: &str = "solid_history_axis_direction_x";
pub const PROP_AXIS_DIRECTION_Y: &str = "solid_history_axis_direction_y";
pub const PROP_AXIS_DIRECTION_Z: &str = "solid_history_axis_direction_z";
pub const PROP_PYRAMID_TYPE: &str = "solid_history_pyramid_type";
pub const PROP_HISTORY: &str = "solid_history_record";
pub const PROP_SHOW_HISTORY: &str = "solid_history_show";
@@ -122,6 +129,7 @@ pub fn has_specialized_primitive_properties(
| SolidHistoryOperation::Pyramid(_)
| SolidHistoryOperation::Sweep(_)
| SolidHistoryOperation::Extrusion(_)
| SolidHistoryOperation::Revolve(_)
)
)
}
@@ -147,10 +155,112 @@ pub fn reference_point(operation: &SolidHistoryOperation) -> Option<glam::DVec3>
),
SolidHistoryOperation::Torus(value) => world_point(value.base.transform, [0.0; 3]),
SolidHistoryOperation::Pyramid(value) => world_point(value.base.transform, [0.0; 3]),
SolidHistoryOperation::Revolve(value) => world_point(
value.base.transform,
[value.axis_point.x, value.axis_point.y, value.axis_point.z],
),
_ => None,
}
}
fn revolve_properties(
document: &acadrust::CadDocument,
handle: acadrust::Handle,
value: &SolidHistoryRevolve,
) -> Vec<PropSection> {
let Some(axis_position) = world_point(
value.base.transform,
[value.axis_point.x, value.axis_point.y, value.axis_point.z],
) else {
return Vec::new();
};
let Some(axis_direction) = world_vector(
value.base.transform,
[value.direction.x, value.direction.y, value.direction.z],
) else {
return Vec::new();
};
let (record_history, object_show_history, show_history_mode) =
history_flags(document, handle).unwrap_or((false, false, 1));
let (show_history, show_history_editable) =
displayed_history_state(object_show_history, show_history_mode);
let show_value = if show_history { "Yes" } else { "No" };
vec![
PropSection {
title: t!("Geometry").into_owned(),
props: vec![
Property {
label: t!("Solid type").into_owned(),
field: "solid_history_type",
value: PropValue::ReadOnly(t!("Revolve").into_owned()),
},
Property {
label: t!("Angle of revolution").into_owned(),
field: PROP_REVOLVE_ANGLE,
value: PropValue::EditText(crate::entities::common::format_angle(
value.revolve_angle,
)),
},
crate::entities::common::edit_prop(
t!("Axis position X").as_ref(),
PROP_AXIS_POSITION_X,
axis_position.x,
),
crate::entities::common::edit_prop(
t!("Axis position Y").as_ref(),
PROP_AXIS_POSITION_Y,
axis_position.y,
),
crate::entities::common::edit_prop(
t!("Axis position Z").as_ref(),
PROP_AXIS_POSITION_Z,
axis_position.z,
),
crate::entities::common::edit_scalar_prop(
t!("Axis direction X").as_ref(),
PROP_AXIS_DIRECTION_X,
axis_direction.x,
),
crate::entities::common::edit_scalar_prop(
t!("Axis direction Y").as_ref(),
PROP_AXIS_DIRECTION_Y,
axis_direction.y,
),
crate::entities::common::edit_scalar_prop(
t!("Axis direction Z").as_ref(),
PROP_AXIS_DIRECTION_Z,
axis_direction.z,
),
],
},
PropSection {
title: t!("Solid History").into_owned(),
props: vec![
Property {
label: t!("History").into_owned(),
field: PROP_HISTORY,
value: PropValue::Choice {
selected: if record_history { "Record" } else { "None" }.to_string(),
options: vec!["None".to_string(), "Record".to_string()],
},
},
Property {
label: t!("Show History").into_owned(),
field: PROP_SHOW_HISTORY,
value: if show_history_editable {
PropValue::Choice {
selected: show_value.to_string(),
options: vec!["No".to_string(), "Yes".to_string()],
}
} else {
PropValue::ReadOnly(show_value.to_string())
},
},
],
},
]
}
fn extrusion_properties(
document: &acadrust::CadDocument,
handle: acadrust::Handle,
@@ -1021,6 +1131,7 @@ pub fn primitive_properties(
SolidHistoryOperation::Extrusion(value) => {
extrusion_properties(document, handle, value)
}
SolidHistoryOperation::Revolve(value) => revolve_properties(document, handle, value),
_ => Vec::new(),
}
}
@@ -1053,6 +1164,13 @@ pub fn is_primitive_property(field: &str) -> bool {
| PROP_SCALE_ALONG_PATH
| PROP_EXTRUSION_HEIGHT
| PROP_TAPER_ANGLE
| PROP_REVOLVE_ANGLE
| PROP_AXIS_POSITION_X
| PROP_AXIS_POSITION_Y
| PROP_AXIS_POSITION_Z
| PROP_AXIS_DIRECTION_X
| PROP_AXIS_DIRECTION_Y
| PROP_AXIS_DIRECTION_Z
| PROP_PYRAMID_TYPE
)
}
@@ -1142,6 +1260,93 @@ fn apply_extrusion_geometry_property(
}
}
fn apply_revolve_geometry_property(
value: &mut SolidHistoryRevolve,
field: &str,
text: &str,
) -> Option<bool> {
if field == PROP_REVOLVE_ANGLE {
let angle = crate::entities::common::parse_angle(text)?;
if !angle.is_finite()
|| angle.abs() <= 1e-12
|| angle.abs() > std::f64::consts::TAU + 1e-12
|| (angle - value.revolve_angle).abs() <= 1e-12
{
return Some(false);
}
value.revolve_angle = angle;
return Some(true);
}
if matches!(
field,
PROP_AXIS_POSITION_X | PROP_AXIS_POSITION_Y | PROP_AXIS_POSITION_Z
) {
let axis = match field {
PROP_AXIS_POSITION_X => 0,
PROP_AXIS_POSITION_Y => 1,
_ => 2,
};
let target = crate::entities::common::parse_length(text)?;
if !target.is_finite() {
return Some(false);
}
let mut world = world_point(
value.base.transform,
[value.axis_point.x, value.axis_point.y, value.axis_point.z],
)?;
if (world[axis] - target).abs() <= 1e-12 {
return Some(false);
}
world[axis] = target;
let local = local_point(value.base.transform, world)?;
if !local.is_finite() {
return Some(false);
}
value.axis_point = acadrust::types::Vector3::new(local.x, local.y, local.z);
return Some(true);
}
if matches!(
field,
PROP_AXIS_DIRECTION_X | PROP_AXIS_DIRECTION_Y | PROP_AXIS_DIRECTION_Z
) {
let axis = match field {
PROP_AXIS_DIRECTION_X => 0,
PROP_AXIS_DIRECTION_Y => 1,
_ => 2,
};
let target = text.trim().replace(',', ".").parse::<f64>().ok()?;
if !target.is_finite() {
return Some(false);
}
let mut world = world_vector(
value.base.transform,
[value.direction.x, value.direction.y, value.direction.z],
)?;
world[axis] = target;
let Some(world) = world.try_normalize() else {
return Some(false);
};
let Some(local) = matrix(value.base.transform)?
.inverse()
.transform_vector3(world)
.try_normalize()
else {
return Some(false);
};
let current = glam::DVec3::new(value.direction.x, value.direction.y, value.direction.z)
.normalize_or_zero();
if (local - current).length_squared() <= 1e-24 {
return Some(false);
}
value.direction = acadrust::types::Vector3::new(local.x, local.y, local.z);
return Some(true);
}
None
}
pub fn apply_history_choice(
document: &mut acadrust::CadDocument,
handle: acadrust::Handle,
@@ -1578,6 +1783,11 @@ pub fn apply_primitive_property(
return applied;
}
}
if let SolidHistoryOperation::Revolve(revolve_value) = operation {
if let Some(applied) = apply_revolve_geometry_property(revolve_value, field, value) {
return applied;
}
}
if let SolidHistoryOperation::Torus(torus_value) = operation {
if let Some(applied) = apply_torus_position_property(torus_value, field, value) {
return applied;
+84 -12
View File
@@ -386,15 +386,65 @@ pub fn revolved(
from: [f64; 3],
to: [f64; 3],
angle: f64,
start_angle: f64,
) -> Option<Body> {
let profile = profile_of(entity)?;
brep::revolve(
profile.plane,
&profile.pieces,
from,
[to[0] - from[0], to[1] - from[1], to[2] - from[2]],
angle,
)
let axis = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
let body = if let Some((plane, profiles)) =
region_profiles(entity).filter(|(_, profiles)| profiles.len() > 1)
{
brep::revolve_region(plane, &profiles, from, axis, angle)?
} else {
let profile = profile_of(entity)?;
brep::revolve(profile.plane, &profile.pieces, from, axis, angle)?
};
turn_revolved_body(body, from, axis, start_angle)
}
/// REVOLVE sheet mode for an open or closed planar profile.
pub fn revolved_surface(
entity: &EntityType,
from: [f64; 3],
to: [f64; 3],
angle: f64,
start_angle: f64,
) -> Option<Body> {
let axis = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
let body = if let Some((plane, profiles)) =
region_profiles(entity).filter(|(_, profiles)| profiles.len() > 1)
{
brep::revolve_surface_region(plane, &profiles, from, axis, angle)?
} else {
let (profile, _) = extrusion_profile_of(entity)?;
brep::revolve_surface(profile.plane, &profile.pieces, from, axis, angle)?
};
turn_revolved_body(body, from, axis, start_angle)
}
fn turn_revolved_body(
body: Body,
pivot: [f64; 3],
axis: [f64; 3],
angle: f64,
) -> Option<Body> {
if !angle.is_finite() {
return None;
}
if angle.abs() <= 1e-12 {
return Some(body);
}
let axis = Vec3::from(axis).normalize()?;
let rotate = |vector: Vec3| {
let (sin, cos) = angle.sin_cos();
vector * cos + axis.cross(vector) * sin + axis * axis.dot(vector) * (1.0 - cos)
};
let pivot = Vec3::from(pivot);
let placement = brep::Placement {
x_axis: rotate(Vec3::X).to_array(),
y_axis: rotate(Vec3::Y).to_array(),
z_axis: rotate(Vec3::Z).to_array(),
origin: (pivot - rotate(pivot)).to_array(),
};
brep::transform(&body, &placement)
}
fn profile_center(profile: &Profile) -> Option<Vec3> {
@@ -413,8 +463,13 @@ pub fn revolve_history(
from: [f64; 3],
to: [f64; 3],
angle: f64,
start_angle: f64,
) -> Option<SolidHistoryOperation> {
if !angle.is_finite() || angle.abs() <= 1e-12 {
if !angle.is_finite()
|| angle.abs() <= 1e-12
|| !start_angle.is_finite()
|| matches!(entity, EntityType::Region(region) if region.wires.len() > 1)
{
return None;
}
let profile = profile_of(entity)?;
@@ -422,16 +477,27 @@ pub fn revolve_history(
let direction = (Vec3::from(to) - from).normalize()?;
let center = profile_center(&profile)?;
let axis_point = from + direction * (center - from).dot(direction);
let (sweep_entity, transform) = embedded_planar_entity(entity)?;
let transform = glam::DMat4::from_cols_array(&transform);
let inverse = transform.inverse();
if !transform.is_finite() || !inverse.is_finite() || transform.determinant().abs() <= 1e-12 {
return None;
}
let axis_point = inverse.transform_point3(glam::DVec3::from_array(axis_point.to_array()));
let direction = inverse
.transform_vector3(glam::DVec3::from_array(direction.to_array()))
.try_normalize()?;
let mut base = SolidHistoryNodeBase::new(1);
base.transform = glam::DMat4::IDENTITY.to_cols_array();
base.transform = transform.to_cols_array();
Some(SolidHistoryOperation::Revolve(SolidHistoryRevolve {
base,
operation_major: 1,
axis_point: Vector3::new(axis_point.x, axis_point.y, axis_point.z),
direction: Vector3::new(direction.x, direction.y, direction.z),
revolve_angle: angle,
start_angle,
flag_290: true,
sweep_entity: Some(embedded_path(entity)?),
sweep_entity: Some(sweep_entity),
..SolidHistoryRevolve::default()
}))
}
@@ -597,6 +663,10 @@ fn embedded_planar_entity(entity: &EntityType) -> Option<(EmbeddedEntity, [f64;
Some((EmbeddedEntity::LwPolyline(polyline), transform))
}
pub fn embedded_revolve_profile(entity: &EntityType) -> Option<(EmbeddedEntity, [f64; 16])> {
embedded_planar_entity(entity)
}
pub fn extrusion_history(
profile: &EntityType,
path: Option<&EntityType>,
@@ -850,6 +920,7 @@ mod tests {
[0.0, 0.0, 0.0],
[0.0, 10.0, 0.0],
std::f64::consts::TAU,
0.0,
);
// The profile is in the XY plane and the axis lies in it, so this is
// a revolution about the y axis: radius ten, height ten.
@@ -875,7 +946,8 @@ mod tests {
&square(10.0),
[0.0, 0.0, 0.0],
[0.0, 1.0, 1.0],
std::f64::consts::TAU
std::f64::consts::TAU,
0.0,
)
.is_none());
}