forked from 1b-agent/opencadstudio-upstream
fix(hatch): pick points stay fast in large drawings, and see off layers
Each picked region matched every edge of its rings against every boundary source of the drawing, twice: with 65,000 sources a click took over 30 seconds. Sources are now narrowed to those reaching the rings' box first, once per hatch and once per ring: the same click takes 9 ms. Objects on layers that are turned off (not frozen) now bound a hatch, as in the reference. Drawings switch off the layers that close the regions to be hatched; without them a click between columns found the outer frame instead. (#1813) Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
d67fd101b4
commit
d2995ecee1
3 files changed
+70
-7
No files matched your search
@@ -350,12 +350,13 @@ impl HatchCommand {
|
||||
.into_iter()
|
||||
.map(|depth| depth == 0)
|
||||
.collect();
|
||||
let near = crate::scene::sources_near_rings(&rings, &self.boundary_sources, 1.0e-6);
|
||||
let mut boundary_sources: Vec<Vec<Handle>> = rings
|
||||
.iter()
|
||||
.map(|ring| crate::scene::ring_source_handles(ring, &self.boundary_sources))
|
||||
.map(|ring| crate::scene::ring_source_handles(ring, &near))
|
||||
.collect();
|
||||
let mut boundary_paths =
|
||||
crate::scene::exact_hatch_paths(&rings, &exterior, &self.boundary_sources, 1.0e-6);
|
||||
let mut boundary_paths = crate::scene::exact_hatch_paths(&rings, &exterior, &near, 1.0e-6);
|
||||
|
||||
if !self.associative {
|
||||
for handles in &mut boundary_sources {
|
||||
handles.clear();
|
||||
@@ -983,12 +984,12 @@ impl GradientCommand {
|
||||
.into_iter()
|
||||
.map(|depth| depth == 0)
|
||||
.collect();
|
||||
let near = crate::scene::sources_near_rings(&rings, &self.boundary_sources, 1.0e-6);
|
||||
let boundary_sources = rings
|
||||
.iter()
|
||||
.map(|ring| crate::scene::ring_source_handles(ring, &self.boundary_sources))
|
||||
.map(|ring| crate::scene::ring_source_handles(ring, &near))
|
||||
.collect();
|
||||
let boundary_paths =
|
||||
crate::scene::exact_hatch_paths(&rings, &exterior, &self.boundary_sources, 1.0e-6);
|
||||
let boundary_paths = crate::scene::exact_hatch_paths(&rings, &exterior, &near, 1.0e-6);
|
||||
HatchModel {
|
||||
pattern_origin: None,
|
||||
render_instance: None,
|
||||
|
||||
@@ -211,10 +211,41 @@ fn matching_face(faces: &[Vec<[f64; 2]>], seed: Option<[f64; 2]>) -> Option<&Vec
|
||||
})
|
||||
}
|
||||
|
||||
/// The sources with a segment reaching the box around `rings`: the only ones
|
||||
/// their edges can lie on. Matching every edge against every source of a
|
||||
/// large drawing took half a minute per picked hatch region. (#1813)
|
||||
pub(crate) fn sources_near_rings(
|
||||
rings: &[Vec<[f64; 2]>],
|
||||
sources: &rustc_hash::FxHashMap<codec::Handle, BoundarySource>,
|
||||
pad: f64,
|
||||
) -> rustc_hash::FxHashMap<codec::Handle, BoundarySource> {
|
||||
let mut lo = [f64::INFINITY; 2];
|
||||
let mut hi = [f64::NEG_INFINITY; 2];
|
||||
for point in rings.iter().flatten() {
|
||||
for axis in 0..2 {
|
||||
lo[axis] = lo[axis].min(point[axis] - pad);
|
||||
hi[axis] = hi[axis].max(point[axis] + pad);
|
||||
}
|
||||
}
|
||||
let reaches = |line: &Line| {
|
||||
(0..2).all(|axis| {
|
||||
line.start[axis].min(line.end[axis]) <= hi[axis]
|
||||
&& line.start[axis].max(line.end[axis]) >= lo[axis]
|
||||
})
|
||||
};
|
||||
sources
|
||||
.iter()
|
||||
.filter(|(_, source)| source.segments.iter().any(reaches))
|
||||
.map(|(handle, source)| (*handle, source.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn ring_source_handles(
|
||||
ring: &[[f64; 2]],
|
||||
sources: &rustc_hash::FxHashMap<codec::Handle, BoundarySource>,
|
||||
) -> Vec<codec::Handle> {
|
||||
let near = sources_near_rings(&[ring.to_vec()], sources, WELD_TOLERANCE);
|
||||
let sources = &near;
|
||||
let mut handles = rustc_hash::FxHashSet::default();
|
||||
for (&start, &end) in ring
|
||||
.iter()
|
||||
@@ -411,6 +442,8 @@ pub(crate) fn exact_hatch_paths(
|
||||
) -> Vec<codec::entities::BoundaryPath> {
|
||||
use codec::entities::{BoundaryPath, BoundaryPathFlags};
|
||||
|
||||
let near = sources_near_rings(rings, sources, tolerance.max(WELD_TOLERANCE));
|
||||
let sources = &near;
|
||||
rings
|
||||
.iter()
|
||||
.enumerate()
|
||||
@@ -568,6 +601,8 @@ fn refined_boundary_ring(
|
||||
if count < 3 {
|
||||
return (ring.to_vec(), Vec::new());
|
||||
}
|
||||
let near = sources_near_rings(&[ring.to_vec()], sources, tolerance.max(WELD_TOLERANCE));
|
||||
let sources = &near;
|
||||
let edge_curves: Vec<_> = (0..count)
|
||||
.map(|index| {
|
||||
edge_curve(
|
||||
@@ -1022,6 +1057,32 @@ impl Scene {
|
||||
.segments
|
||||
.extend(segments);
|
||||
}
|
||||
// Objects on a layer that is turned off (not frozen) still bound a
|
||||
// hatch, as in the reference: drawings switch off the layers that
|
||||
// close the regions to be hatched. They draw no wire, so they come
|
||||
// from the document. (#1813)
|
||||
let space = if self.active_viewport.is_some() || self.current_layout == "Model" {
|
||||
self.content_render_block_handle()
|
||||
} else {
|
||||
self.current_layout_block_handle()
|
||||
};
|
||||
for entity in self.document.entities() {
|
||||
let common = entity.common();
|
||||
if common.owner_handle != space || sources.contains_key(&common.handle) {
|
||||
continue;
|
||||
}
|
||||
let hidden = self
|
||||
.document
|
||||
.layers
|
||||
.get(&common.layer)
|
||||
.is_some_and(|layer| layer.is_off() && !layer.is_frozen());
|
||||
if !hidden {
|
||||
continue;
|
||||
}
|
||||
if let Some(source) = entity_boundary_source_on_plane(entity, plane, tolerance, None) {
|
||||
sources.insert(common.handle, source);
|
||||
}
|
||||
}
|
||||
// Finite extents of the wire geometry, so rays and infinite
|
||||
// construction lines clip to something real instead of tessellating
|
||||
// to a unit stub (see `live_segments_for_curves`).
|
||||
|
||||
+2
-1
@@ -80,7 +80,8 @@ mod parametric_solve;
|
||||
pub(crate) use boundary::{
|
||||
boundary_entities, boundary_entities_from_sources, boundary_faces, boundary_polyline_entities,
|
||||
exact_hatch_paths, hatch_boundary_rings, hatch_path_directions, hatch_path_ring,
|
||||
remap_hatch_associations, ring_source_handles, separated_hatch_path_groups, BoundarySource,
|
||||
remap_hatch_associations, ring_source_handles, separated_hatch_path_groups, sources_near_rings,
|
||||
BoundarySource,
|
||||
};
|
||||
|
||||
// Parallel tessellation free functions live in `convert::tess` (alongside the
|
||||
|
||||
Reference in new issue
Block a user