Use exact kernel curves for PRESSPULL area containment

This commit is contained in:
Hakan Seven committed 2026-09-04 19:34:38 +03:00
1 parent eee077cbce
commit d8790ef433
3 files changed
+59 -14

No files matched your search

+1 -1
View File
@@ -237,7 +237,7 @@ impl super::OpenCADStudio {
if let Some(command) = self.tabs[i].active_cmd.as_mut() {
command.on_presspull_applied(true);
}
self.command_line.push_output(crate::tf!("PRESSPULL: %{count} object(s) updated.", count = handles.len()).as_ref());
self.command_line.push_output(crate::tf!("PRESSPULL: {count} object(s) updated.", count = handles.len()).as_ref());
self.refresh_properties();
self.refresh_presspull_prompt();
}
+15 -13
View File
@@ -141,24 +141,20 @@ fn boundary_sources(scene: &Scene, plane: WorkingPlane) -> FxHashMap<Handle, Bou
}).collect()
}
fn ring_curves(ring: &[[f64; 2]]) -> Vec<Curve> {
ring.iter().copied().zip(ring.iter().copied().cycle().skip(1))
.take(ring.len()).map(|(start, end)| Curve::Line(Line { start, end })).collect()
}
fn selected_rings(sources: &FxHashMap<Handle, BoundarySource>, point: [f64; 2]) -> Option<Vec<Vec<[f64; 2]>>> {
let rings = boundary_faces(sources, BOUNDARY_TOLERANCE);
let loops = boundary_loops(sources, &rings)?;
let tolerance = Tolerance::new(BOUNDARY_TOLERANCE);
let outer = rings.iter().enumerate()
.filter(|(_, ring)| contains(&ring_curves(ring), point, tolerance))
.filter(|(index, _)| contains(&loops[*index], point, tolerance))
.min_by(|(_, left), (_, right)| signed_area(left).abs().total_cmp(&signed_area(right).abs()))?
.0;
let depths = ring_nesting_depths(&rings);
let boundary = ring_curves(&rings[outer]);
let boundary = &loops[outer];
let mut result = vec![rings[outer].clone()];
for (index, ring) in rings.iter().enumerate() {
if index != outer && depths.get(index) == depths.get(outer).map(|depth| depth + 1).as_ref()
&& ring.first().is_some_and(|point| contains(&boundary, *point, tolerance))
&& ring.first().is_some_and(|point| contains(boundary, *point, tolerance))
{
result.push(ring.clone());
}
@@ -166,17 +162,23 @@ fn selected_rings(sources: &FxHashMap<Handle, BoundarySource>, point: [f64; 2])
Some(result)
}
fn boundary_region(
sources: &FxHashMap<Handle, BoundarySource>, rings: &[Vec<[f64; 2]>], plane: WorkingPlane,
) -> Option<EntityType> {
fn boundary_loops(
sources: &FxHashMap<Handle, BoundarySource>, rings: &[Vec<[f64; 2]>],
) -> Option<Vec<Vec<Curve>>> {
let exterior = (0..rings.len()).map(|index| index == 0).collect::<Vec<_>>();
let paths = exact_hatch_paths(rings, &exterior, sources, BOUNDARY_TOLERANCE);
if paths.len() != rings.len() {
return None;
}
let loops = paths.iter().map(|path| {
paths.iter().map(|path| {
path.edges.iter().map(crate::entities::hatch::edge_curve).collect::<Option<Vec<_>>>()
}).collect::<Option<Vec<_>>>()?;
}).collect()
}
fn boundary_region(
sources: &FxHashMap<Handle, BoundarySource>, rings: &[Vec<[f64; 2]>], plane: WorkingPlane,
) -> Option<EntityType> {
let loops = boundary_loops(sources, rings)?;
let kernel_plane = Plane::from_axes(plane.origin.to_array(), plane.x.to_array(), plane.y.to_array());
let sheet = brep::planar_region(kernel_plane, &loops)?;
let document = crate::scene::convert::acis_export::solid_to_sat(&sheet)?;
+43
View File
@@ -0,0 +1,43 @@
use acadrust::{entities::Circle, types::Vector3, EntityType};
use glam::DVec3;
use OpenCADStudio::command::WorkingPlane;
use OpenCADStudio::scene::{model::presspull_model, Scene};
#[test]
fn area_picks_follow_exact_curves_and_keep_holes() {
let mut scene = Scene::new();
for radius in [1000.0, 500.0] {
scene
.document
.add_entity(EntityType::Circle(Circle::from_center_radius(
Vector3::new(0.0, 0.0, 0.0),
radius,
)))
.unwrap();
}
// Halfway between display vertices, these points lie beyond the chords.
let angle = std::f64::consts::PI / 48.0;
for (radius, expected_loops) in [(999.0, 2), (499.0, 1)] {
let point = DVec3::new(radius * angle.cos(), radius * angle.sin(), 0.0);
let target =
presspull_model::resolve_target(&scene, None, point, WorkingPlane::default(), false)
.expect("point inside the exact circular boundary");
let presspull_model::PresspullTargetKind::Profile { entity, .. } = target.kind else {
panic!("expected a bounded profile");
};
let (_, loops, closed) = presspull_model::profile_geometry(&entity).unwrap();
assert!(closed);
assert_eq!(loops.len(), expected_loops);
assert!(loops
.iter()
.flatten()
.all(|curve| !matches!(curve, cadkernel::geom2d::Curve::Line(_))));
let body = presspull_model::extrusion_body(&entity, [0.0, 0.0, 10.0]).unwrap();
assert!(body.validate().is_empty());
}
let outside = DVec3::new(1001.0 * angle.cos(), 1001.0 * angle.sin(), 0.0);
assert!(
presspull_model::resolve_target(&scene, None, outside, WorkingPlane::default(), false,)
.is_err()
);
}