fix(selection): separate transient visibility
Keep object isolation out of persisted entity visibility and clear stale grips across block-editor transitions. Refs #512 Refs #517
This commit is contained in:
parent
7f41e1666f
commit
d5be78a187
10 changed files with 295 additions and 106 deletions
|
|
@ -444,12 +444,22 @@ impl OpenCADStudio {
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
|
// The first click of a double-click selects the model-space
|
||||||
|
// INSERT and populates the cached grip overlay. Clearing only
|
||||||
|
// Scene::selected leaves that INSERT's grips active inside the
|
||||||
|
// block editor, where dragging one moves the outer reference
|
||||||
|
// instead of block-local geometry (#517).
|
||||||
|
self.tabs[i].active_grip = None;
|
||||||
|
self.grip_hover = None;
|
||||||
|
self.grip_popup = None;
|
||||||
|
self.visibility_popup = None;
|
||||||
self.tabs[i].scene.bump_geometry();
|
self.tabs[i].scene.bump_geometry();
|
||||||
// Frame the camera on the block's own geometry (block-local, near
|
// Frame the camera on the block's own geometry (block-local, near
|
||||||
// origin) — fit_all() goes through current_layout_block_handle so
|
// origin) — fit_all() goes through current_layout_block_handle so
|
||||||
// it already scopes to the edited block. Without this the view
|
// it already scopes to the edited block. Without this the view
|
||||||
// stays wherever model/paper space was. (#261)
|
// stays wherever model/paper space was. (#261)
|
||||||
self.tabs[i].scene.fit_all();
|
self.tabs[i].scene.fit_all();
|
||||||
|
self.refresh_properties();
|
||||||
self.tabs[i].active_cmd = None;
|
self.tabs[i].active_cmd = None;
|
||||||
self.tabs[i].dirty = true;
|
self.tabs[i].dirty = true;
|
||||||
self.command_line.push_info(&format!(
|
self.command_line.push_info(&format!(
|
||||||
|
|
@ -470,11 +480,16 @@ impl OpenCADStudio {
|
||||||
// Edits are live on the block record — just leave the block space.
|
// Edits are live on the block record — just leave the block space.
|
||||||
self.tabs[i].scene.block_edit_block = None;
|
self.tabs[i].scene.block_edit_block = None;
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
|
self.tabs[i].active_grip = None;
|
||||||
|
self.grip_hover = None;
|
||||||
|
self.grip_popup = None;
|
||||||
|
self.visibility_popup = None;
|
||||||
self.tabs[i].scene.set_current_layout(session.return_layout.clone());
|
self.tabs[i].scene.set_current_layout(session.return_layout.clone());
|
||||||
// Return the view to where it was when BEDIT began (#425).
|
// Return the view to where it was when BEDIT began (#425).
|
||||||
*self.tabs[i].scene.camera.borrow_mut() = session.return_camera.clone();
|
*self.tabs[i].scene.camera.borrow_mut() = session.return_camera.clone();
|
||||||
self.tabs[i].scene.camera_generation += 1;
|
self.tabs[i].scene.camera_generation += 1;
|
||||||
self.tabs[i].scene.rebuild_derived_caches();
|
self.tabs[i].scene.rebuild_derived_caches();
|
||||||
|
self.refresh_properties();
|
||||||
self.tabs[i].dirty = true;
|
self.tabs[i].dirty = true;
|
||||||
self.command_line.push_output(&format!(
|
self.command_line.push_output(&format!(
|
||||||
"BEDIT: Block \"{}\" saved. All references updated.",
|
"BEDIT: Block \"{}\" saved. All references updated.",
|
||||||
|
|
@ -521,11 +536,16 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
self.tabs[i].scene.block_edit_block = None;
|
self.tabs[i].scene.block_edit_block = None;
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
|
self.tabs[i].active_grip = None;
|
||||||
|
self.grip_hover = None;
|
||||||
|
self.grip_popup = None;
|
||||||
|
self.visibility_popup = None;
|
||||||
self.tabs[i].scene.set_current_layout(session.return_layout.clone());
|
self.tabs[i].scene.set_current_layout(session.return_layout.clone());
|
||||||
// Return the view to where it was when BEDIT began (#425).
|
// Return the view to where it was when BEDIT began (#425).
|
||||||
*self.tabs[i].scene.camera.borrow_mut() = session.return_camera.clone();
|
*self.tabs[i].scene.camera.borrow_mut() = session.return_camera.clone();
|
||||||
self.tabs[i].scene.camera_generation += 1;
|
self.tabs[i].scene.camera_generation += 1;
|
||||||
self.tabs[i].scene.rebuild_derived_caches();
|
self.tabs[i].scene.rebuild_derived_caches();
|
||||||
|
self.refresh_properties();
|
||||||
self.tabs[i].dirty = true;
|
self.tabs[i].dirty = true;
|
||||||
self.command_line.push_output(&format!(
|
self.command_line.push_output(&format!(
|
||||||
"BEDIT: Block \"{}\" edit discarded.",
|
"BEDIT: Block \"{}\" edit discarded.",
|
||||||
|
|
|
||||||
|
|
@ -491,9 +491,16 @@ impl OpenCADStudio {
|
||||||
.push_error("ISOLATEOBJECTS: select the objects to isolate first.");
|
.push_error("ISOLATEOBJECTS: select the objects to isolate first.");
|
||||||
} else {
|
} else {
|
||||||
let n = self.tabs[i].scene.selected.len();
|
let n = self.tabs[i].scene.selected.len();
|
||||||
self.push_undo_snapshot(i, "ISOLATEOBJECTS");
|
let before = self.tabs[i].scene.object_isolation.clone();
|
||||||
|
let selected_before =
|
||||||
|
self.tabs[i].scene.selected.iter().copied().collect();
|
||||||
self.tabs[i].scene.isolate_selected();
|
self.tabs[i].scene.isolate_selected();
|
||||||
self.tabs[i].dirty = true;
|
self.push_object_visibility_history(
|
||||||
|
i,
|
||||||
|
"ISOLATEOBJECTS",
|
||||||
|
before,
|
||||||
|
selected_before,
|
||||||
|
);
|
||||||
self.command_line.push_info(&format!(
|
self.command_line.push_info(&format!(
|
||||||
"Isolated {n} object(s). UNISOLATEOBJECTS to restore."
|
"Isolated {n} object(s). UNISOLATEOBJECTS to restore."
|
||||||
));
|
));
|
||||||
|
|
@ -507,9 +514,17 @@ impl OpenCADStudio {
|
||||||
.push_error("HIDEOBJECTS: select the objects to hide first.");
|
.push_error("HIDEOBJECTS: select the objects to hide first.");
|
||||||
} else {
|
} else {
|
||||||
let n = self.tabs[i].scene.selected.len();
|
let n = self.tabs[i].scene.selected.len();
|
||||||
self.push_undo_snapshot(i, "HIDEOBJECTS");
|
let before = self.tabs[i].scene.object_isolation.clone();
|
||||||
|
let selected_before =
|
||||||
|
self.tabs[i].scene.selected.iter().copied().collect();
|
||||||
self.tabs[i].scene.hide_selected();
|
self.tabs[i].scene.hide_selected();
|
||||||
self.tabs[i].dirty = true;
|
self.push_object_visibility_history(
|
||||||
|
i,
|
||||||
|
"HIDEOBJECTS",
|
||||||
|
before,
|
||||||
|
selected_before,
|
||||||
|
);
|
||||||
|
self.refresh_properties();
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_info(&format!("Hid {n} object(s). UNISOLATEOBJECTS to restore."));
|
.push_info(&format!("Hid {n} object(s). UNISOLATEOBJECTS to restore."));
|
||||||
}
|
}
|
||||||
|
|
@ -518,9 +533,16 @@ impl OpenCADStudio {
|
||||||
// UNISOLATEOBJECTS — bring back everything hidden by Isolate / Hide
|
// UNISOLATEOBJECTS — bring back everything hidden by Isolate / Hide
|
||||||
"UNISOLATEOBJECTS" => {
|
"UNISOLATEOBJECTS" => {
|
||||||
if self.tabs[i].scene.is_isolation_active() {
|
if self.tabs[i].scene.is_isolation_active() {
|
||||||
self.push_undo_snapshot(i, "UNISOLATEOBJECTS");
|
let before = self.tabs[i].scene.object_isolation.clone();
|
||||||
|
let selected_before =
|
||||||
|
self.tabs[i].scene.selected.iter().copied().collect();
|
||||||
self.tabs[i].scene.end_isolation();
|
self.tabs[i].scene.end_isolation();
|
||||||
self.tabs[i].dirty = true;
|
self.push_object_visibility_history(
|
||||||
|
i,
|
||||||
|
"UNISOLATEOBJECTS",
|
||||||
|
before,
|
||||||
|
selected_before,
|
||||||
|
);
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_info("Isolation ended — all objects shown.");
|
.push_info("Isolation ended — all objects shown.");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::modules::draw::modify::block_edit::BlockEditSession;
|
||||||
use crate::modules::draw::modify::refedit::RefEditSession;
|
use crate::modules::draw::modify::refedit::RefEditSession;
|
||||||
use crate::scene::pick::grip::GripEdit;
|
use crate::scene::pick::grip::GripEdit;
|
||||||
use crate::scene::GripDef;
|
use crate::scene::GripDef;
|
||||||
use crate::scene::Scene;
|
use crate::scene::{ObjectIsolationState, Scene};
|
||||||
use crate::snap::SnapResult;
|
use crate::snap::SnapResult;
|
||||||
use crate::ui::{LayerPanel, PropertiesPanel};
|
use crate::ui::{LayerPanel, PropertiesPanel};
|
||||||
use acadrust::tables::Ucs;
|
use acadrust::tables::Ucs;
|
||||||
|
|
@ -518,12 +518,14 @@ impl DocumentTab {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(super) enum HistorySnapshot {
|
pub(super) enum HistorySnapshot {
|
||||||
Delta(DeltaSnapshot),
|
Delta(DeltaSnapshot),
|
||||||
|
ObjectVisibility(ObjectVisibilitySnapshot),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HistorySnapshot {
|
impl HistorySnapshot {
|
||||||
pub(super) fn label(&self) -> &str {
|
pub(super) fn label(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
HistorySnapshot::Delta(d) => &d.label,
|
HistorySnapshot::Delta(d) => &d.label,
|
||||||
|
HistorySnapshot::ObjectVisibility(v) => &v.label,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -544,10 +546,42 @@ impl HistorySnapshot {
|
||||||
.saturating_add(d.selected_before.len().saturating_mul(16))
|
.saturating_add(d.selected_before.len().saturating_mul(16))
|
||||||
.saturating_add(d.selected_after.len().saturating_mul(16))
|
.saturating_add(d.selected_after.len().saturating_mul(16))
|
||||||
.saturating_add(d.label.len()),
|
.saturating_add(d.label.len()),
|
||||||
|
HistorySnapshot::ObjectVisibility(v) => v
|
||||||
|
.before
|
||||||
|
.hidden
|
||||||
|
.len()
|
||||||
|
.saturating_add(
|
||||||
|
v.before
|
||||||
|
.keep
|
||||||
|
.as_ref()
|
||||||
|
.map_or(0, rustc_hash::FxHashSet::len),
|
||||||
|
)
|
||||||
|
.saturating_add(v.after.hidden.len())
|
||||||
|
.saturating_add(
|
||||||
|
v.after
|
||||||
|
.keep
|
||||||
|
.as_ref()
|
||||||
|
.map_or(0, rustc_hash::FxHashSet::len),
|
||||||
|
)
|
||||||
|
.saturating_add(v.selected_before.len())
|
||||||
|
.saturating_add(v.selected_after.len())
|
||||||
|
.saturating_mul(16)
|
||||||
|
.saturating_add(v.label.len()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Symmetric undo/redo image for session-only object visibility. It contains
|
||||||
|
/// no document entity data and therefore can never make isolation serializable.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(super) struct ObjectVisibilitySnapshot {
|
||||||
|
pub(super) before: ObjectIsolationState,
|
||||||
|
pub(super) after: ObjectIsolationState,
|
||||||
|
pub(super) selected_before: Vec<Handle>,
|
||||||
|
pub(super) selected_after: Vec<Handle>,
|
||||||
|
pub(super) label: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// A transactional undo entry: for each touched handle, its before-image and
|
/// A transactional undo entry: for each touched handle, its before-image and
|
||||||
/// after-image (`None` = the entity was absent on that side, i.e. an add or an
|
/// after-image (`None` = the entity was absent on that side, i.e. an add or an
|
||||||
/// erase). Symmetric — undo applies the before side, redo the after side — so
|
/// erase). Symmetric — undo applies the before side, redo the after side — so
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use super::{
|
use super::{
|
||||||
document::{
|
document::{
|
||||||
DeltaSnapshot, HistorySnapshot, ObjectEntryDelta, PendingHistorySnapshot,
|
DeltaSnapshot, HistorySnapshot, ObjectEntryDelta, ObjectVisibilitySnapshot,
|
||||||
StructureSnapshot, TableEntryDelta,
|
PendingHistorySnapshot, StructureSnapshot, TableEntryDelta,
|
||||||
},
|
},
|
||||||
OpenCADStudio,
|
OpenCADStudio,
|
||||||
};
|
};
|
||||||
|
use crate::scene::ObjectIsolationState;
|
||||||
use acadrust::{EntityType, Handle};
|
use acadrust::{EntityType, Handle};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet as HashSet};
|
use rustc_hash::{FxHashMap, FxHashSet as HashSet};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -157,6 +158,32 @@ impl OpenCADStudio {
|
||||||
self.trim_history(i);
|
self.trim_history(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn push_object_visibility_history(
|
||||||
|
&mut self,
|
||||||
|
i: usize,
|
||||||
|
label: impl Into<String>,
|
||||||
|
before: ObjectIsolationState,
|
||||||
|
selected_before: Vec<Handle>,
|
||||||
|
) {
|
||||||
|
self.finish_pending_history(i);
|
||||||
|
let after = self.tabs[i].scene.object_isolation.clone();
|
||||||
|
let selected_after: Vec<Handle> =
|
||||||
|
self.tabs[i].scene.selected.iter().copied().collect();
|
||||||
|
if before == after && selected_before == selected_after {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.push_undo_entry(
|
||||||
|
i,
|
||||||
|
HistorySnapshot::ObjectVisibility(ObjectVisibilitySnapshot {
|
||||||
|
before,
|
||||||
|
after,
|
||||||
|
selected_before,
|
||||||
|
selected_after,
|
||||||
|
label: label.into(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn push_single_entity_history(
|
pub(super) fn push_single_entity_history(
|
||||||
&mut self,
|
&mut self,
|
||||||
i: usize,
|
i: usize,
|
||||||
|
|
@ -870,6 +897,27 @@ impl OpenCADStudio {
|
||||||
changes
|
changes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_object_visibility_state(
|
||||||
|
&mut self,
|
||||||
|
i: usize,
|
||||||
|
snapshot: &ObjectVisibilitySnapshot,
|
||||||
|
undo: bool,
|
||||||
|
) {
|
||||||
|
let (state, selected) = if undo {
|
||||||
|
(&snapshot.before, &snapshot.selected_before)
|
||||||
|
} else {
|
||||||
|
(&snapshot.after, &snapshot.selected_after)
|
||||||
|
};
|
||||||
|
let scene = &mut self.tabs[i].scene;
|
||||||
|
scene.object_isolation = state.clone();
|
||||||
|
scene.selected = selected
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|handle| scene.document.get_entity(*handle).is_some())
|
||||||
|
.collect();
|
||||||
|
scene.bump_geometry_no_blocks();
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform the one expensive cache/UI synchronization required after a
|
/// Perform the one expensive cache/UI synchronization required after a
|
||||||
/// batch of history steps. Intermediate states are never rendered, so
|
/// batch of history steps. Intermediate states are never rendered, so
|
||||||
/// rebuilding them only multiplies latency.
|
/// rebuilding them only multiplies latency.
|
||||||
|
|
@ -888,7 +936,6 @@ impl OpenCADStudio {
|
||||||
// for the final state. Unlike the old path, decoded images stay
|
// for the final state. Unlike the old path, decoded images stay
|
||||||
// cached instead of being immediately cleared.
|
// cached instead of being immediately cleared.
|
||||||
scene.rebuild_derived_caches();
|
scene.rebuild_derived_caches();
|
||||||
scene.sync_hidden_from_invisible();
|
|
||||||
} else if !changes.is_empty() {
|
} else if !changes.is_empty() {
|
||||||
use crate::scene::ChangeKind::{Added, Modified, Removed};
|
use crate::scene::ChangeKind::{Added, Modified, Removed};
|
||||||
let mut net: FxHashMap<Handle, crate::scene::ChangeKind> = FxHashMap::default();
|
let mut net: FxHashMap<Handle, crate::scene::ChangeKind> = FxHashMap::default();
|
||||||
|
|
@ -974,6 +1021,13 @@ impl OpenCADStudio {
|
||||||
.redo_stack
|
.redo_stack
|
||||||
.push(HistorySnapshot::Delta(d));
|
.push(HistorySnapshot::Delta(d));
|
||||||
}
|
}
|
||||||
|
HistorySnapshot::ObjectVisibility(v) => {
|
||||||
|
self.apply_object_visibility_state(i, &v, true);
|
||||||
|
self.tabs[i]
|
||||||
|
.history
|
||||||
|
.redo_stack
|
||||||
|
.push(HistorySnapshot::ObjectVisibility(v));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.finish_history_apply(i, had_full, structure_changed, &changes);
|
self.finish_history_apply(i, had_full, structure_changed, &changes);
|
||||||
|
|
@ -1010,6 +1064,13 @@ impl OpenCADStudio {
|
||||||
.undo_stack
|
.undo_stack
|
||||||
.push(HistorySnapshot::Delta(d));
|
.push(HistorySnapshot::Delta(d));
|
||||||
}
|
}
|
||||||
|
HistorySnapshot::ObjectVisibility(v) => {
|
||||||
|
self.apply_object_visibility_state(i, &v, false);
|
||||||
|
self.tabs[i]
|
||||||
|
.history
|
||||||
|
.undo_stack
|
||||||
|
.push(HistorySnapshot::ObjectVisibility(v));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.finish_history_apply(i, had_full, structure_changed, &changes);
|
self.finish_history_apply(i, had_full, structure_changed, &changes);
|
||||||
|
|
|
||||||
|
|
@ -549,7 +549,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||||
*e = orig;
|
*e = orig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.tabs[i].scene.hidden.remove(&h);
|
self.tabs[i].scene.preview_hidden.remove(&h);
|
||||||
self.tabs[i].scene.clear_preview_wire();
|
self.tabs[i].scene.clear_preview_wire();
|
||||||
// Geometry restored to the backup — re-tessellate just it.
|
// Geometry restored to the backup — re-tessellate just it.
|
||||||
self.tabs[i]
|
self.tabs[i]
|
||||||
|
|
|
||||||
|
|
@ -821,10 +821,10 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
||||||
.unwrap_or_else(|| "Model".to_string()),
|
.unwrap_or_else(|| "Model".to_string()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Rebuild the Isolate/Hide set from the entities the file itself
|
// Object isolation is session-only. A newly opened drawing must
|
||||||
// marks invisible (DXF code 60), so hidden objects stay hidden on
|
// not inherit the previous tab's filter, and persisted entity
|
||||||
// reopen and End Isolation can bring them back.
|
// visibility remains independent (not an isolation session).
|
||||||
self.tabs[i].scene.sync_hidden_from_invisible();
|
self.tabs[i].scene.reset_transient_visibility();
|
||||||
crate::io::linetypes::populate_document(&mut self.tabs[i].scene.document);
|
crate::io::linetypes::populate_document(&mut self.tabs[i].scene.document);
|
||||||
self.tabs[i].properties = PropertiesPanel::empty();
|
self.tabs[i].properties = PropertiesPanel::empty();
|
||||||
// Seed the current table / multileader style from the file's
|
// Seed the current table / multileader style from the file's
|
||||||
|
|
|
||||||
|
|
@ -742,11 +742,11 @@ impl OpenCADStudio {
|
||||||
// the whole model on every move.
|
// the whole model on every move.
|
||||||
if self.grip_preview_handle != Some(grip.handle) {
|
if self.grip_preview_handle != Some(grip.handle) {
|
||||||
if let Some(prev) = self.grip_preview_handle.take() {
|
if let Some(prev) = self.grip_preview_handle.take() {
|
||||||
self.tabs[i].scene.hidden.remove(&prev);
|
self.tabs[i].scene.preview_hidden.remove(&prev);
|
||||||
}
|
}
|
||||||
// Back up the original geometry so Esc can cancel the drag.
|
// Back up the original geometry so Esc can cancel the drag.
|
||||||
self.grip_original = self.tabs[i].scene.document.get_entity(grip.handle).cloned();
|
self.grip_original = self.tabs[i].scene.document.get_entity(grip.handle).cloned();
|
||||||
self.tabs[i].scene.hidden.insert(grip.handle);
|
self.tabs[i].scene.preview_hidden.insert(grip.handle);
|
||||||
// Hiding changes exactly one resident run. Publishing a full
|
// Hiding changes exactly one resident run. Publishing a full
|
||||||
// delta here made the first grip move rebuild every wire.
|
// delta here made the first grip move rebuild every wire.
|
||||||
self.tabs[i]
|
self.tabs[i]
|
||||||
|
|
@ -2016,7 +2016,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
self.grip_text_verts = Vec::new();
|
self.grip_text_verts = Vec::new();
|
||||||
self.grip_text_slide = false;
|
self.grip_text_slide = false;
|
||||||
self.tabs[i].scene.hidden.remove(&h);
|
self.tabs[i].scene.preview_hidden.remove(&h);
|
||||||
self.tabs[i].scene.clear_preview_wire();
|
self.tabs[i].scene.clear_preview_wire();
|
||||||
// Only the dragged entity changed — re-tessellate just it.
|
// Only the dragged entity changed — re-tessellate just it.
|
||||||
self.tabs[i]
|
self.tabs[i]
|
||||||
|
|
|
||||||
|
|
@ -812,7 +812,10 @@ impl Scene {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
let c = entity.common();
|
let c = entity.common();
|
||||||
if c.invisible || layer_hidden(&c.layer) {
|
if c.invisible
|
||||||
|
|| self.entity_temporarily_hidden(handle)
|
||||||
|
|| layer_hidden(&c.layer)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Per-viewport layer freeze: a content viewport that freezes
|
// Per-viewport layer freeze: a content viewport that freezes
|
||||||
|
|
@ -1059,7 +1062,10 @@ impl Scene {
|
||||||
let EntityType::Insert(ins) = contextual.as_ref() else {
|
let EntityType::Insert(ins) = contextual.as_ref() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if ins.common.invisible || layer_hidden(&ins.common.layer) {
|
if ins.common.invisible
|
||||||
|
|| self.entity_temporarily_hidden(ins.common.handle)
|
||||||
|
|| layer_hidden(&ins.common.layer)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Per-viewport freeze: an INSERT on a layer frozen in this content
|
// Per-viewport freeze: an INSERT on a layer frozen in this content
|
||||||
|
|
@ -1291,7 +1297,9 @@ impl Scene {
|
||||||
let EntityType::Wipeout(wo) = entity else {
|
let EntityType::Wipeout(wo) = entity else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if entity.common().invisible {
|
if entity.common().invisible
|
||||||
|
|| self.entity_temporarily_hidden(wo.common.handle)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Reject block-defn-only wipeouts (owned by a BLOCK record that is
|
// Reject block-defn-only wipeouts (owned by a BLOCK record that is
|
||||||
|
|
@ -1361,6 +1369,7 @@ impl Scene {
|
||||||
};
|
};
|
||||||
let c = &ins.common;
|
let c = &ins.common;
|
||||||
if c.invisible
|
if c.invisible
|
||||||
|
|| self.entity_temporarily_hidden(c.handle)
|
||||||
|| self
|
|| self
|
||||||
.document
|
.document
|
||||||
.layers
|
.layers
|
||||||
|
|
|
||||||
196
src/scene/mod.rs
196
src/scene/mod.rs
|
|
@ -153,6 +153,29 @@ use std::rc::Rc;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// Session-only object visibility used by ISOLATEOBJECTS / HIDEOBJECTS.
|
||||||
|
///
|
||||||
|
/// This is deliberately separate from `EntityCommon::invisible`: that DXF
|
||||||
|
/// property belongs to the drawing (and dynamic-block visibility states),
|
||||||
|
/// whereas object isolation must never be serialized.
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub struct ObjectIsolationState {
|
||||||
|
/// Objects explicitly hidden with HIDEOBJECTS.
|
||||||
|
pub hidden: HashSet<Handle>,
|
||||||
|
/// Objects retained by ISOLATEOBJECTS. `None` means no isolate filter.
|
||||||
|
pub keep: Option<HashSet<Handle>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectIsolationState {
|
||||||
|
fn hides(&self, handle: Handle) -> bool {
|
||||||
|
self.hidden.contains(&handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
!self.hidden.is_empty() || self.keep.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Global counter so every Scene and every geometry mutation gets a
|
/// Global counter so every Scene and every geometry mutation gets a
|
||||||
/// process-wide unique epoch. This prevents two different tabs (Scenes)
|
/// process-wide unique epoch. This prevents two different tabs (Scenes)
|
||||||
/// from ever sharing the same epoch value, which would cause the shared
|
/// from ever sharing the same epoch value, which would cause the shared
|
||||||
|
|
@ -1359,10 +1382,13 @@ pub struct Scene {
|
||||||
lighting_cache: RefCell<Option<Vec<SceneLight>>>,
|
lighting_cache: RefCell<Option<Vec<SceneLight>>>,
|
||||||
/// Currently selected entity handles.
|
/// Currently selected entity handles.
|
||||||
pub selected: HashSet<Handle>,
|
pub selected: HashSet<Handle>,
|
||||||
/// Entity handles hidden by Isolate / Hide. Empty = nothing hidden.
|
/// Session-only ISOLATEOBJECTS / HIDEOBJECTS state. Never written to DWG/DXF.
|
||||||
/// `tessellate_block`'s visibility test skips these, so they neither
|
pub object_isolation: ObjectIsolationState,
|
||||||
/// render nor hit-test until isolation ends.
|
/// Entity handles temporarily removed from the base render while an
|
||||||
pub hidden: HashSet<Handle>,
|
/// interactive preview (currently grip drag) draws their live replacement.
|
||||||
|
/// Separate from object isolation so a grip can never activate the
|
||||||
|
/// isolation status or be captured in its undo state.
|
||||||
|
pub preview_hidden: HashSet<Handle>,
|
||||||
/// During in-place block edit (REFEDIT), the handles of the entities being
|
/// During in-place block edit (REFEDIT), the handles of the entities being
|
||||||
/// edited. Everything else is rendered faded toward the background so the
|
/// edited. Everything else is rendered faded toward the background so the
|
||||||
/// edited geometry stands out while the surrounding drawing stays visible
|
/// edited geometry stands out while the surrounding drawing stays visible
|
||||||
|
|
@ -1751,7 +1777,8 @@ impl Scene {
|
||||||
object_data_cache: crate::entities::object_data::ObjectDataCache::default(),
|
object_data_cache: crate::entities::object_data::ObjectDataCache::default(),
|
||||||
lighting_cache: RefCell::new(None),
|
lighting_cache: RefCell::new(None),
|
||||||
selected: HashSet::default(),
|
selected: HashSet::default(),
|
||||||
hidden: HashSet::default(),
|
object_isolation: ObjectIsolationState::default(),
|
||||||
|
preview_hidden: HashSet::default(),
|
||||||
refedit_keep: None,
|
refedit_keep: None,
|
||||||
hover_highlight: None,
|
hover_highlight: None,
|
||||||
transparency_display: true,
|
transparency_display: true,
|
||||||
|
|
@ -3475,9 +3502,15 @@ impl Scene {
|
||||||
content.len()
|
content.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// True when any entities are hidden by Isolate / Hide.
|
/// True when ISOLATEOBJECTS or HIDEOBJECTS has an active session filter.
|
||||||
pub fn is_isolation_active(&self) -> bool {
|
pub fn is_isolation_active(&self) -> bool {
|
||||||
!self.hidden.is_empty()
|
self.object_isolation.is_active()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True when a top-level entity must be omitted for a session-only object
|
||||||
|
/// visibility command or an interactive replacement preview.
|
||||||
|
fn entity_temporarily_hidden(&self, handle: Handle) -> bool {
|
||||||
|
self.object_isolation.hides(handle) || self.preview_hidden.contains(&handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set (or clear) the previewed entity that renders with the selection
|
/// Set (or clear) the previewed entity that renders with the selection
|
||||||
|
|
@ -3499,80 +3532,63 @@ impl Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hide every drawable entity except the current selection (Isolate).
|
/// Keep the current selection visible and temporarily filter every other
|
||||||
|
/// top-level object. Block-definition children are intentionally not added
|
||||||
|
/// here: an isolated INSERT remains a complete visible block instance.
|
||||||
pub fn isolate_selected(&mut self) {
|
pub fn isolate_selected(&mut self) {
|
||||||
if self.selected.is_empty() {
|
if self.selected.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let keep = self.selected.clone();
|
let keep = self.selected.clone();
|
||||||
|
let active_block = self.interaction_block_handle();
|
||||||
let hide: Vec<Handle> = self
|
let hide: Vec<Handle> = self
|
||||||
.document
|
.document
|
||||||
.entities()
|
.entities()
|
||||||
.map(|e| e.common().handle)
|
.filter(|entity| {
|
||||||
.filter(|h| !h.is_null() && !keep.contains(h))
|
let common = entity.common();
|
||||||
|
!common.handle.is_null()
|
||||||
|
&& !keep.contains(&common.handle)
|
||||||
|
&& self.belongs_to_visible_block(
|
||||||
|
common.handle,
|
||||||
|
common.owner_handle,
|
||||||
|
active_block,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|entity| entity.common().handle)
|
||||||
.collect();
|
.collect();
|
||||||
// Persist the hidden state on each entity (DXF code 60) so it survives
|
self.object_isolation.hidden.extend(hide);
|
||||||
// save/reopen — the renderer already skips `invisible` entities.
|
self.object_isolation.keep = Some(keep);
|
||||||
self.set_invisible(&hide, true);
|
self.bump_geometry_no_blocks();
|
||||||
self.hidden = hide.into_iter().collect();
|
|
||||||
self.selected.clear();
|
|
||||||
self.bump_geometry();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hide the current selection (Hide Objects).
|
/// Temporarily hide the current selection without changing any entity
|
||||||
|
/// property in the document.
|
||||||
pub fn hide_selected(&mut self) {
|
pub fn hide_selected(&mut self) {
|
||||||
if self.selected.is_empty() {
|
if self.selected.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let sel: Vec<Handle> = self.selected.iter().copied().collect();
|
self.object_isolation
|
||||||
for h in sel.iter().copied() {
|
.hidden
|
||||||
self.hidden.insert(h);
|
.extend(self.selected.iter().copied());
|
||||||
}
|
|
||||||
self.set_invisible(&sel, true);
|
|
||||||
self.selected.clear();
|
self.selected.clear();
|
||||||
// Only the hidden entities changed visibility — report just those so the
|
self.bump_geometry_no_blocks();
|
||||||
// resident set drops them instead of re-tessellating the whole drawing.
|
|
||||||
let changes: Vec<(Handle, ChangeKind)> =
|
|
||||||
sel.iter().map(|&h| (h, ChangeKind::Modified)).collect();
|
|
||||||
self.bump_entities(&changes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear isolation — bring every hidden entity back (End Isolation),
|
/// Clear every session-only object visibility filter.
|
||||||
/// clearing the persisted invisible flag too so the reveal is saved.
|
|
||||||
pub fn end_isolation(&mut self) {
|
pub fn end_isolation(&mut self) {
|
||||||
if self.hidden.is_empty() {
|
if !self.object_isolation.is_active() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let restore: Vec<Handle> = self.hidden.iter().copied().collect();
|
self.object_isolation = ObjectIsolationState::default();
|
||||||
self.set_invisible(&restore, false);
|
self.bump_geometry_no_blocks();
|
||||||
self.hidden.clear();
|
|
||||||
// Re-reveal just the previously hidden entities (bounded to that set).
|
|
||||||
let changes: Vec<(Handle, ChangeKind)> =
|
|
||||||
restore.iter().map(|&h| (h, ChangeKind::Modified)).collect();
|
|
||||||
self.bump_entities(&changes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the persisted visibility flag (DXF code 60) on each handle.
|
/// A newly opened/replaced document starts with no session visibility.
|
||||||
fn set_invisible(&mut self, handles: &[Handle], invisible: bool) {
|
/// Persisted `EntityCommon::invisible` values stay untouched and continue
|
||||||
for &h in handles {
|
/// to serve file/dynamic-block visibility.
|
||||||
if let Some(e) = self.document.get_entity_mut(h) {
|
pub fn reset_transient_visibility(&mut self) {
|
||||||
e.common_mut().invisible = invisible;
|
self.object_isolation = ObjectIsolationState::default();
|
||||||
}
|
self.preview_hidden.clear();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rebuild the Isolate/Hide set (`hidden`) from the entities the document
|
|
||||||
/// currently marks invisible (DXF code 60). Call after loading a file or
|
|
||||||
/// restoring an undo/redo snapshot so the session set matches the persisted
|
|
||||||
/// per-entity visibility (and End Isolation stays available).
|
|
||||||
pub fn sync_hidden_from_invisible(&mut self) {
|
|
||||||
self.hidden = self
|
|
||||||
.document
|
|
||||||
.entities()
|
|
||||||
.filter(|e| e.common().invisible)
|
|
||||||
.map(|e| e.common().handle)
|
|
||||||
.filter(|h| !h.is_null())
|
|
||||||
.collect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// True if any currently selected entity is a Viewport.
|
/// True if any currently selected entity is a Viewport.
|
||||||
|
|
@ -4624,17 +4640,15 @@ impl Scene {
|
||||||
self.images
|
self.images
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(handle, model)| {
|
.filter_map(|(handle, model)| {
|
||||||
if frozen.is_some() {
|
let entity = self.document.get_entity(*handle)?;
|
||||||
let layer = self
|
let common = entity.common();
|
||||||
.document
|
if common.invisible
|
||||||
.get_entity(*handle)
|
|| self.entity_temporarily_hidden(*handle)
|
||||||
.map(|e| e.common().layer.clone());
|
|| self.layer_hidden(&common.layer)
|
||||||
if let Some(layer) = layer {
|
|| self.layer_frozen_in(&common.layer, frozen)
|
||||||
if self.layer_frozen_in(&layer, frozen) {
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut m = model.clone();
|
let mut m = model.clone();
|
||||||
m.draw_depth = depth_map.get(&handle.value()).map_or(0.0, |d| d[0]);
|
m.draw_depth = depth_map.get(&handle.value()).map_or(0.0, |d| d[0]);
|
||||||
Some(m)
|
Some(m)
|
||||||
|
|
@ -4655,6 +4669,8 @@ impl Scene {
|
||||||
let entity = self.document.get_entity(handle)?;
|
let entity = self.document.get_entity(handle)?;
|
||||||
let c = entity.common();
|
let c = entity.common();
|
||||||
if c.invisible
|
if c.invisible
|
||||||
|
|| self.entity_temporarily_hidden(handle)
|
||||||
|
|| self.layer_hidden(&c.layer)
|
||||||
|| !self.belongs_to_visible_block(handle, c.owner_handle, layout_block)
|
|| !self.belongs_to_visible_block(handle, c.owner_handle, layout_block)
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -5012,20 +5028,29 @@ impl Scene {
|
||||||
locked.then_some(name)
|
locked.then_some(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visibility test for a solid mesh entity, mirroring the 2D wire path:
|
/// File-backed visibility shared by top-level and block-definition meshes.
|
||||||
/// honour the invisible flag, the isolate/hide set, and the layer's
|
/// Object isolation is intentionally absent: a retained INSERT must retain
|
||||||
/// off/frozen state.
|
/// every visible child of its block definition.
|
||||||
fn mesh_entity_visible(&self, handle: Handle) -> bool {
|
fn mesh_definition_entity_visible(&self, handle: Handle) -> bool {
|
||||||
let Some(c) = self.document.get_entity(handle).map(|e| e.common()) else {
|
let Some(c) = self.document.get_entity(handle).map(|e| e.common()) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if c.invisible {
|
if c.invisible {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if !self.hidden.is_empty() && self.hidden.contains(&handle) {
|
!self.layer_hidden(&c.layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visibility test for a top-level solid mesh entity, mirroring the direct
|
||||||
|
/// 2D wire path.
|
||||||
|
fn mesh_entity_visible(&self, handle: Handle) -> bool {
|
||||||
|
if !self.mesh_definition_entity_visible(handle) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
!self.layer_hidden(&c.layer)
|
if self.entity_temporarily_hidden(handle) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mesh_visible_for_interaction(&self, handle: Handle) -> bool {
|
fn mesh_visible_for_interaction(&self, handle: Handle) -> bool {
|
||||||
|
|
@ -5152,7 +5177,7 @@ impl Scene {
|
||||||
let e = contextual.as_ref();
|
let e = contextual.as_ref();
|
||||||
// A block-internal solid / nested INSERT on an off/frozen layer
|
// A block-internal solid / nested INSERT on an off/frozen layer
|
||||||
// (or flagged invisible) must not render, same as a top-level one.
|
// (or flagged invisible) must not render, same as a top-level one.
|
||||||
if !self.mesh_entity_visible(h) {
|
if !self.mesh_definition_entity_visible(h) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let EntityType::Insert(ins) = e {
|
if let EntityType::Insert(ins) = e {
|
||||||
|
|
@ -5371,7 +5396,7 @@ impl Scene {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if common.invisible
|
if common.invisible
|
||||||
|| (!self.hidden.is_empty() && self.hidden.contains(&handle))
|
|| self.entity_temporarily_hidden(handle)
|
||||||
|| self.layer_hidden(&common.layer)
|
|| self.layer_hidden(&common.layer)
|
||||||
|| self.interaction_layer_frozen(&common.layer)
|
|| self.interaction_layer_frozen(&common.layer)
|
||||||
{
|
{
|
||||||
|
|
@ -5496,7 +5521,7 @@ impl Scene {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if ins.common.invisible
|
if ins.common.invisible
|
||||||
|| (!self.hidden.is_empty() && self.hidden.contains(&ins.common.handle))
|
|| self.entity_temporarily_hidden(ins.common.handle)
|
||||||
|| layer_hidden(&ins.common.layer)
|
|| layer_hidden(&ins.common.layer)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -6638,8 +6663,8 @@ impl Scene {
|
||||||
if c.invisible {
|
if c.invisible {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Isolate / Hide: skip entities the user has hidden.
|
// Session-only Isolate / Hide and interactive replacement previews.
|
||||||
if !self.hidden.is_empty() && self.hidden.contains(&c.handle) {
|
if self.entity_temporarily_hidden(c.handle) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Block/BlockEnd are block-defn sentinels, not drawable geometry.
|
// Block/BlockEnd are block-defn sentinels, not drawable geometry.
|
||||||
|
|
@ -7544,7 +7569,10 @@ impl Scene {
|
||||||
}
|
}
|
||||||
// 3D solids render as meshes, not wires, so fold their
|
// 3D solids render as meshes, not wires, so fold their
|
||||||
// XY AABBs in too — otherwise ZOOM EXTENTS ignores them.
|
// XY AABBs in too — otherwise ZOOM EXTENTS ignores them.
|
||||||
for set in self.meshes.values() {
|
for (&handle, set) in &self.meshes {
|
||||||
|
if !self.mesh_entity_visible(handle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let [ax, ay, bx, by] = set.world_aabb;
|
let [ax, ay, bx, by] = set.world_aabb;
|
||||||
let lo = glam::Vec3::new(ax, ay, 0.0);
|
let lo = glam::Vec3::new(ax, ay, 0.0);
|
||||||
let hi = glam::Vec3::new(bx, by, 0.0);
|
let hi = glam::Vec3::new(bx, by, 0.0);
|
||||||
|
|
@ -7570,7 +7598,10 @@ impl Scene {
|
||||||
// wrong location on UTM-scale drawings.
|
// wrong location on UTM-scale drawings.
|
||||||
for entity in self.document.entities() {
|
for entity in self.document.entities() {
|
||||||
let c = entity.common();
|
let c = entity.common();
|
||||||
if c.owner_handle != model_block || c.invisible {
|
if c.owner_handle != model_block
|
||||||
|
|| c.invisible
|
||||||
|
|| self.entity_temporarily_hidden(c.handle)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for wire in self.tessellate_one(entity) {
|
for wire in self.tessellate_one(entity) {
|
||||||
|
|
@ -7588,7 +7619,10 @@ impl Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Same mesh inclusion for the tessellate fallback path.
|
// Same mesh inclusion for the tessellate fallback path.
|
||||||
for set in self.meshes.values() {
|
for (&handle, set) in &self.meshes {
|
||||||
|
if !self.mesh_entity_visible(handle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let [ax, ay, bx, by] = set.world_aabb;
|
let [ax, ay, bx, by] = set.world_aabb;
|
||||||
let lo = glam::Vec3::new(ax, ay, 0.0);
|
let lo = glam::Vec3::new(ax, ay, 0.0);
|
||||||
let hi = glam::Vec3::new(bx, by, 0.0);
|
let hi = glam::Vec3::new(bx, by, 0.0);
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,11 @@ impl Scene {
|
||||||
let Some(EntityType::Viewport(vp)) = self.document.get_entity(handle) else {
|
let Some(EntityType::Viewport(vp)) = self.document.get_entity(handle) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if !vp.status.is_on {
|
if !vp.status.is_on
|
||||||
|
|| vp.common.invisible
|
||||||
|
|| self.entity_temporarily_hidden(handle)
|
||||||
|
|| self.layer_hidden(&vp.common.layer)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let h = vp.common.handle;
|
let h = vp.common.handle;
|
||||||
|
|
@ -317,7 +321,10 @@ impl Scene {
|
||||||
crate::scene::annotative::entity_for_active_context(&self.document, source);
|
crate::scene::annotative::entity_for_active_context(&self.document, source);
|
||||||
let entity = contextual.as_ref();
|
let entity = contextual.as_ref();
|
||||||
let c = entity.common();
|
let c = entity.common();
|
||||||
if c.invisible || layer_hidden(&c.layer) {
|
if c.invisible
|
||||||
|
|| self.entity_temporarily_hidden(handle)
|
||||||
|
|| layer_hidden(&c.layer)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if !self.belongs_to_visible_block(handle, c.owner_handle, layout_block) {
|
if !self.belongs_to_visible_block(handle, c.owner_handle, layout_block) {
|
||||||
|
|
@ -382,7 +389,9 @@ impl Scene {
|
||||||
let EntityType::Wipeout(wo) = entity else {
|
let EntityType::Wipeout(wo) = entity else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if wo.common.invisible {
|
if wo.common.invisible
|
||||||
|
|| self.entity_temporarily_hidden(wo.common.handle)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if self
|
if self
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue