fix(layout): complete paperspace integration

Apply each layout's embedded page setup and plot style consistently across display, PDF, printing, and multi-layout output.\n\nRefs #844
This commit is contained in:
Hakan Seven committed 2026-08-26 08:58:44 +03:00
1 parent 9c8f841f71
commit 8c61d89b1e
18 files changed
+1393 -373

No files matched your search

+181 -19
View File
@@ -1614,6 +1614,15 @@ pub struct Scene {
/// movement scanned the whole document for wipeouts and recreated the same
/// Arcs, making Paper frame construction CPU-bound on large drawings.
paper_sheet_render_cache: RefCell<HashMap<String, PaperSheetRenderCache>>,
display_plot_style_cache:
RefCell<HashMap<(String, String), Option<Arc<crate::io::plot_style::PlotStyleTable>>>>,
styled_wire_cache: RefCell<
HashMap<(u64, String), (u64, Arc<Vec<WireModel>>)>,
>,
styled_hatch_cache:
RefCell<HashMap<(u64, usize, usize, String, u32), Arc<Vec<HatchModel>>>>,
styled_wire_fill_cache:
RefCell<HashMap<(u64, String, u32), Arc<Vec<HatchModel>>>>,
/// Per-viewport projected wire cache for paper-space content viewports.
/// Stores projected + clipped wires in paper-space coordinates.
/// Maps vp_handle → (geometry_epoch, Vec<WireModel>).
@@ -1880,6 +1889,10 @@ impl Scene {
paper_sheet_cache: RefCell::new(HashMap::default()),
paper_viewport_cache: RefCell::new(HashMap::default()),
paper_sheet_render_cache: RefCell::new(HashMap::default()),
display_plot_style_cache: RefCell::new(HashMap::default()),
styled_wire_cache: RefCell::new(HashMap::default()),
styled_hatch_cache: RefCell::new(HashMap::default()),
styled_wire_fill_cache: RefCell::new(HashMap::default()),
paper_projected_cache: RefCell::new(HashMap::default()),
current_layout: "Model".to_string(),
block_edit_block: None,
@@ -2878,7 +2891,9 @@ impl Scene {
/// active layout or its background are dropped.
pub fn set_current_layout(&mut self, name: String) {
if self.current_layout != name {
self.persist_current_layout_state();
self.current_layout = name;
self.load_current_layout_state();
self.sync_active_space_to_document();
self.recolor_meshes();
*self.wire_cache.borrow_mut() = None;
@@ -2888,6 +2903,91 @@ impl Scene {
}
}
pub fn load_current_layout_state(&mut self) {
self.display_plot_style_cache
.borrow_mut()
.retain(|(layout, _), _| layout != &self.current_layout);
self.styled_wire_cache.borrow_mut().clear();
self.styled_hatch_cache.borrow_mut().clear();
self.styled_wire_fill_cache.borrow_mut().clear();
if self.current_layout == "Model" {
return;
}
if let Some((flags, insertion_base, min_extents, max_extents, min_limits, max_limits)) =
self.document.objects.values().find_map(|object| {
let ObjectType::Layout(layout) = object else {
return None;
};
(layout.name == self.current_layout).then_some((
layout.flags,
layout.insertion_base,
layout.min_extents,
layout.max_extents,
layout.min_limits,
layout.max_limits,
))
}) {
self.document.header.paper_space_linetype_scaling = flags & 1 != 0;
self.document.header.paper_space_limit_check = flags & 2 != 0;
self.document.header.paper_space_insertion_base = acadrust::types::Vector3::new(
insertion_base.0,
insertion_base.1,
insertion_base.2,
);
self.document.header.paper_space_extents_min = acadrust::types::Vector3::new(
min_extents.0,
min_extents.1,
min_extents.2,
);
self.document.header.paper_space_extents_max = acadrust::types::Vector3::new(
max_extents.0,
max_extents.1,
max_extents.2,
);
self.document.header.paper_space_limits_min =
acadrust::types::Vector2::new(min_limits.0, min_limits.1);
self.document.header.paper_space_limits_max =
acadrust::types::Vector2::new(max_limits.0, max_limits.1);
}
}
pub fn persist_current_layout_state(&mut self) {
if self.current_layout == "Model" {
return;
}
let header = &self.document.header;
let psltscale = header.paper_space_linetype_scaling;
let plimcheck = header.paper_space_limit_check;
let insertion_base = header.paper_space_insertion_base;
let min_extents = header.paper_space_extents_min;
let max_extents = header.paper_space_extents_max;
let min_limits = header.paper_space_limits_min;
let max_limits = header.paper_space_limits_max;
for object in self.document.objects.values_mut() {
let ObjectType::Layout(layout) = object else {
continue;
};
if layout.name == self.current_layout {
layout.flags = if psltscale {
layout.flags | 1
} else {
layout.flags & !1
};
layout.flags = if plimcheck {
layout.flags | 2
} else {
layout.flags & !2
};
layout.insertion_base = (insertion_base.x, insertion_base.y, insertion_base.z);
layout.min_extents = (min_extents.x, min_extents.y, min_extents.z);
layout.max_extents = (max_extents.x, max_extents.y, max_extents.z);
layout.min_limits = (min_limits.x, min_limits.y);
layout.max_limits = (max_limits.x, max_limits.y);
break;
}
}
}
/// Mirror the active space (Model tile-mode vs a paper layout) into the
/// document's persisted settings so it round-trips on save: the `$TILEMODE`
/// header (`show_model_space`) and the `CTAB` current-tab variable. The
@@ -3305,32 +3405,17 @@ impl Scene {
Some(wire)
}
/// The effective plot settings for the current layout: a standalone
/// PlotSettings page setup if one exists, otherwise the settings embedded in
/// the LAYOUT object (paper size, margins, origin, rotation, scale). Loaded
/// The fallback preserves rotation, origin and scale from loaded files.
/// The effective plot settings embedded in the current layout.
pub fn effective_plot_settings(&self) -> Option<acadrust::objects::PlotSettings> {
self.plot_settings_for(&self.current_layout)
}
/// Plot settings for a specific layout by name: its standalone
/// `PlotSettings` object if one exists, else synthesized from the `Layout`
/// object's embedded fields.
/// Plot settings embedded in a specific layout.
pub fn plot_settings_for(&self, name: &str) -> Option<acadrust::objects::PlotSettings> {
use acadrust::objects::{
ObjectType, PaperMargin, PlotPaperUnits, PlotRotation, PlotSettings, PlotType,
PlotWindow, ScaledType,
PlotWindow, ScaledType, ShadePlotMode, ShadePlotResolutionLevel,
};
if let Some(ps) = self.document.objects.values().find_map(|o| {
if let ObjectType::PlotSettings(ps) = o {
if ps.page_name.as_str() == name {
return Some(ps.clone());
}
}
None
}) {
return Some(ps);
}
self.document.objects.values().find_map(|o| {
let ObjectType::Layout(l) = o else {
return None;
@@ -3338,10 +3423,13 @@ impl Scene {
if l.name.as_str() != name {
return None;
}
let mut ps = PlotSettings::new(l.name.clone());
let mut ps = PlotSettings::new(l.plot_page_name.clone());
ps.printer_name = l.plot_printer_name.clone();
ps.paper_width = l.paper_width;
ps.paper_height = l.paper_height;
ps.paper_size = l.paper_size.clone();
ps.plot_view_name = l.plot_view_name.clone();
ps.current_style_sheet = l.plot_style_sheet.clone();
ps.margins = PaperMargin::new(
l.plot_margin_left,
l.plot_margin_bottom,
@@ -3362,10 +3450,84 @@ impl Scene {
ps.scale_type = ScaledType::from_code(l.plot_scale_type);
ps.scale_numerator = l.plot_scale_numerator;
ps.scale_denominator = l.plot_scale_denominator;
ps.flags = l.plot_flags;
ps.standard_scale_factor = l.plot_scale_factor;
ps.paper_image_origin_x = l.paper_image_origin_x;
ps.paper_image_origin_y = l.paper_image_origin_y;
ps.shade_plot_mode = ShadePlotMode::from_code(l.shade_plot_mode);
ps.shade_plot_resolution =
ShadePlotResolutionLevel::from_code(l.shade_plot_resolution);
ps.shade_plot_dpi = l.shade_plot_dpi;
ps.plot_view_handle = l.plot_view_handle;
ps.visual_style_handle = l.visual_style_handle;
Some(ps)
})
}
/// Replace the plot-settings portion embedded in one layout.
pub fn set_layout_plot_settings(
&mut self,
name: &str,
ps: &acadrust::objects::PlotSettings,
) -> bool {
let Some(layout) = self.document.objects.values_mut().find_map(|object| {
let ObjectType::Layout(layout) = object else {
return None;
};
(layout.name == name).then_some(layout)
}) else {
return false;
};
layout.plot_page_name = ps.page_name.clone();
layout.plot_printer_name = ps.printer_name.clone();
layout.paper_size = ps.paper_size.clone();
layout.plot_view_name = ps.plot_view_name.clone();
layout.plot_style_sheet = ps.current_style_sheet.clone();
layout.plot_margin_left = ps.margins.left;
layout.plot_margin_bottom = ps.margins.bottom;
layout.plot_margin_right = ps.margins.right;
layout.plot_margin_top = ps.margins.top;
layout.paper_width = ps.paper_width;
layout.paper_height = ps.paper_height;
layout.plot_origin_x = ps.origin_x;
layout.plot_origin_y = ps.origin_y;
layout.plot_window_min_x = ps.plot_window.lower_left_x;
layout.plot_window_min_y = ps.plot_window.lower_left_y;
layout.plot_window_max_x = ps.plot_window.upper_right_x;
layout.plot_window_max_y = ps.plot_window.upper_right_y;
layout.plot_scale_numerator = ps.scale_numerator;
layout.plot_scale_denominator = ps.scale_denominator;
layout.plot_paper_units = ps.paper_units.to_code();
layout.plot_rotation = ps.rotation.to_code();
layout.plot_type = ps.plot_type.to_code();
layout.plot_scale_type = ps.scale_type.to_code();
layout.shade_plot_mode = ps.shade_plot_mode.to_code();
layout.shade_plot_resolution = ps.shade_plot_resolution.to_code();
layout.shade_plot_dpi = ps.shade_plot_dpi;
layout.plot_flags = ps.flags;
layout.plot_scale_factor = ps.standard_scale_factor;
layout.paper_image_origin_x = ps.paper_image_origin_x;
layout.paper_image_origin_y = ps.paper_image_origin_y;
layout.plot_view_handle = ps.plot_view_handle;
layout.visual_style_handle = ps.visual_style_handle;
layout.raw_plot_settings_codes = None;
self.display_plot_style_cache
.borrow_mut()
.retain(|(layout_name, _), _| layout_name != name);
self.styled_wire_cache.borrow_mut().clear();
self.styled_hatch_cache.borrow_mut().clear();
self.styled_wire_fill_cache.borrow_mut().clear();
true
}
pub fn invalidate_display_plot_style(&self) {
self.display_plot_style_cache.borrow_mut().clear();
self.styled_wire_cache.borrow_mut().clear();
self.styled_hatch_cache.borrow_mut().clear();
self.styled_wire_fill_cache.borrow_mut().clear();
}
/// PlotSettings store the paper size and plot margins in millimetres, but a
/// layout's paper space is laid out in its own units — its viewports,
/// drawing limits and the sheet the user sees can be in inches, millimetres,