forked from 1b-agent/opencadstudio-upstream
feat(model): align polysolid workflow
This commit is contained in:
1 parent
6d6defa0ad
commit
1dd6b115cf
12 files changed
+857
-250
No files matched your search
+103
-77
@@ -1,8 +1,13 @@
|
||||
// Exact profile sweeps stored as kernel B-reps and ACIS.
|
||||
|
||||
use cadkernel::brep::{self, Body};
|
||||
use cadkernel::geom2d::{offset_polyline, Arc, Curve, EllipseArc, Line, Polyline};
|
||||
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,
|
||||
};
|
||||
use acadrust::types::{Vector2, Vector3};
|
||||
use acadrust::EntityType;
|
||||
|
||||
use crate::entities::curve::entity_curve;
|
||||
@@ -162,88 +167,109 @@ pub fn lofted(profiles: &[EntityType]) -> Option<Body> {
|
||||
brep::loft(§ions)
|
||||
}
|
||||
|
||||
/// Builds an exact wall solid around a polyline centreline.
|
||||
pub fn polysolid(entity: &EntityType, width: f64, height: f64) -> Option<Body> {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum PolysolidJustification {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
fn embedded_path(entity: &EntityType) -> Option<EmbeddedEntity> {
|
||||
match entity {
|
||||
EntityType::Line(value) => Some(EmbeddedEntity::Line(value.clone())),
|
||||
EntityType::Arc(value) => Some(EmbeddedEntity::Arc(value.clone())),
|
||||
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::Spline(_) => {
|
||||
let planar = entity_curve(entity)?;
|
||||
let samples = planar.curve.tessellate_within(1e-4);
|
||||
if samples.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
let mut path = LwPolyline::new();
|
||||
path.vertices = samples
|
||||
.into_iter()
|
||||
.map(|point| LwVertex::new(Vector2::new(point[0], point[1])))
|
||||
.collect();
|
||||
path.is_closed = planar.curve.is_closed();
|
||||
let plane = crate::command::WorkingPlane::new(
|
||||
glam::DVec3::from_array(planar.plane.origin),
|
||||
glam::DVec3::from_array(planar.plane.x_axis),
|
||||
glam::DVec3::from_array(planar.plane.y_axis),
|
||||
);
|
||||
let EntityType::LwPolyline(path) = plane.place_entity(EntityType::LwPolyline(path))
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
Some(EmbeddedEntity::LwPolyline(path))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a history-backed wall solid along a supported planar curve.
|
||||
pub fn polysolid(
|
||||
entity: &EntityType,
|
||||
width: f64,
|
||||
height: f64,
|
||||
justification: PolysolidJustification,
|
||||
) -> Option<(Body, SolidHistoryOperation)> {
|
||||
if !width.is_finite() || !height.is_finite() || width <= 0.0 || height.abs() <= 1e-12 {
|
||||
return None;
|
||||
}
|
||||
let planar = entity_curve(entity)?;
|
||||
let Curve::Polyline(source) = planar.curve else {
|
||||
let start_local = planar.curve.point_at(0.0);
|
||||
let tangent_local = Vec3::from([
|
||||
planar.curve.tangent_at(0.0)[0],
|
||||
planar.curve.tangent_at(0.0)[1],
|
||||
0.0,
|
||||
])
|
||||
.normalize()?;
|
||||
let start = Vec3::from(planar.plane.point_at(start_local));
|
||||
let tangent = (Vec3::from(planar.plane.x_axis) * tangent_local.x
|
||||
+ Vec3::from(planar.plane.y_axis) * tangent_local.y)
|
||||
.normalize()?;
|
||||
let normal = Vec3::from(planar.plane.normal()?).normalize()?;
|
||||
let side = tangent.cross(normal).normalize()?;
|
||||
let offset = match justification {
|
||||
PolysolidJustification::Left => 0.0,
|
||||
PolysolidJustification::Center => -width * 0.5,
|
||||
PolysolidJustification::Right => -width,
|
||||
};
|
||||
let profile_origin = start + side * offset;
|
||||
let profile_plane = crate::command::WorkingPlane::new(
|
||||
glam::DVec3::from_array(profile_origin.to_array()),
|
||||
glam::DVec3::from_array(side.to_array()),
|
||||
glam::DVec3::from_array(normal.to_array()),
|
||||
);
|
||||
let mut profile = LwPolyline::new();
|
||||
profile.vertices = [[0.0, 0.0], [width, 0.0], [width, height], [0.0, height]]
|
||||
.into_iter()
|
||||
.map(|point| LwVertex::new(Vector2::new(point[0], point[1])))
|
||||
.collect();
|
||||
profile.is_closed = true;
|
||||
let EntityType::LwPolyline(profile) =
|
||||
profile_plane.place_entity(EntityType::LwPolyline(profile))
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
let (left_pick, right_pick) = offset_picks(&source, width)?;
|
||||
let mut left = offset_polyline(&source, width * 0.5, left_pick);
|
||||
let mut right = offset_polyline(&source, width * 0.5, right_pick);
|
||||
if left.len() != 1 || right.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
let left = left.remove(0);
|
||||
let right = right.remove(0);
|
||||
let normal = Vec3::from(planar.plane.normal()?);
|
||||
let direction = (normal * height).to_array();
|
||||
|
||||
if source.closed {
|
||||
if !left.closed || !right.closed {
|
||||
return None;
|
||||
}
|
||||
let mut profiles = [left, right]
|
||||
.into_iter()
|
||||
.map(|polyline| {
|
||||
let area = Curve::Polyline(polyline.clone()).enclosed_area().abs();
|
||||
(area, Curve::Polyline(polyline).segments())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
profiles.sort_by(|first, second| second.0.total_cmp(&first.0));
|
||||
if profiles[0].0 <= profiles[1].0 || profiles[1].0 <= 1e-12 {
|
||||
return None;
|
||||
}
|
||||
return brep::extrude_region(
|
||||
planar.plane,
|
||||
&[profiles.remove(0).1, profiles.remove(0).1],
|
||||
direction,
|
||||
);
|
||||
}
|
||||
|
||||
if left.closed || right.closed {
|
||||
return None;
|
||||
}
|
||||
let left_start = left.vertices.first()?.position;
|
||||
let left_end = left.vertices.last()?.position;
|
||||
let right_start = right.vertices.first()?.position;
|
||||
let right_end = right.vertices.last()?.position;
|
||||
let mut outline = Curve::Polyline(left).segments();
|
||||
outline.push(Curve::Line(Line {
|
||||
start: left_end,
|
||||
end: right_end,
|
||||
}));
|
||||
let mut back = Curve::Polyline(right).segments();
|
||||
back.reverse();
|
||||
outline.extend(back);
|
||||
outline.push(Curve::Line(Line {
|
||||
start: right_start,
|
||||
end: left_start,
|
||||
}));
|
||||
brep::extrude(planar.plane, &outline, direction)
|
||||
}
|
||||
|
||||
fn offset_picks(polyline: &Polyline, width: f64) -> Option<([f64; 2], [f64; 2])> {
|
||||
let start = polyline.vertices.first()?.position;
|
||||
let next = polyline.vertices.get(1)?.position;
|
||||
let along = if let Some(arc) = polyline.segment_arc(0) {
|
||||
let near = arc.sample(1e-6);
|
||||
[near[0] - start[0], near[1] - start[1]]
|
||||
} else {
|
||||
[next[0] - start[0], next[1] - start[1]]
|
||||
};
|
||||
let length = along[0].hypot(along[1]);
|
||||
if length <= 1e-12 {
|
||||
return None;
|
||||
}
|
||||
let side = [-along[1] / length * width, along[0] / length * width];
|
||||
Some((
|
||||
[start[0] + side[0], start[1] + side[1]],
|
||||
[start[0] - side[0], start[1] - side[1]],
|
||||
))
|
||||
let mut base = SolidHistoryNodeBase::new(1);
|
||||
base.transform = glam::DMat4::IDENTITY.to_cols_array();
|
||||
let operation = SolidHistoryOperation::Sweep(SolidHistorySweep {
|
||||
base,
|
||||
operation_major: 1,
|
||||
direction: Vector3::new(normal.x * height, normal.y * height, normal.z * height),
|
||||
sweep_entity: Some(EmbeddedEntity::LwPolyline(profile)),
|
||||
path_entity: Some(embedded_path(entity)?),
|
||||
scale_factor: 1.0,
|
||||
sweep_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
||||
path_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
||||
reference_point: Vector3::new(start.x, start.y, start.z),
|
||||
..SolidHistorySweep::default()
|
||||
});
|
||||
let body = cadkernel::acis::rebuild_body(&operation).ok()?;
|
||||
Some((body, operation))
|
||||
}
|
||||
|
||||
fn circular_loft(profiles: &[EntityType]) -> Option<Body> {
|
||||
|
||||
Reference in new issue
Block a user