Merge PR #374: pick innermost hatch boundary and detect nested holes
HATCH/GRADIENT/BOUNDARY pick-inside now resolves the click against every enclosing outline, fills the smallest-area one, and emits inner outlines as NaN-separated holes. DXF export writes one boundary path per ring. Co-authored-by: KarimJerbi <KarimJerbi@users.noreply.github.com>
This commit is contained in:
commit
b4a25701de
3 changed files with 280 additions and 41 deletions
|
|
@ -1651,25 +1651,59 @@ impl Scene {
|
|||
// render-side `boundary`, the points arrive in local render space
|
||||
// (world_offset already subtracted), so add `world_origin` back —
|
||||
// otherwise the boundary wire lands `world_offset` away from the fill.
|
||||
let verts: Vec<Vector2> = if let Some(wcs) = &model.boundary_wcs {
|
||||
wcs.iter()
|
||||
.filter(|v| v[0].is_finite() && v[1].is_finite())
|
||||
.map(|&[x, y]| Vector2::new(x, y))
|
||||
.collect()
|
||||
} else {
|
||||
// Build one DXF boundary path per ring. The command layer separates
|
||||
// the outer boundary from its holes with NaN sentinels in
|
||||
// `boundary_wcs`; split on those so nested hatches (e.g. a small
|
||||
// rectangle inside a big one) persist with real holes instead of a
|
||||
// single self-intersecting polyline. Only the FIRST ring carries the
|
||||
// external / outermost flags — every later ring is a hole and must not
|
||||
// be flagged external, or DXF/DWG consumers treat the inner loop as
|
||||
// another outer island rather than a hole.
|
||||
if let Some(wcs) = &model.boundary_wcs {
|
||||
let mut ring: Vec<Vector2> = Vec::new();
|
||||
let mut first = true;
|
||||
let mut push_ring = |r: &mut Vec<Vector2>, is_outer: bool| {
|
||||
if !r.is_empty() {
|
||||
let edge = PolylineEdge::new(std::mem::take(r), true);
|
||||
let mut path = if is_outer {
|
||||
let mut p = BoundaryPath::external();
|
||||
p.flags = acadrust::entities::hatch::BoundaryPathFlags::from_bits(
|
||||
p.flags.bits()
|
||||
| acadrust::entities::hatch::BoundaryPathFlags::OUTERMOST.bits(),
|
||||
);
|
||||
p
|
||||
} else {
|
||||
BoundaryPath::new()
|
||||
};
|
||||
path.add_edge(BoundaryEdge::Polyline(edge));
|
||||
dxf.paths.push(path);
|
||||
}
|
||||
};
|
||||
for &[x, y] in wcs.iter() {
|
||||
if x.is_finite() && y.is_finite() {
|
||||
ring.push(Vector2::new(x, y));
|
||||
} else {
|
||||
let is_outer = first;
|
||||
first = false;
|
||||
push_ring(&mut ring, is_outer);
|
||||
}
|
||||
}
|
||||
push_ring(&mut ring, first);
|
||||
}
|
||||
if dxf.paths.is_empty() {
|
||||
let wx = model.world_origin[0];
|
||||
let wy = model.world_origin[1];
|
||||
model
|
||||
let verts: Vec<Vector2> = model
|
||||
.boundary
|
||||
.iter()
|
||||
.filter(|v| v[0].is_finite() && v[1].is_finite())
|
||||
.map(|&[x, y]| Vector2::new(x as f64 + wx, y as f64 + wy))
|
||||
.collect()
|
||||
};
|
||||
let edge = PolylineEdge::new(verts, true);
|
||||
let mut path = BoundaryPath::external();
|
||||
path.add_edge(BoundaryEdge::Polyline(edge));
|
||||
dxf.paths.push(path);
|
||||
.collect();
|
||||
let edge = PolylineEdge::new(verts, true);
|
||||
let mut path = BoundaryPath::external();
|
||||
path.add_edge(BoundaryEdge::Polyline(edge));
|
||||
dxf.paths.push(path);
|
||||
}
|
||||
if let Some(entry) = crate::scene::model::hatch_patterns::find(&model.name) {
|
||||
dxf.pattern = crate::scene::model::hatch_patterns::build_dxf_pattern(entry);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue