layer states: restore into the active layout viewport (#1796)

Restoring a layer state while a layout viewport is active now applies it
to that viewport alone, as viewport freezes and viewport overrides for
colour, linetype, lineweight and transparency (where they differ from the
layer, or an override is already there). On/off, lock and plot have no
per-viewport form and still apply to the drawing.

Viewport colour overrides are packed as the format does (0xC3 index,
0xC2 true colour); reading them now honours that type byte, so an index
override from a file no longer shows as a near-black true colour.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-15 00:46:39 +03:00
1 parent 1d5b87d931
commit 7c3ec2666c
3 files changed
+158 -12

No files matched your search

+129 -5
View File
@@ -335,11 +335,7 @@ impl OpenCADStudio {
.map(|layer| layer.name.clone())
.collect();
self.push_undo_snapshot(i, "LAYERSTATE RESTORE");
let n = self.tabs[i]
.scene
.document
.restore_layer_state(arg)
.unwrap_or(0);
let n = self.restore_layer_state_here(i, arg);
self.tabs[i].active_layer = self.tabs[i]
.scene
.document
@@ -1145,3 +1141,131 @@ impl OpenCADStudio {
Task::none()
}
}
impl OpenCADStudio {
/// Restore the saved layer state `name`, returning how many layers it
/// touched. Inside a layout viewport the state applies to that viewport
/// alone: its frozen layers become the viewport's freezes, and colour,
/// linetype, lineweight and transparency become viewport overrides where
/// they differ from the layer. On / off, lock and plot have no
/// per-viewport form and apply to the drawing.
pub(in crate::app) fn restore_layer_state_here(&mut self, i: usize, name: &str) -> usize {
use codec::layer_state::LayerStateMask as Mask;
use codec::objects::{KnownXRecordKind as Kind, XRecordValue as Value};
use codec::EntityType;
let scene = &mut self.tabs[i].scene;
let viewport = scene.active_viewport.filter(|handle| {
scene.current_layout != "Model"
&& matches!(scene.document.get_entity(*handle), Some(EntityType::Viewport(_)))
});
let document = &mut scene.document;
let Some(viewport) = viewport else {
return document.restore_layer_state(name).unwrap_or(0);
};
let Some(state) = document.layer_state(name) else {
return 0;
};
let mut restored = 0;
let (mut freeze, mut thaw) = (Vec::new(), Vec::new());
for saved in &state.layers {
let found = document
.layers
.iter()
.find(|layer| !saved.layer_handle.is_null() && layer.handle == saved.layer_handle)
.or_else(|| document.layers.get(&saved.layer_name))
.map(|layer| layer.name.clone());
let Some(layer_name) = found else {
continue;
};
let line_type_handle = document
.line_types
.iter()
.find(|line_type| line_type.name.eq_ignore_ascii_case(&saved.line_type))
.map(|line_type| line_type.handle);
let Some(layer) = document.layers.get_mut(&layer_name) else {
continue;
};
restored += 1;
if state.mask.contains(Mask::ON) {
layer.flags.off = saved.off;
}
if state.mask.contains(Mask::LOCKED) {
layer.flags.locked = saved.locked;
}
if state.mask.contains(Mask::PLOT) {
layer.is_plottable = saved.plottable;
}
if state.mask.contains(Mask::NEW_VIEWPORT) {
layer.flags.frozen_in_new_viewport = saved.new_viewport_frozen;
}
let layer = layer.clone();
if state.mask.contains(Mask::FROZEN) {
if saved.frozen { &mut freeze } else { &mut thaw }.push(layer.handle);
}
// (kind, saved value, whether it differs from the layer's own)
let mut overrides: Vec<(Kind, Option<Value>, bool)> = Vec::new();
if state.mask.contains(Mask::COLOR) {
let packed = crate::scene::view::render::pack_layer_override_color(&saved.color);
overrides.push((
Kind::LayerViewportColorOverride,
packed.map(Value::Int32),
saved.color != layer.color,
));
}
if state.mask.contains(Mask::LINE_TYPE) {
overrides.push((
Kind::LayerViewportLinetypeOverride,
line_type_handle.map(Value::Handle),
!saved.line_type.eq_ignore_ascii_case(&layer.line_type),
));
}
if state.mask.contains(Mask::LINE_WEIGHT) {
overrides.push((
Kind::LayerViewportLineweightOverride,
Some(Value::Int32(saved.line_weight.value() as i32)),
saved.line_weight != layer.line_weight,
));
}
if let (true, Some(transparency)) =
(state.mask.contains(Mask::TRANSPARENCY), saved.transparency)
{
// The opacity byte under the "by alpha" method, as the format packs it.
let packed = 0x0200_0000u32 | (255 - transparency.alpha()) as u32;
overrides.push((
Kind::LayerViewportAlphaOverride,
Some(Value::Int32(packed as i32)),
transparency != layer.transparency,
));
}
// An override is written where the state differs from the layer,
// and where one already exists for this viewport, so it cannot
// keep an older value.
let overrides: Vec<(Kind, Value)> = overrides
.into_iter()
.filter_map(|(kind, value, differs)| {
let existing = document
.layer_viewport_overrides(layer.handle, kind)
.iter()
.any(|(handle, _)| *handle == viewport);
value.filter(|_| differs || existing).map(|value| (kind, value))
})
.collect();
for (kind, value) in overrides {
document.set_layer_viewport_override(layer.handle, kind, viewport, value);
}
}
if let Some(EntityType::Viewport(vp)) = document.get_entity_mut(viewport) {
vp.frozen_layers.retain(|handle| !thaw.contains(handle));
for handle in freeze {
if !vp.frozen_layers.contains(&handle) {
vp.frozen_layers.push(handle);
}
}
}
scene.bump_geometry();
let doc_layers = self.tabs[i].scene.document.layers.clone();
let vp_info = self.tabs[i].scene.viewport_list();
self.tabs[i].layers.sync_with_viewports(&doc_layers, vp_info);
restored
}
}
+1 -5
View File
@@ -3799,11 +3799,7 @@ impl OpenCADStudio {
.map(|layer| layer.name.clone())
.collect();
self.push_undo_snapshot(i, "LAYERSTATE RESTORE");
let restored = self.tabs[i]
.scene
.document
.restore_layer_state(&name)
.unwrap_or(0);
let restored = self.restore_layer_state_here(i, &name);
let active = self.tabs[i]
.scene
.document
+28 -2
View File
@@ -3433,6 +3433,32 @@ pub(in crate::scene) fn render_style_for(
render_style_for_viewport(document, e, None)
}
/// A viewport layer colour override as the format packs it (code 420): the
/// top byte says what follows — 0xC3 an index colour in the low byte, 0xC2 a
/// 24-bit true colour, 0xC0 / 0xC1 ByLayer / ByBlock. A bare 24-bit value is
/// read as true colour.
pub(crate) fn layer_override_color(value: i32) -> AcadColor {
match (value as u32) >> 24 {
0xC0 => AcadColor::ByLayer,
0xC1 => AcadColor::ByBlock,
0xC3 => AcadColor::Index((value & 0xFF) as u8),
_ => AcadColor::from_true_color_value(value),
}
}
/// [`layer_override_color`] the other way round; `None` for a colour an
/// override cannot hold.
pub(crate) fn pack_layer_override_color(color: &AcadColor) -> Option<i32> {
let packed = match color {
AcadColor::Index(index) => 0xC300_0000u32 | *index as u32,
AcadColor::Rgb { r, g, b } => {
0xC200_0000u32 | (*r as u32) << 16 | (*g as u32) << 8 | *b as u32
}
_ => return None,
};
Some(packed as i32)
}
fn viewport_override(
document: &CadDocument,
layer_name: &str,
@@ -3473,7 +3499,7 @@ pub(crate) fn render_style_for_common_viewport(
codec::objects::KnownXRecordKind::LayerViewportColorOverride,
)
.and_then(|value| value.as_i32())
.map(AcadColor::from_true_color_value)
.map(layer_override_color)
})
.flatten();
let resolved = if *ec == AcadColor::ByLayer {
@@ -3622,7 +3648,7 @@ pub(crate) fn layer_render_style_viewport(
codec::objects::KnownXRecordKind::LayerViewportColorOverride,
)
.and_then(|value| value.as_i32())
.map(AcadColor::from_true_color_value)
.map(layer_override_color)
} else {
None
};