fix(plot): match PDF output to viewport

Refs #618
This commit is contained in:
Hakan Seven 2026-08-01 20:05:38 +03:00
commit 6c62daf260
6 changed files with 140 additions and 88 deletions

View file

@ -1343,8 +1343,20 @@ impl Scene {
style.4
};
let color = style.0;
let color =
crate::scene::view::render::adapt_to_bg(color, hatch_bg);
// Explicit ACI 7 solid fills inside blocks are often
// white masks. Keep the explicit white on a light
// layout; adapting it to paper turns the mask into a
// black rectangle unlike the viewport (#618).
let preserve_white_mask = dxf.is_solid
&& matches!(
dxf.common.color,
acadrust::types::Color::Index(7)
);
let color = if preserve_white_mask {
color
} else {
crate::scene::view::render::adapt_to_bg(color, hatch_bg)
};
if let Some(mut model) =
Self::hatch_model_from_dxf(&dxf, color)
{

View file

@ -210,16 +210,12 @@ impl HatchModel {
self.pattern_segments_with_dot_length(None)
}
/// Plot variant of [`Self::pattern_segments`] that renders PAT zero-length
/// dash entries at a caller-provided world length. Screen rendering snaps
/// these entries to one pixel; plot callers know the model-to-paper scale
/// and can therefore choose the equivalent physical mark without tying its
/// size to the pattern period.
pub(crate) fn pattern_segments_for_plot(
&self,
dot_length: f64,
) -> Vec<[[f64; 2]; 2]> {
self.pattern_segments_with_dot_length(Some(dot_length.max(1e-9)))
/// Plot variant of [`Self::pattern_segments`] that preserves PAT
/// zero-length dash entries as coincident endpoints. The PDF exporter can
/// then paint a round point; turning them into short segments here makes
/// point-only patterns such as AR-CONC visibly look like tiny lines.
pub(crate) fn pattern_segments_for_plot(&self) -> Vec<[[f64; 2]; 2]> {
self.pattern_segments_with_dot_length(Some(0.0))
}
fn pattern_segments_with_dot_length(
@ -436,13 +432,18 @@ impl HatchModel {
]);
}
} else if d == 0.0 && seg_t >= t0 - 1e-6 && seg_t <= t1 + 1e-6 {
let a = (seg_t - dot_len * 0.5).max(t0);
let b = (seg_t + dot_len * 0.5).min(t1);
if b > a {
segments.push([
[lx + a * cos_a, ly + a * sin_a],
[lx + b * cos_a, ly + b * sin_a],
]);
if plot_dot_length == Some(0.0) {
let point = [lx + seg_t * cos_a, ly + seg_t * sin_a];
segments.push([point, point]);
} else {
let a = (seg_t - dot_len * 0.5).max(t0);
let b = (seg_t + dot_len * 0.5).min(t1);
if b > a {
segments.push([
[lx + a * cos_a, ly + a * sin_a],
[lx + b * cos_a, ly + b * sin_a],
]);
}
}
}
seg_t += dl;

View file

@ -433,13 +433,6 @@ impl Scene {
let hatches = self.plot_hatches_for_block(model_block, Some(&frozen));
for hatch in hatches {
if matches!(&hatch.pattern, HatchPattern::Pattern(_)) {
// The GPU draws PAT zero-length dash entries as one-pixel
// dots. Convert that display pixel to model units before
// projecting so vector output keeps the same dot size
// instead of expanding it from the pattern period.
let dot_length = (25.4 / 96.0)
* self.paper_space_unit_factor()
/ viewport_scale.max(1e-9);
let mut points = Vec::new();
let mut aabb = [
f32::INFINITY,
@ -447,7 +440,7 @@ impl Scene {
f32::NEG_INFINITY,
f32::NEG_INFINITY,
];
for [a, b] in hatch.pattern_segments_for_plot(dot_length) {
for [a, b] in hatch.pattern_segments_for_plot() {
let (Some(a), Some(b)) =
(project(a[0], a[1]), project(b[0], b[1]))
else {