Fix extrusion surface properties and snapping

This commit is contained in:
ramox81 committed 2026-09-10 16:07:05 +03:00
1 parent a24d1e6108
commit 11057cc5e9
15 files changed
+383 -55

No files matched your search

+2 -2
View File
@@ -24,7 +24,7 @@ pub fn tessellate_sat(
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
isolines: [usize; 2],
planar_isolines: bool,
) -> Option<MeshLodSet> {
let (bodies, loss) = lift(document);
@@ -115,7 +115,7 @@ pub fn tessellate_sat(
max_angle,
source_fit * placement_scale,
)
.with_isolines(isolines)
.with_uv_isolines(isolines[0], isolines[1])
.with_planar_isolines(planar_isolines);
if let Some(deflection) = chordal_deflection {
tolerance = tolerance.with_chordal_deflection(deflection);
+8 -7
View File
@@ -10,7 +10,7 @@ fn tessellate_acis(
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
isolines: [usize; 2],
planar_isolines: bool,
) -> Option<MeshLodSet> {
crate::scene::convert::acis_kernel::tessellate_sat(
@@ -220,7 +220,7 @@ fn finish(
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
isolines: [usize; 2],
planar_isolines: bool,
acis: &acadrust::entities::AcisData,
) -> Option<MeshLodSet> {
@@ -255,7 +255,7 @@ pub fn tessellate_region(
color,
facet_res,
chordal_deflection,
isolines,
[isolines; 2],
false,
&region.acis_data,
)
@@ -279,7 +279,7 @@ pub fn tessellate_body(
color,
facet_res,
chordal_deflection,
isolines,
[isolines; 2],
false,
&body.acis_data,
)
@@ -290,7 +290,8 @@ pub fn tessellate_surface(
color: [f32; 4],
facet_res: f64,
chordal_deflection: Option<f64>,
isolines: usize,
isolines: [usize; 2],
planar_isolines: bool,
) -> Option<MeshLodSet> {
let sat = parse_acis(
|| surface.parse_sat(),
@@ -304,7 +305,7 @@ pub fn tessellate_surface(
facet_res,
chordal_deflection,
isolines,
true,
planar_isolines,
&surface.acis_data,
)
}
@@ -327,7 +328,7 @@ pub fn tessellate_solid3d(
color,
facet_res,
chordal_deflection,
isolines,
[isolines; 2],
false,
&solid.acis_data,
)
+7 -5
View File
@@ -3082,11 +3082,13 @@ impl Scene {
let facet_resolution = self.document.header.facet_resolution;
let chordal_deflection =
crate::entities::solid3d::display_deflection(&self.document.header, facet_resolution);
let isolines = self.document.header.isolines.max(0) as usize;
let planar_isolines = matches!(
self.document.get_entity(handle),
Some(EntityType::Surface(_))
);
let (isolines, planar_isolines) = match self.document.get_entity(handle) {
Some(EntityType::Surface(surface)) => (
crate::entities::solid3d::surface_isoline_counts(surface),
crate::entities::solid3d::surface_property_state(surface).isolines,
),
_ => ([self.document.header.isolines.max(0) as usize; 2], false),
};
let (mut set, wires, center) =
crate::scene::model::solid_model::display_from_solid(
solid,
+176 -14
View File
@@ -94,6 +94,14 @@ pub const PROP_LOFT_CLOSED: &str = "solid_history_loft_closed";
pub const PROP_LOFT_PERIODIC: &str = "solid_history_loft_periodic";
pub const PROP_HISTORY: &str = "solid_history_record";
pub const PROP_SHOW_HISTORY: &str = "solid_history_show";
pub const PROP_SURFACE_TYPE: &str = "surface_type";
pub const PROP_SURFACE_WIREFRAME_TYPE: &str = "srf_wireframe_type";
pub const PROP_SURFACE_U_ISOLINES: &str = "srf_u_isolines";
pub const PROP_SURFACE_V_ISOLINES: &str = "srf_v_isolines";
pub const PROP_SURFACE_MAINTAIN_ASSOCIATIVITY: &str = "srf_maintain_associativity";
pub const PROP_SURFACE_SHOW_ASSOCIATIVITY: &str = "srf_show_associativity";
pub const PROP_SURFACE_TRIMMED: &str = "srf_trimmed";
pub const PROP_SURFACE_TRIMMING_EDGES: &str = "srf_trimming_edges";
fn history_prop(label: &str, field: &'static str, value: impl ToString) -> Property {
Property {
@@ -103,6 +111,23 @@ fn history_prop(label: &str, field: &'static str, value: impl ToString) -> Prope
}
}
fn compact_surface_number(mut value: String) -> String {
if value.ends_with('°') {
value.pop();
}
if let Some(decimal) = value.rfind('.') {
if value[decimal + 1..].chars().all(|character| character.is_ascii_digit()) {
while value.ends_with('0') {
value.pop();
}
if value.ends_with('.') {
value.pop();
}
}
}
value
}
fn history_flags(
document: &acadrust::CadDocument,
handle: acadrust::Handle,
@@ -239,9 +264,14 @@ pub fn has_compact_solid_properties(
document: &acadrust::CadDocument,
handle: acadrust::Handle,
) -> bool {
document
.get_entity(handle)
.is_some_and(|entity| matches!(entity, acadrust::EntityType::Solid3D(_)))
match document.get_entity(handle) {
Some(acadrust::EntityType::Solid3D(_)) => true,
Some(acadrust::EntityType::Surface(_)) => matches!(
primitive_property_operation(document, handle),
Some(SolidHistoryOperation::Extrusion(_))
),
_ => false,
}
}
pub fn reference_point(operation: &SolidHistoryOperation) -> Option<glam::DVec3> {
@@ -403,10 +433,6 @@ fn extrusion_properties(
|direction| crate::entities::common::format_length(direction[axis]),
)),
};
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 path_length = sweep_path_length(value);
let height_value = path_length.unwrap_or(value.end_draft_distance);
let height = if value.path_entity.is_some() {
@@ -419,6 +445,134 @@ fn extrusion_properties(
} else {
PropValue::EditText(crate::entities::common::format_angle(value.draft_angle))
};
if let Some(EntityType::Surface(surface)) = document.get_entity(handle) {
let state = crate::entities::solid3d::surface_property_state(surface);
let surface_height = if value.path_entity.is_some() {
PropValue::ReadOnly(compact_surface_number(
crate::entities::common::format_length(height_value),
))
} else {
PropValue::EditText(compact_surface_number(
crate::entities::common::format_length(height_value),
))
};
let surface_taper = if value.path_entity.is_some() {
PropValue::ReadOnly(compact_surface_number(
crate::entities::common::format_angle(value.draft_angle),
))
} else {
PropValue::EditText(compact_surface_number(
crate::entities::common::format_angle(value.draft_angle),
))
};
let surface_direction_property =
|label: &str, field: &'static str, axis: usize| Property {
label: label.to_string(),
field,
value: PropValue::ReadOnly(direction.map_or_else(
|| t!("Unavailable").into_owned(),
|direction| {
compact_surface_number(crate::entities::common::format_length(
direction[axis],
))
},
)),
};
let yes_no_choice = |selected: bool| PropValue::Choice {
selected: if selected { "Yes" } else { "No" }.to_string(),
options: vec!["Yes".to_string(), "No".to_string()],
};
return vec![
PropSection {
title: t!("Geometry").into_owned(),
props: vec![
Property {
label: t!("Surface Type").into_owned(),
field: PROP_SURFACE_TYPE,
value: PropValue::ReadOnly(t!("Extrusion").into_owned()),
},
Property {
label: t!("Height").into_owned(),
field: PROP_EXTRUSION_HEIGHT,
value: surface_height,
},
Property {
label: t!("Taper angle").into_owned(),
field: PROP_TAPER_ANGLE,
value: surface_taper,
},
surface_direction_property(
t!("Direction X").as_ref(),
PROP_EXTRUSION_DIRECTION_X,
0,
),
surface_direction_property(
t!("Direction Y").as_ref(),
PROP_EXTRUSION_DIRECTION_Y,
1,
),
surface_direction_property(
t!("Direction Z").as_ref(),
PROP_EXTRUSION_DIRECTION_Z,
2,
),
Property {
label: t!("Wireframe type").into_owned(),
field: PROP_SURFACE_WIREFRAME_TYPE,
value: PropValue::Choice {
selected: if state.isolines { "Isolines" } else { "Isoparms" }
.to_string(),
options: vec!["Isolines".to_string(), "Isoparms".to_string()],
},
},
Property {
label: t!("U isolines").into_owned(),
field: PROP_SURFACE_U_ISOLINES,
value: PropValue::EditText(surface.u_isolines.max(0).to_string()),
},
Property {
label: t!("V isolines").into_owned(),
field: PROP_SURFACE_V_ISOLINES,
value: PropValue::EditText(surface.v_isolines.max(0).to_string()),
},
],
},
PropSection {
title: t!("Surface Associativity").into_owned(),
props: vec![
Property {
label: t!("Maintain associativity").into_owned(),
field: PROP_SURFACE_MAINTAIN_ASSOCIATIVITY,
value: yes_no_choice(state.maintain_associativity),
},
Property {
label: t!("Show associativity").into_owned(),
field: PROP_SURFACE_SHOW_ASSOCIATIVITY,
value: yes_no_choice(state.show_associativity),
},
],
},
PropSection {
title: t!("Trims").into_owned(),
props: vec![
Property {
label: t!("Trimmed surface").into_owned(),
field: PROP_SURFACE_TRIMMED,
value: PropValue::ReadOnly("No".to_string()),
},
Property {
label: t!("Trimming edges").into_owned(),
field: PROP_SURFACE_TRIMMING_EDGES,
value: PropValue::ReadOnly("0".to_string()),
},
],
},
];
}
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);
vec![
PropSection {
title: t!("Geometry").into_owned(),
@@ -1531,6 +1685,15 @@ pub fn is_history_choice(field: &str) -> bool {
matches!(field, PROP_HISTORY | PROP_SHOW_HISTORY)
}
pub fn is_surface_property_choice(field: &str) -> bool {
matches!(
field,
PROP_SURFACE_WIREFRAME_TYPE
| PROP_SURFACE_MAINTAIN_ASSOCIATIVITY
| PROP_SURFACE_SHOW_ASSOCIATIVITY
)
}
pub fn is_loft_geometry_choice(field: &str) -> bool {
matches!(field, PROP_LOFT_NORMALS | PROP_LOFT_CLOSED | PROP_LOFT_PERIODIC)
}
@@ -1538,7 +1701,11 @@ pub fn is_loft_geometry_choice(field: &str) -> bool {
pub fn is_specialized_property(field: &str) -> bool {
matches!(field, "solid_history_type" | PROP_BANK | PROP_SWEEP_LENGTH
| PROP_LOFT_TYPE | PROP_LOFT_SECTION_COUNT
| PROP_EXTRUSION_DIRECTION_X | PROP_EXTRUSION_DIRECTION_Y | PROP_EXTRUSION_DIRECTION_Z)
| PROP_EXTRUSION_DIRECTION_X | PROP_EXTRUSION_DIRECTION_Y | PROP_EXTRUSION_DIRECTION_Z
| PROP_SURFACE_TYPE | PROP_SURFACE_WIREFRAME_TYPE
| PROP_SURFACE_U_ISOLINES | PROP_SURFACE_V_ISOLINES
| PROP_SURFACE_MAINTAIN_ASSOCIATIVITY | PROP_SURFACE_SHOW_ASSOCIATIVITY
| PROP_SURFACE_TRIMMED | PROP_SURFACE_TRIMMING_EDGES)
|| is_primitive_property(field)
|| is_history_choice(field)
}
@@ -3018,11 +3185,6 @@ fn extrusion_draft_grip(
if !inverse.is_finite() {
return None;
}
let profile = value.sweep_entity.as_ref().and_then(embedded_entity)?;
let normal = crate::entities::curve::entity_curve(&profile)?.plane.normal()?;
let normal = transform
.transform_vector3(glam::DVec3::from_array(normal))
.try_normalize()?;
let center_local = inverse.transform_point3(center);
let anchor = profile_grips
.into_iter()
@@ -3057,7 +3219,7 @@ fn extrusion_draft_grip(
value.direction.y,
value.direction.z,
));
let height = direction.dot(normal).abs();
let height = direction.length();
if !direction.is_finite() || !height.is_finite() || height <= 1e-6 {
return None;
}
+8 -7
View File
@@ -22,7 +22,7 @@ fn display_tessellation(
body: &Body,
facet_resolution: f64,
chordal_deflection: Option<f64>,
isolines: usize,
isolines: [usize; 2],
planar_isolines: bool,
) -> brep::mesh::BodyMesh {
let resolution = if facet_resolution.is_finite() && facet_resolution > 0.0 {
@@ -35,7 +35,7 @@ fn display_tessellation(
|_| cadkernel::tessellation::display_angle_for_resolution(resolution),
);
let mut tolerance = brep::mesh::TessellationTolerance::new(max_angle, TOL)
.with_isolines(isolines)
.with_uv_isolines(isolines[0], isolines[1])
.with_planar_isolines(planar_isolines);
if let Some(deflection) = chordal_deflection {
tolerance = tolerance.with_chordal_deflection(deflection);
@@ -278,7 +278,8 @@ pub fn edge_wires(body: &Body) -> Vec<acadrust::entities::Wire> {
pub fn grip_preview_wires(
body: &Body,
handle: acadrust::Handle,
isolines: usize,
isolines: [usize; 2],
planar_isolines: bool,
) -> Vec<WireModel> {
let tessellation = brep::mesh::tessellate(
body,
@@ -286,8 +287,8 @@ pub fn grip_preview_wires(
cadkernel::tessellation::DEFAULT_ANGLE,
TOL,
)
.with_isolines(isolines)
.with_planar_isolines(isolines > 0),
.with_uv_isolines(isolines[0], isolines[1])
.with_planar_isolines(planar_isolines),
);
tessellation
.edges
@@ -476,7 +477,7 @@ pub fn display_from_solid(
color: [f32; 4],
facet_resolution: f64,
chordal_deflection: Option<f64>,
isolines: usize,
isolines: [usize; 2],
planar_isolines: bool,
) -> Option<(MeshLodSet, Vec<acadrust::entities::Wire>, [f64; 3])> {
use acadrust::types::Vector3;
@@ -566,7 +567,7 @@ mod tests {
use super::*;
fn tri_count(body: &Body) -> usize {
display_from_solid(body, [0.7, 0.7, 0.7, 1.0], 1.0, None, 0, false)
display_from_solid(body, [0.7, 0.7, 0.7, 1.0], 1.0, None, [0; 2], false)
.map(|(m, _, _)| m.lods[0].indices.len() / 3)
.unwrap_or(0)
}
+9 -5
View File
@@ -1041,16 +1041,20 @@ impl Scene {
{
return false;
}
let (isoline_counts, planar_isolines) = match self.document.get_entity(handle) {
Some(EntityType::Surface(surface)) => (
crate::entities::solid3d::surface_isoline_counts(surface),
crate::entities::solid3d::surface_property_state(surface).isolines,
),
_ => ([0; 2], false),
};
self.solid_history_preview_wires.insert(
handle,
crate::scene::model::solid_model::grip_preview_wires(
&body,
handle,
if matches!(self.document.get_entity(handle), Some(EntityType::Surface(_))) {
self.document.header.isolines.max(0) as usize
} else {
0
},
isoline_counts,
planar_isolines,
),
);
true