forked from 1b-agent/opencadstudio-upstream
Use exact kernel curves for PRESSPULL area containment
This commit is contained in:
1 parent
eee077cbce
commit
d8790ef433
3 files changed
+59
-14
No files matched your search
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user