forked from 1b-agent/opencadstudio-upstream
fix(pdf): leave out what the export window clips away
Everything was written and only clipped at view time, so a window over a corner of a large drawing produced a near-empty page of several MB. Wires and hatches whose sheet bounds miss the window are now skipped. Fixes #1568 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
31341e8ea2
commit
440adec834
1 file changed
+76
@@ -621,6 +621,23 @@ fn append_pdf_page(
|
||||
draw_items.push((image.image.draw_depth, 1u8, sequence, DrawItem::Image(image)));
|
||||
sequence += 1;
|
||||
}
|
||||
// Everything outside the export window would only be clipped away, yet
|
||||
// still written: a window over a corner of a large drawing came out as a
|
||||
// blank page of several megabytes. Drop items whose ink misses the window.
|
||||
if let Some((cx, cy, cw, ch)) = clip {
|
||||
let marker_h = paper_h as f64 / scale.max(1e-6) as f64;
|
||||
let window = [cx as f64, cy as f64, (cx + cw) as f64, (cy + ch) as f64];
|
||||
draw_items.retain(|(_, _, _, item)| {
|
||||
let bounds = match item {
|
||||
DrawItem::WireFill(wire) | DrawItem::Wire(wire) | DrawItem::Text(wire) => {
|
||||
wire_sheet_bounds(&wire.wire, ox, oy, marker_h)
|
||||
}
|
||||
DrawItem::Hatch(hatch) => hatch_sheet_bounds(hatch, ox, oy),
|
||||
DrawItem::Image(_) => None,
|
||||
};
|
||||
bounds.is_none_or(|b| sheet_overlaps(b, window))
|
||||
});
|
||||
}
|
||||
draw_items.sort_by(|a, b| {
|
||||
a.0.total_cmp(&b.0)
|
||||
.then_with(|| a.1.cmp(&b.1))
|
||||
@@ -928,6 +945,65 @@ fn append_pdf_page(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Slack around the export window, in sheet mm, so a stroke whose centreline
|
||||
/// sits just outside still contributes its width.
|
||||
const CULL_MARGIN_MM: f64 = 10.0;
|
||||
|
||||
fn sheet_overlaps(b: [f64; 4], window: [f64; 4]) -> bool {
|
||||
b[0] <= window[2] + CULL_MARGIN_MM
|
||||
&& b[2] >= window[0] - CULL_MARGIN_MM
|
||||
&& b[1] <= window[3] + CULL_MARGIN_MM
|
||||
&& b[3] >= window[1] - CULL_MARGIN_MM
|
||||
}
|
||||
|
||||
fn grow(bounds: &mut Option<[f64; 4]>, x: f64, y: f64) {
|
||||
if !x.is_finite() || !y.is_finite() {
|
||||
return;
|
||||
}
|
||||
let b = bounds.get_or_insert([x, y, x, y]);
|
||||
b[0] = b[0].min(x);
|
||||
b[1] = b[1].min(y);
|
||||
b[2] = b[2].max(x);
|
||||
b[3] = b[3].max(y);
|
||||
}
|
||||
|
||||
/// Sheet-mm bounds of everything a wire inks (strokes, glyphs, fills).
|
||||
fn wire_sheet_bounds(wire: &WireModel, ox: f64, oy: f64, marker_h: f64) -> Option<[f64; 4]> {
|
||||
let mut bounds = None;
|
||||
for (index, point) in wire.points.iter().enumerate() {
|
||||
if point[0].is_finite() && point[1].is_finite() {
|
||||
let world = wire.point_world(index, marker_h);
|
||||
grow(&mut bounds, world.x + ox, world.y + oy);
|
||||
}
|
||||
}
|
||||
for vertex in &wire.text_verts {
|
||||
let [x, y] = glyph_world_xy(vertex);
|
||||
grow(&mut bounds, x + ox, y + oy);
|
||||
}
|
||||
for (index, point) in wire.fill_tris.iter().enumerate() {
|
||||
let low = wire.fill_tris_low.get(index).copied().unwrap_or([0.0; 3]);
|
||||
grow(
|
||||
&mut bounds,
|
||||
point[0] as f64 + low[0] as f64 + ox,
|
||||
point[1] as f64 + low[1] as f64 + oy,
|
||||
);
|
||||
}
|
||||
bounds
|
||||
}
|
||||
|
||||
/// Sheet-mm bounds of a hatch, resolved the way `emit_hatch` places it.
|
||||
fn hatch_sheet_bounds(hatch: &HatchModel, ox: f64, oy: f64) -> Option<[f64; 4]> {
|
||||
let mut bounds = None;
|
||||
for &[x, y] in hatch.boundary.iter() {
|
||||
grow(
|
||||
&mut bounds,
|
||||
x as f64 + hatch.world_origin[0] + ox,
|
||||
y as f64 + hatch.world_origin[1] + oy,
|
||||
);
|
||||
}
|
||||
bounds
|
||||
}
|
||||
|
||||
/// Build a PDF dash array (in points) from a WireModel linetype pattern.
|
||||
///
|
||||
/// `pattern` holds the linetype run lengths in paper-mm: positive = dash,
|
||||
|
||||
Reference in new issue
Block a user