fix(count): redraw only app-edited table blocks; recolour count mode in place

- refresh_table_block no longer runs for every table change: only tables
  whose picture reference this app dropped while editing them (and new
  tables without one), never on appearance-only bumps. Moved, restyled or
  freshly opened tables keep their authored *T block. TABLE edits now drop
  the picture reference like the other table edit paths.
- set_count_display recolours just the handles whose count class changed
  (restyle path + recolor_meshes_for_handles); only entering or leaving
  count mode rebuilds the whole drawing.
- Count colouring keeps insert instancing: counted and duplicate wires get
  their own instance groups instead of dropping render_instance.
- The hatch backdrop takes the count colour too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-06 09:33:07 +03:00
1 parent 6da9675c55
commit 8fb4a692c6
3 files changed
+66 -17

No files matched your search

+8
View File
@@ -179,6 +179,8 @@ impl OpenCADStudio {
else { else {
continue; continue;
}; };
// `changed` tracks this table while the action runs.
let changed_before = std::mem::take(&mut changed);
match &action { match &action {
TableAction::Cell(row, column, text) => { TableAction::Cell(row, column, text) => {
if let Some(cell) = table.cell_mut(*row, *column) { if let Some(cell) = table.cell_mut(*row, *column) {
@@ -321,6 +323,12 @@ impl OpenCADStudio {
} }
_ => {} _ => {}
} }
if changed {
// The stored picture no longer shows the cells: draw
// from them, and redraw the picture.
table.block_record_handle = None;
}
changed |= changed_before;
} }
if changed { if changed {
self.invalidate_property_targets(i, &selected_handles); self.invalidate_property_targets(i, &selected_handles);
+3
View File
@@ -1452,6 +1452,9 @@ impl Scene {
} }
} }
m.color = self.count_color(handle, m.color, self.current_bg()); m.color = self.count_color(handle, m.color, self.current_bg());
if let Some(b) = &mut backdrop {
b.color = self.count_color(handle, b.color, self.current_bg());
}
if tint_selected && self.selected.contains(&handle) { if tint_selected && self.selected.contains(&handle) {
m.color = [0.15, 0.55, 1.00, m.color[3]]; m.color = [0.15, 0.55, 1.00, m.color[3]];
} }
+55 -17
View File
@@ -3511,13 +3511,21 @@ impl Scene {
self.associative_hatch_source_cache.borrow_mut().take(); self.associative_hatch_source_cache.borrow_mut().take();
} }
let mut changes = changes.to_vec(); let mut changes = changes.to_vec();
// A table's drawing (its *T block) follows the table's cells, sizes // A table whose content this app changed (its picture reference
// and styles; it is left alone while it already matches. // dropped by the edit, or a new table without one) has its drawing
for (handle, kind) in &changes { // (the *T block) redrawn from the cells. Tables keeping their picture
if !matches!(kind, ChangeKind::Removed) // (moved, restyled, opened from a file) are never redrawn: the block
&& matches!(self.document.get_entity(*handle), Some(EntityType::Table(_))) // may hold authored content the redraw does not reproduce.
{ if !appearance_only {
self.document.refresh_table_block(*handle); for (handle, kind) in &changes {
if !matches!(kind, ChangeKind::Removed)
&& matches!(
self.document.get_entity(*handle),
Some(EntityType::Table(t)) if t.block_record_handle.is_none()
)
{
self.document.refresh_table_block(*handle);
}
} }
} }
// A restyle (layer on/off, colour, text or dimension style) moves no // A restyle (layer on/off, colour, text or dimension style) moves no
@@ -4111,6 +4119,9 @@ impl Scene {
material.diffuse = material.diffuse =
crate::scene::cache::block_cache::fade_toward_bg(material.diffuse, bg); crate::scene::cache::block_cache::fade_toward_bg(material.diffuse, bg);
} }
if self.meshes.contains_key(&handle) {
material.diffuse = self.count_color(handle, material.diffuse, bg);
}
(handle, material) (handle, material)
}) })
}) })
@@ -4161,14 +4172,35 @@ impl Scene {
} }
/// Enter / leave the COUNT colouring; a no-op when nothing changed. /// Enter / leave the COUNT colouring; a no-op when nothing changed.
/// Entering or leaving fades or restores the whole drawing; a change
/// inside count mode recolours only the objects whose colour changed.
pub fn set_count_display(&mut self, display: Option<CountDisplay>) { pub fn set_count_display(&mut self, display: Option<CountDisplay>) {
if self.count_display == display { if self.count_display == display {
return; return;
} }
self.count_display = display; let old = std::mem::replace(&mut self.count_display, display);
self.hatch_cache.borrow_mut().clear(); let (Some(old), Some(new)) = (old, self.count_display.as_ref()) else {
self.recolor_meshes(); self.hatch_cache.borrow_mut().clear();
self.bump_geometry_no_blocks(); self.recolor_meshes();
self.bump_geometry_no_blocks();
return;
};
let recoloured = (old.color, old.error_color) != (new.color, new.error_color);
let class = |d: &CountDisplay, h: &Handle| (d.errors.contains(h), d.counted.contains(h));
let changed: Vec<Handle> = old
.counted
.iter()
.chain(&old.errors)
.chain(&new.counted)
.chain(&new.errors)
.copied()
.collect::<HashSet<Handle>>()
.into_iter()
.filter(|h| recoloured || class(&old, h) != class(new, h))
.collect();
self.recolor_meshes_for_handles(&changed);
let changes: Vec<(Handle, ChangeKind)> = changed.into_iter().map(|h| (h, ChangeKind::Modified)).collect();
self.bump_entities_restyled(&changes);
} }
/// The colour count mode gives an object drawn as part of `handle` (a /// The colour count mode gives an object drawn as part of `handle` (a
@@ -4195,13 +4227,19 @@ impl Scene {
fn apply_refedit_fade(&self, wires: &mut [WireModel], bg: [f32; 4]) { fn apply_refedit_fade(&self, wires: &mut [WireModel], bg: [f32; 4]) {
if let Some(count) = &self.count_display { if let Some(count) = &self.count_display {
for w in wires.iter_mut() { for w in wires.iter_mut() {
w.color = match Self::handle_from_wire_name(&w.name) { let (color, class) = match Self::handle_from_wire_name(&w.name) {
Some(h) if count.errors.contains(&h) => count.error_color, Some(h) if count.errors.contains(&h) => (count.error_color, 2u64),
Some(h) if count.counted.contains(&h) => count.color, Some(h) if count.counted.contains(&h) => (count.color, 1),
_ => crate::scene::cache::block_cache::fade_toward_bg(w.color, bg), _ => (crate::scene::cache::block_cache::fade_toward_bg(w.color, bg), 0),
}; };
// Shared instance geometry keeps its own colour; draw these wires on their own. w.color = color;
w.render_instance = None; // Instances of one definition draw in one colour: counted and
// duplicate references get instance groups of their own.
// ponytail: tags the two top bits of the source id, which the
// id counter never reaches.
if let Some(instance) = w.render_instance.as_mut() {
instance.source_id |= class << 62;
}
} }
} }
let Some(keep) = &self.refedit_keep else { let Some(keep) = &self.refedit_keep else {