forked from 1b-agent/opencadstudio-upstream
wipeout: circles and arc polylines as boundaries
WIPEOUT's Polyline option took only straight closed polylines. It now also takes a circle or a closed polyline with arc segments, sampled every 5° into the polygon a wipeout stores, so the result stays a standard polygonal wipeout (#641). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
53b93dc98a
commit
f7bd0ca5ee
1 file changed
+63
-23
@@ -245,13 +245,13 @@ impl CadCommand for WipeoutCommand {
|
|||||||
};
|
};
|
||||||
if !matches!(
|
if !matches!(
|
||||||
entity,
|
entity,
|
||||||
EntityType::LwPolyline(_) | EntityType::Polyline2D(_)
|
EntityType::LwPolyline(_) | EntityType::Polyline2D(_) | EntityType::Circle(_)
|
||||||
) {
|
) {
|
||||||
return CmdResult::NeedPoint;
|
return CmdResult::NeedPoint;
|
||||||
}
|
}
|
||||||
if wipeout_from_polyline(&entity).is_none() {
|
if wipeout_from_polyline(&entity).is_none() {
|
||||||
return CmdResult::Measurement(
|
return CmdResult::Measurement(
|
||||||
"WIPEOUT: select a closed planar polyline made of zero-width straight segments."
|
"WIPEOUT: select a circle or a closed planar polyline of zero-width segments."
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -457,52 +457,92 @@ fn explicitly_closed(points: &[[f64; 2]]) -> bool {
|
|||||||
<= Tolerance::default().linear()
|
<= Tolerance::default().linear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a wipeout boundary from a picked closed polyline without consuming
|
/// The largest turn between boundary points along an arc. A wipeout stores
|
||||||
/// the source entity. Only straight, closed 2D polygonal boundaries qualify.
|
/// a polygon, so circles and arc segments are drawn as fine polygons.
|
||||||
|
const ARC_STEP: f64 = std::f64::consts::PI / 36.0;
|
||||||
|
|
||||||
|
/// The polygon of a closed run of `(point, bulge)` vertices, arc segments
|
||||||
|
/// sampled at [`ARC_STEP`].
|
||||||
|
fn bulged_polygon(vertices: &[([f64; 2], f64)]) -> Vec<[f64; 2]> {
|
||||||
|
let mut points = Vec::new();
|
||||||
|
for (index, &(point, bulge)) in vertices.iter().enumerate() {
|
||||||
|
points.push(point);
|
||||||
|
let next = vertices[(index + 1) % vertices.len()].0;
|
||||||
|
if let Some(arc) = crate::entities::common::BulgeArc::from_bulge(point, next, bulge) {
|
||||||
|
let samples = arc.tessellate_angle(ARC_STEP);
|
||||||
|
points.extend_from_slice(&samples[1..samples.len() - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
points
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a wipeout boundary from a picked circle or closed polyline without
|
||||||
|
/// consuming the source entity. Widened polylines do not qualify.
|
||||||
pub(crate) fn wipeout_from_polyline(entity: &EntityType) -> Option<EntityType> {
|
pub(crate) fn wipeout_from_polyline(entity: &EntityType) -> Option<EntityType> {
|
||||||
fn from_ocs(points: &[[f64; 2]], normal: Vector3, elevation: f64) -> Option<EntityType> {
|
fn from_ocs(points: &[[f64; 2]], normal: Vector3, elevation: f64) -> Option<EntityType> {
|
||||||
make_poly_wipeout(points, crate::entities::curve::ocs_plane(normal, elevation))
|
make_poly_wipeout(points, crate::entities::curve::ocs_plane(normal, elevation))
|
||||||
}
|
}
|
||||||
|
// An explicitly repeated closing vertex is the same corner twice.
|
||||||
|
fn closed_run(mut vertices: Vec<([f64; 2], f64)>, closed: bool) -> Option<Vec<[f64; 2]>> {
|
||||||
|
let points: Vec<[f64; 2]> = vertices.iter().map(|(point, _)| *point).collect();
|
||||||
|
if explicitly_closed(&points) {
|
||||||
|
vertices.pop();
|
||||||
|
} else if !closed {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(bulged_polygon(&vertices))
|
||||||
|
}
|
||||||
|
|
||||||
match entity {
|
match entity {
|
||||||
EntityType::LwPolyline(polyline) => {
|
EntityType::LwPolyline(polyline) => {
|
||||||
if polyline.constant_width != 0.0
|
if polyline.constant_width != 0.0
|
||||||
|| polyline.vertices.iter().any(|vertex| {
|
|| polyline
|
||||||
vertex.bulge != 0.0 || vertex.start_width != 0.0 || vertex.end_width != 0.0
|
.vertices
|
||||||
})
|
.iter()
|
||||||
|
.any(|vertex| vertex.start_width != 0.0 || vertex.end_width != 0.0)
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let raw: Vec<[f64; 2]> = polyline
|
let vertices = polyline
|
||||||
.vertices
|
.vertices
|
||||||
.iter()
|
.iter()
|
||||||
.map(|vertex| [vertex.location.x, vertex.location.y])
|
.map(|vertex| ([vertex.location.x, vertex.location.y], vertex.bulge))
|
||||||
.collect();
|
.collect();
|
||||||
if !polyline.is_closed && !explicitly_closed(&raw) {
|
let points = closed_run(vertices, polyline.is_closed)?;
|
||||||
return None;
|
from_ocs(&points, polyline.normal, polyline.elevation)
|
||||||
}
|
|
||||||
from_ocs(&raw, polyline.normal, polyline.elevation)
|
|
||||||
}
|
}
|
||||||
EntityType::Polyline2D(polyline) => {
|
EntityType::Polyline2D(polyline) => {
|
||||||
if polyline.start_width != 0.0
|
if polyline.start_width != 0.0
|
||||||
|| polyline.end_width != 0.0
|
|| polyline.end_width != 0.0
|
||||||
|| polyline.vertices.iter().any(|vertex| {
|
|| polyline
|
||||||
vertex.bulge != 0.0 || vertex.start_width != 0.0 || vertex.end_width != 0.0
|
.vertices
|
||||||
})
|
.iter()
|
||||||
|
.any(|vertex| vertex.start_width != 0.0 || vertex.end_width != 0.0)
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let raw: Vec<[f64; 2]> = polyline
|
let vertices = polyline
|
||||||
.vertices
|
.vertices
|
||||||
.iter()
|
.iter()
|
||||||
.map(|vertex| [vertex.location.x, vertex.location.y])
|
.map(|vertex| ([vertex.location.x, vertex.location.y], vertex.bulge))
|
||||||
.collect();
|
.collect();
|
||||||
if !polyline.is_closed() && !explicitly_closed(&raw) {
|
let points = closed_run(vertices, polyline.is_closed())?;
|
||||||
return None;
|
from_ocs(&points, polyline.normal, polyline.elevation)
|
||||||
}
|
|
||||||
from_ocs(&raw, polyline.normal, polyline.elevation)
|
|
||||||
}
|
}
|
||||||
_ => return None,
|
EntityType::Circle(circle) if circle.radius > 0.0 => {
|
||||||
|
let steps = (std::f64::consts::TAU / ARC_STEP).round() as usize;
|
||||||
|
let points: Vec<[f64; 2]> = (0..steps)
|
||||||
|
.map(|step| {
|
||||||
|
let angle = std::f64::consts::TAU * step as f64 / steps as f64;
|
||||||
|
[
|
||||||
|
circle.center.x + circle.radius * angle.cos(),
|
||||||
|
circle.center.y + circle.radius * angle.sin(),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
from_ocs(&points, circle.normal, circle.center.z)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user