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 <noreply@anthropic.com>
This commit is contained in:
parent
b3016ca892
commit
7d68eb0f10
2 changed files with 85 additions and 5 deletions
|
|
@ -1165,7 +1165,11 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
||||||
// Determine paper size and drawing offset.
|
// Determine paper size and drawing offset.
|
||||||
let (paper_w, paper_h, mut draw_ox, mut draw_oy, rotation_deg) =
|
let (paper_w, paper_h, mut draw_ox, mut draw_oy, rotation_deg) =
|
||||||
if let Some(((x0, y0), (x1, y1))) = scene.paper_limits() {
|
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.
|
// If PlotSettings says Extents, use model space extents instead.
|
||||||
let use_extents = ps_snap
|
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 ps_snap = scene.effective_plot_settings();
|
||||||
let (paper_w, paper_h, draw_ox, draw_oy, rotation_deg) =
|
let (paper_w, paper_h, draw_ox, draw_oy, rotation_deg) =
|
||||||
if let Some(((x0, y0), (x1, y1))) = scene.paper_limits() {
|
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
|
let use_extents = ps_snap
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|ps| matches!(ps.plot_type, PlotType::Extents))
|
.map(|ps| matches!(ps.plot_type, PlotType::Extents))
|
||||||
|
|
|
||||||
|
|
@ -2063,6 +2063,12 @@ impl Scene {
|
||||||
2 => (right, top, left, bottom),
|
2 => (right, top, left, bottom),
|
||||||
_ => (left, bottom, right, top),
|
_ => (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).
|
// Nothing to show when there are no margins (printable area == sheet).
|
||||||
if ml <= 0.0 && mb <= 0.0 && mr <= 0.0 && mt <= 0.0 {
|
if ml <= 0.0 && mb <= 0.0 && mr <= 0.0 && mt <= 0.0 {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -2084,9 +2090,12 @@ impl Scene {
|
||||||
[0.5, 0.5, 0.5, 1.0],
|
[0.5, 0.5, 0.5, 1.0],
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
// Dashed: 4 mm dash, 3 mm gap.
|
// Dashed: 4 mm dash, 3 mm gap. The dash lengths are millimetres, so
|
||||||
wire.pattern_length = 7.0;
|
// scale them into the layout's paper-space units too (`f`) — otherwise
|
||||||
wire.pattern = [4.0, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
|
// 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)
|
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))> {
|
pub fn paper_limits(&self) -> Option<((f64, f64), (f64, f64))> {
|
||||||
if self.current_layout == "Model" {
|
if self.current_layout == "Model" {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -2173,6 +2237,14 @@ impl Scene {
|
||||||
} else {
|
} else {
|
||||||
(l.paper_width, l.paper_height)
|
(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 ox = l.min_limits.0.min(0.0);
|
||||||
let oy = l.min_limits.1.min(0.0);
|
let oy = l.min_limits.1.min(0.0);
|
||||||
return Some(((ox, oy), (ox + pw, oy + ph)));
|
return Some(((ox, oy), (ox + pw, oy + ph)));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue