forked from 1b-agent/opencadstudio-upstream
Merge PR #1083: implement solid shell workflow
This commit is contained in:
commit
c5503e4758
19 files changed
+851
-115
No files matched your search
@@ -55,6 +55,44 @@ pub struct MeshMetrics {
|
||||
pub surface_area: f64,
|
||||
pub volume: f64,
|
||||
pub centroid: [f64; 3],
|
||||
pub moment_of_inertia: [f64; 3],
|
||||
pub principal_directions: [f64; 9],
|
||||
pub principal_moments: [f64; 3],
|
||||
pub product_of_inertia: [f64; 3],
|
||||
pub radii_of_gyration: [f64; 3],
|
||||
}
|
||||
|
||||
impl MeshMetrics {
|
||||
fn apply_mass_properties(&mut self, properties: cadkernel::brep::MassProperties) {
|
||||
self.volume = properties.volume;
|
||||
self.centroid = properties.centroid;
|
||||
self.moment_of_inertia = properties.moment_of_inertia;
|
||||
self.principal_directions = properties.principal_directions;
|
||||
self.principal_moments = properties.principal_moments;
|
||||
self.product_of_inertia = properties.product_of_inertia;
|
||||
self.radii_of_gyration = properties.radii_of_gyration;
|
||||
}
|
||||
|
||||
pub fn translate(&mut self, delta: [f64; 3]) {
|
||||
if self.volume > 1e-18 {
|
||||
self.apply_mass_properties(
|
||||
cadkernel::brep::MassProperties {
|
||||
volume: self.volume,
|
||||
centroid: self.centroid,
|
||||
moment_of_inertia: self.moment_of_inertia,
|
||||
principal_directions: self.principal_directions,
|
||||
principal_moments: self.principal_moments,
|
||||
product_of_inertia: self.product_of_inertia,
|
||||
radii_of_gyration: self.radii_of_gyration,
|
||||
}
|
||||
.translated(delta),
|
||||
);
|
||||
} else {
|
||||
for axis in 0..3 {
|
||||
self.centroid[axis] += delta[axis];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -146,74 +184,52 @@ fn compute_mesh_metrics(lods: &[MeshModel]) -> MeshMetrics {
|
||||
let Some(mesh) = lods.iter().find(|mesh| !mesh.indices.is_empty()) else {
|
||||
return MeshMetrics::default();
|
||||
};
|
||||
let point = |index: u32| {
|
||||
let index = index as usize;
|
||||
let high = mesh.verts.get(index).copied().unwrap_or([0.0; 3]);
|
||||
let low = mesh.verts_low.get(index).copied().unwrap_or([0.0; 3]);
|
||||
[
|
||||
high[0] as f64 + low[0] as f64,
|
||||
high[1] as f64 + low[1] as f64,
|
||||
high[2] as f64 + low[2] as f64,
|
||||
]
|
||||
let kernel_mesh = cadkernel::brep::Mesh {
|
||||
positions: mesh
|
||||
.verts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, high)| {
|
||||
let low = mesh.verts_low.get(index).copied().unwrap_or([0.0; 3]);
|
||||
[
|
||||
high[0] as f64 + low[0] as f64,
|
||||
high[1] as f64 + low[1] as f64,
|
||||
high[2] as f64 + low[2] as f64,
|
||||
]
|
||||
})
|
||||
.collect(),
|
||||
normals: Vec::new(),
|
||||
triangles: mesh
|
||||
.indices
|
||||
.chunks_exact(3)
|
||||
.map(|triangle| {
|
||||
[
|
||||
triangle[0] as usize,
|
||||
triangle[1] as usize,
|
||||
triangle[2] as usize,
|
||||
]
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
let mut area = 0.0;
|
||||
let mut area_centroid_numerator = [0.0; 3];
|
||||
let mut signed_volume = 0.0;
|
||||
let mut centroid_numerator = [0.0; 3];
|
||||
for triangle in mesh.indices.chunks_exact(3) {
|
||||
let a = point(triangle[0]);
|
||||
let b = point(triangle[1]);
|
||||
let c = point(triangle[2]);
|
||||
let ab = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
|
||||
let ac = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
|
||||
let cross = [
|
||||
ab[1] * ac[2] - ab[2] * ac[1],
|
||||
ab[2] * ac[0] - ab[0] * ac[2],
|
||||
ab[0] * ac[1] - ab[1] * ac[0],
|
||||
];
|
||||
let triangle_area = 0.5
|
||||
* (cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2])
|
||||
.sqrt();
|
||||
area += triangle_area;
|
||||
for axis in 0..3 {
|
||||
area_centroid_numerator[axis] +=
|
||||
triangle_area * (a[axis] + b[axis] + c[axis]) / 3.0;
|
||||
}
|
||||
let tetra = (
|
||||
a[0] * (b[1] * c[2] - b[2] * c[1])
|
||||
- a[1] * (b[0] * c[2] - b[2] * c[0])
|
||||
+ a[2] * (b[0] * c[1] - b[1] * c[0])
|
||||
) / 6.0;
|
||||
signed_volume += tetra;
|
||||
for axis in 0..3 {
|
||||
centroid_numerator[axis] += tetra * (a[axis] + b[axis] + c[axis]) / 4.0;
|
||||
}
|
||||
}
|
||||
let centroid = if signed_volume.abs() > 1e-18 {
|
||||
[
|
||||
centroid_numerator[0] / signed_volume,
|
||||
centroid_numerator[1] / signed_volume,
|
||||
centroid_numerator[2] / signed_volume,
|
||||
]
|
||||
} else if area > 1e-18 {
|
||||
[
|
||||
area_centroid_numerator[0] / area,
|
||||
area_centroid_numerator[1] / area,
|
||||
area_centroid_numerator[2] / area,
|
||||
]
|
||||
} else {
|
||||
[0.0; 3]
|
||||
};
|
||||
MeshMetrics {
|
||||
let surface = kernel_mesh.surface_properties();
|
||||
let mut metrics = MeshMetrics {
|
||||
vertices: mesh.verts.len(),
|
||||
triangles: mesh.indices.len() / 3,
|
||||
surface_area: area,
|
||||
volume: signed_volume.abs(),
|
||||
centroid,
|
||||
surface_area: surface.map_or(0.0, |value| value.0),
|
||||
centroid: surface.map_or([0.0; 3], |value| value.1),
|
||||
..MeshMetrics::default()
|
||||
};
|
||||
if let Some(properties) = kernel_mesh.inertial_properties() {
|
||||
metrics.apply_mass_properties(properties);
|
||||
}
|
||||
metrics
|
||||
}
|
||||
|
||||
impl MeshLodSet {
|
||||
pub fn apply_mass_properties(&mut self, properties: cadkernel::brep::MassProperties) {
|
||||
self.metrics.apply_mass_properties(properties);
|
||||
}
|
||||
|
||||
/// Build a set from its LODs, computing the 3D AABB.
|
||||
pub fn from_lods(lods: Vec<MeshModel>) -> Self {
|
||||
let (world_aabb, z_aabb) = compute_mesh_aabb(&lods);
|
||||
|
||||
@@ -1013,9 +1013,44 @@ fn brep_properties(
|
||||
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!("Solid History").into_owned(),
|
||||
props: vec![
|
||||
let mut geometry = vec![Property {
|
||||
label: t!("Solid type").into_owned(),
|
||||
field: "solid_history_type",
|
||||
value: PropValue::ReadOnly(t!("3D Solid").into_owned()),
|
||||
}];
|
||||
if let Some(position) = document
|
||||
.solid_history_operation(handle)
|
||||
.and_then(|operation| match operation {
|
||||
SolidHistoryOperation::Brep(value) => world_point(value.base.transform, [0.0; 3]),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
geometry.extend([
|
||||
crate::entities::common::edit_prop(
|
||||
t!("Position X").as_ref(),
|
||||
PROP_POSITION_X,
|
||||
position.x,
|
||||
),
|
||||
crate::entities::common::edit_prop(
|
||||
t!("Position Y").as_ref(),
|
||||
PROP_POSITION_Y,
|
||||
position.y,
|
||||
),
|
||||
crate::entities::common::edit_prop(
|
||||
t!("Position Z").as_ref(),
|
||||
PROP_POSITION_Z,
|
||||
position.z,
|
||||
),
|
||||
]);
|
||||
}
|
||||
vec![
|
||||
PropSection {
|
||||
title: t!("Geometry").into_owned(),
|
||||
props: geometry,
|
||||
},
|
||||
PropSection {
|
||||
title: t!("Solid History").into_owned(),
|
||||
props: vec![
|
||||
Property {
|
||||
label: t!("History").into_owned(),
|
||||
field: PROP_HISTORY,
|
||||
@@ -1037,7 +1072,8 @@ fn brep_properties(
|
||||
},
|
||||
},
|
||||
],
|
||||
}]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn cylinder_properties(
|
||||
@@ -2203,11 +2239,50 @@ fn set_pyramid_sides(value: &mut SolidHistoryPyramid, sides: i32) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn apply_brep_position_property(
|
||||
value: &mut SolidHistoryBrep,
|
||||
field: &str,
|
||||
text: &str,
|
||||
) -> Option<bool> {
|
||||
let axis = match field {
|
||||
PROP_POSITION_X => 0,
|
||||
PROP_POSITION_Y => 1,
|
||||
PROP_POSITION_Z => 2,
|
||||
_ => return None,
|
||||
};
|
||||
let Some(target) = crate::entities::common::parse_length(text) else {
|
||||
return Some(false);
|
||||
};
|
||||
if !target.is_finite() {
|
||||
return Some(false);
|
||||
}
|
||||
let Some(current) = matrix(value.base.transform) else {
|
||||
return Some(false);
|
||||
};
|
||||
let position = current.transform_point3(glam::DVec3::ZERO);
|
||||
let mut next = position;
|
||||
next[axis] = target;
|
||||
if next == position {
|
||||
return Some(true);
|
||||
}
|
||||
let updated = glam::DMat4::from_translation(next - position) * current;
|
||||
if !updated.is_finite() || updated.determinant().abs() <= 1e-12 {
|
||||
return Some(false);
|
||||
}
|
||||
value.base.transform = updated.to_cols_array();
|
||||
Some(true)
|
||||
}
|
||||
|
||||
pub fn apply_primitive_property(
|
||||
operation: &mut SolidHistoryOperation,
|
||||
field: &str,
|
||||
value: &str,
|
||||
) -> bool {
|
||||
if let SolidHistoryOperation::Brep(brep_value) = operation {
|
||||
if let Some(applied) = apply_brep_position_property(brep_value, field, value) {
|
||||
return applied;
|
||||
}
|
||||
}
|
||||
if let SolidHistoryOperation::Box(rectangular_value)
|
||||
| SolidHistoryOperation::Wedge(rectangular_value) = operation
|
||||
{
|
||||
|
||||
@@ -475,7 +475,11 @@ pub fn display_from_solid(
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
Some((mesh_from_tessellation(tessellation, color)?, wires, center))
|
||||
let mut mesh = mesh_from_tessellation(tessellation, color)?;
|
||||
if let Some(properties) = cadkernel::brep::analytic_mass_properties(body) {
|
||||
mesh.apply_mass_properties(properties);
|
||||
}
|
||||
Some((mesh, wires, center))
|
||||
}
|
||||
|
||||
/// The middle of a body, for a caller needing a point to turn or scale about.
|
||||
|
||||
Reference in new issue
Block a user