fix(paper): size inch paper-space sheets in their own units

Some layouts lay out paper space in inches (viewports and drawing limits
in inch units), but paper_width/height and plot margins are always
millimetres. paper_limits() drew the sheet at the mm size, 25.4x too
large, so the drawing shrank into the corner and the printable-area
dashes ran 25.4x too long.

Infer the mm->paper-space unit factor per layout from the limits/paper
ratio (plot_paper_units records the plot output unit and can disagree,
so it is not used). Scale the sheet, the printable-area margins and its
dash pattern by it. PDF export recovers physical mm by dividing back,
leaving millimetre layouts byte-identical.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-20 17:13:01 +03:00
1 parent b3016ca892
commit 7d68eb0f10
2 files changed
+85 -5

No files matched your search

+10 -2
View File
@@ -1165,7 +1165,11 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
// Determine paper size and drawing offset.
let (paper_w, paper_h, mut draw_ox, mut draw_oy, rotation_deg) =
if let Some(((x0, y0), (x1, y1))) = scene.paper_limits() {
let (pw, ph) = (x1 - x0, y1 - y0);
// `paper_limits()` is in paper-space units; the PDF page is
// physical millimetres, so scale the sheet size back to mm via
// the layout's unit factor (identity for millimetre layouts).
let uf = scene.paper_space_unit_factor();
let (pw, ph) = ((x1 - x0) / uf, (y1 - y0) / uf);
// If PlotSettings says Extents, use model space extents instead.
let use_extents = ps_snap
@@ -1379,7 +1383,11 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
let ps_snap = scene.effective_plot_settings();
let (paper_w, paper_h, draw_ox, draw_oy, rotation_deg) =
if let Some(((x0, y0), (x1, y1))) = scene.paper_limits() {
let (pw, ph) = (x1 - x0, y1 - y0);
// `paper_limits()` is in paper-space units; the PDF page is
// physical millimetres, so scale the sheet size back to mm via
// the layout's unit factor (identity for millimetre layouts).
let uf = scene.paper_space_unit_factor();
let (pw, ph) = ((x1 - x0) / uf, (y1 - y0) / uf);
let use_extents = ps_snap
.as_ref()
.map(|ps| matches!(ps.plot_type, PlotType::Extents))
+75 -3
View File
@@ -2063,6 +2063,12 @@ impl Scene {
2 => (right, top, left, bottom),
_ => (left, bottom, right, top),
};
// Plot margins are millimetres like the paper size; scale them into the
// layout's paper-space units so the inset matches the (already scaled)
// sheet rect. Without this an inch paper space insets an ~8-inch sheet
// by ~6 "inches" of margin and the printable rect collapses.
let f = self.paper_space_unit_factor();
let (ml, mb, mr, mt) = (ml * f, mb * f, mr * f, mt * f);
// Nothing to show when there are no margins (printable area == sheet).
if ml <= 0.0 && mb <= 0.0 && mr <= 0.0 && mt <= 0.0 {
return None;
@@ -2084,9 +2090,12 @@ impl Scene {
[0.5, 0.5, 0.5, 1.0],
false,
);
// Dashed: 4 mm dash, 3 mm gap.
wire.pattern_length = 7.0;
wire.pattern = [4.0, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
// Dashed: 4 mm dash, 3 mm gap. The dash lengths are millimetres, so
// scale them into the layout's paper-space units too (`f`) — otherwise
// an inch paper space draws 4-"inch" dashes (25.4× too long).
let ff = f as f32;
wire.pattern_length = 7.0 * ff;
wire.pattern = [4.0 * ff, -3.0 * ff, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
Some(wire)
}
@@ -2150,6 +2159,61 @@ impl Scene {
})
}
/// PlotSettings store the paper size and plot margins in millimetres, but a
/// layout's paper space may be laid out in inches — its viewports, drawing
/// limits and the sheet the user sees are all in inch units. Return the
/// factor that converts those millimetre plot-settings values into the
/// active layout's paper-space units: `1.0` for a millimetre layout,
/// `1/25.4` for an inch layout.
///
/// The unit is inferred from the layout's drawing limits — the only
/// per-layout tie between paper-space coordinates and the physical sheet.
/// `plot_paper_units` (code 72) records the plot *output* unit and can
/// disagree with the paper-space geometry (a layout can carry inch output
/// units over a millimetre paper space and vice-versa), so it is not used.
pub fn paper_space_unit_factor(&self) -> f64 {
if self.current_layout == "Model" {
return 1.0;
}
self.document
.objects
.values()
.find_map(|obj| match obj {
ObjectType::Layout(l) if l.name == self.current_layout => {
Some(Self::layout_unit_factor(l))
}
_ => None,
})
.unwrap_or(1.0)
}
/// Millimetre → paper-space-unit factor for a specific layout (see
/// [`Scene::paper_space_unit_factor`]).
fn layout_unit_factor(l: &acadrust::objects::Layout) -> f64 {
if l.paper_width <= 1e-6 || l.paper_height <= 1e-6 {
return 1.0;
}
// Match the rotation swap `paper_limits` applies so the span/paper axes
// line up.
let (pw, ph) = if l.plot_rotation == 1 || l.plot_rotation == 3 {
(l.paper_height, l.paper_width)
} else {
(l.paper_width, l.paper_height)
};
let span_w = (l.max_limits.0 - l.min_limits.0).abs();
let span_h = (l.max_limits.1 - l.min_limits.1).abs();
// The paper-space span ≈ physical_mm × factor. Solve factor = span / mm
// on whichever axis carries a usable span, then snap to inch when it
// lands near 1/25.4; otherwise treat the layout as millimetre (default).
let ratio = [(span_w, pw), (span_h, ph)]
.into_iter()
.find_map(|(s, p)| (s > 1e-6 && p > 1e-6).then_some(s / p));
match ratio {
Some(r) if (r - 1.0 / 25.4).abs() < 0.15 / 25.4 => 1.0 / 25.4,
_ => 1.0,
}
}
pub fn paper_limits(&self) -> Option<((f64, f64), (f64, f64))> {
if self.current_layout == "Model" {
return None;
@@ -2173,6 +2237,14 @@ impl Scene {
} else {
(l.paper_width, l.paper_height)
};
// paper_width/height are millimetres; scale them into
// the layout's paper-space units so the sheet lands in
// the same coordinate system as the viewports drawn on
// it. An inch-based paper space would otherwise get a
// 25.4× oversized sheet, shrinking the content into the
// corner.
let f = Self::layout_unit_factor(l);
let (pw, ph) = (pw * f, ph * f);
let ox = l.min_limits.0.min(0.0);
let oy = l.min_limits.1.min(0.0);
return Some(((ox, oy), (ox + pw, oy + ph)));