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 {
continue;
};
// `changed` tracks this table while the action runs.
let changed_before = std::mem::take(&mut changed);
match &action {
TableAction::Cell(row, column, text) => {
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 {
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());
if let Some(b) = &mut backdrop {
b.color = self.count_color(handle, b.color, self.current_bg());
}
if tint_selected && self.selected.contains(&handle) {
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();
}
let mut changes = changes.to_vec();
// A table's drawing (its *T block) follows the table's cells, sizes
// and styles; it is left alone while it already matches.
for (handle, kind) in &changes {
if !matches!(kind, ChangeKind::Removed)
&& matches!(self.document.get_entity(*handle), Some(EntityType::Table(_)))
{
self.document.refresh_table_block(*handle);
// A table whose content this app changed (its picture reference
// dropped by the edit, or a new table without one) has its drawing
// (the *T block) redrawn from the cells. Tables keeping their picture
// (moved, restyled, opened from a file) are never redrawn: the block
// may hold authored content the redraw does not reproduce.
if !appearance_only {
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
@@ -4111,6 +4119,9 @@ impl Scene {
material.diffuse =
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)
})
})
@@ -4161,14 +4172,35 @@ impl Scene {
}
/// 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>) {
if self.count_display == display {
return;
}
self.count_display = display;
self.hatch_cache.borrow_mut().clear();
self.recolor_meshes();
self.bump_geometry_no_blocks();
let old = std::mem::replace(&mut self.count_display, display);
let (Some(old), Some(new)) = (old, self.count_display.as_ref()) else {
self.hatch_cache.borrow_mut().clear();
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
@@ -4195,13 +4227,19 @@ impl Scene {
fn apply_refedit_fade(&self, wires: &mut [WireModel], bg: [f32; 4]) {
if let Some(count) = &self.count_display {
for w in wires.iter_mut() {
w.color = match Self::handle_from_wire_name(&w.name) {
Some(h) if count.errors.contains(&h) => count.error_color,
Some(h) if count.counted.contains(&h) => count.color,
_ => crate::scene::cache::block_cache::fade_toward_bg(w.color, bg),
let (color, class) = match Self::handle_from_wire_name(&w.name) {
Some(h) if count.errors.contains(&h) => (count.error_color, 2u64),
Some(h) if count.counted.contains(&h) => (count.color, 1),
_ => (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.render_instance = None;
w.color = color;
// 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 {