Count: measured messages and listing, groups, Current area, boundary checks, fills and solids recoloured

- COUNTCOLOR / COUNTERRORCOLOR / COUNTSERVICE reject any other value with
  the range message; COUNTTABLE ? lists in the reference order, padded,
  22 lines a page with Press ENTER to continue:, then the totals.
- Target picks report N found / N found, M total; several objects (or one
  that is not a block reference) count their copies, shown as Geometries
  in the palette.
- Current area is the area in use (kept aside by Entire model space),
  otherwise all of model space; an invalid Object boundary ends with the
  reference message.
- Count colours reach solids and fills; the palette list and the count
  are recomputed only when the drawing changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-05 11:36:24 +03:00
1 parent d830e40bfa
commit 0a26709d13
31 files changed
+214 -60

No files matched your search

+31 -17
View File
@@ -216,7 +216,7 @@ impl OpenCADStudio {
self.persist_settings_if_changed();
self.apply_count_display(i);
}
Ok(_) => {
_ => {
self.command_line.push_error(&if max == 1 {
"Requires 0 or 1 only.".to_string()
} else {
@@ -224,10 +224,6 @@ impl OpenCADStudio {
});
ask(self);
}
Err(_) => {
self.command_line.push_error("Requires an integer value.");
ask(self);
}
}
}
@@ -249,6 +245,10 @@ impl OpenCADStudio {
/// Recolour the drawing for count mode: counted references, duplicates,
/// the rest faded (nothing changes without a target).
pub(in crate::app) fn apply_count_display(&mut self, i: usize) {
let computed = self.tabs[i].count.as_ref().map(|m| m.compute(&self.tabs[i].scene.document));
if let (Some(mode), Some(c)) = (self.tabs[i].count.as_mut(), computed) {
mode.cache = Some(c);
}
let display = self.tabs[i].count.as_ref().filter(|m| m.target.is_some()).map(|m| {
let r = m.result(&self.tabs[i].scene.document);
crate::scene::CountDisplay {
@@ -278,6 +278,7 @@ impl OpenCADStudio {
pub(in crate::app) fn close_count(&mut self, i: usize) {
if let Some(mode) = self.tabs[i].count.take() {
self.drop_count_boundary(i, mode.boundary);
self.drop_count_boundary(i, mode.remembered.and_then(|(_, b)| b));
}
self.tabs[i].scene.set_count_display(None);
}
@@ -310,10 +311,13 @@ impl OpenCADStudio {
let mode = self.tabs[i].count.as_ref();
Some((mode.and_then(|m| m.area.clone()), mode.and_then(|m| m.boundary)))
}
"C" => {
let (x0, y0, x1, y1) = self.display_plot_window()?;
Some((Some(vec![[x0, y0], [x1, y0], [x1, y1], [x0, y1]]), None))
}
// Current area: the count area in use (also one Entire model space
// set aside), else all of model space.
"C" => Some(match self.tabs[i].count.as_ref() {
Some(m) if m.area.is_some() => (m.area.clone(), m.boundary),
Some(m) => m.remembered.clone().map_or((None, None), |(a, b)| (Some(a), b)),
None => (None, None),
}),
"R" => {
let p = points();
let (a, b) = (p.first()?, p.get(1)?);
@@ -404,11 +408,18 @@ impl OpenCADStudio {
};
// The picked targets do not stay selected.
self.tabs[i].scene.deselect_all();
let old = self.tabs[i].count.take();
if let Some(old) = old.as_ref().filter(|o| o.boundary != boundary) {
self.drop_count_boundary(i, old.boundary);
// Entire model space counts without the area but keeps it for a later
// Current area; a new area replaces it.
let mut remembered = None;
if let Some(old) = self.tabs[i].count.take() {
let old_area = old.area.map(|a| (a, old.boundary)).or(old.remembered);
if area.is_none() {
remembered = old_area;
} else if let Some((_, b)) = old_area.filter(|(_, b)| *b != boundary) {
self.drop_count_boundary(i, b);
}
}
let mode = CountMode { area, boundary, target, cursor: None, epoch: 0 };
let mode = CountMode { area, boundary, target, remembered, ..CountMode::default() };
if let Some(name) = mode.target_name() {
let n = mode.count(&self.tabs[i].scene.document);
self.command_line.push_output(&format!("{name} ...... {n}"));
@@ -566,13 +577,16 @@ impl OpenCADStudio {
self.tabs[i].dirty = true;
}
/// Zoom to a reference (← / →, an error of the report).
/// Zoom to a counted object (← / →, an error of the report).
fn zoom_to_insert(&mut self, i: usize, h: Handle) {
let doc = &self.tabs[i].scene.document;
let Some(EntityType::Insert(ins)) = doc.get_entity(h) else { return };
let Some(corners) = codec::count::insert_corners(doc, ins, 0) else { return };
let Some(entity) = doc.get_entity(h) else { return };
let outline = codec::count::entity_outline(doc, entity);
if outline.iter().all(|part| part.is_empty()) {
return;
}
let (mut lo, mut hi) = (glam::Vec3::splat(f32::MAX), glam::Vec3::splat(f32::MIN));
for c in corners {
for c in outline.into_iter().flatten() {
let p = glam::Vec3::new(c.x as f32, c.y as f32, c.z as f32);
lo = lo.min(p);
hi = hi.max(p);
+1 -1
View File
@@ -5544,7 +5544,7 @@ impl OpenCADStudio {
/// Current visible rectangle in the active space. The result is deliberately
/// not clamped to the paper sheet: Display and Window may include the grey
/// canvas outside the sheet, matching Model-space plotting.
pub(in crate::app) fn display_plot_window(&self) -> Option<(f64, f64, f64, f64)> {
fn display_plot_window(&self) -> Option<(f64, f64, f64, f64)> {
let scene = &self.tabs[self.active_tab].scene;
let (canvas_w, canvas_h) = scene.selection.borrow().vp_size;
let viewport = if scene.current_layout == "Model" {
+1
View File
@@ -3035,6 +3035,7 @@ impl OpenCADStudio {
&self.count_palette,
tab.count.as_ref(),
&tab.scene.document,
tab.scene.geometry_epoch,
width,
auto_collapse,
),