forked from 1b-agent/opencadstudio-upstream
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:
1 parent
d830e40bfa
commit
0a26709d13
31 files changed
+214
-60
No files matched your search
Generated
+1
-1
@@ -4206,7 +4206,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "opencadcodec"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/ramox81/cadcodec.git?rev=48b86d0#48b86d08c895f07b53753888363d1d9a6b282305"
|
||||
source = "git+https://github.com/ramox81/cadcodec.git?rev=178b596#178b596ba1606ce6d4ed719cd48551750a0f32a5"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ block2 = "0.5"
|
||||
objc2-foundation = { version = "0.2", features = ["NSArray", "NSURL", "NSString", "NSObject", "NSNotification", "NSThread", "NSEnumerator"] }
|
||||
|
||||
[patch."https://github.com/HakanSeven12/opencadcodec.git"]
|
||||
opencadcodec = { git = "https://github.com/ramox81/cadcodec.git", rev = "48b86d0" }
|
||||
opencadcodec = { git = "https://github.com/ramox81/cadcodec.git", rev = "178b596" }
|
||||
|
||||
[patch.crates-io]
|
||||
iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
|
||||
|
||||
@@ -5955,3 +5955,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5943,3 +5943,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5940,3 +5940,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5941,3 +5941,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5943,3 +5943,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5944,3 +5944,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5943,3 +5943,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5940,3 +5940,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5942,3 +5942,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5931,3 +5931,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5942,3 +5942,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5942,3 +5942,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5931,3 +5931,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5942,3 +5942,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5941,3 +5941,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5942,3 +5942,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5943,3 +5943,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5944,3 +5944,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5866,3 +5866,4 @@ count-feature =
|
||||
.toolbar-count = Sayım: __ocs_fmt_0__
|
||||
.overlap = Üst üste binen nesne
|
||||
.show-instances = Örnekleri göster
|
||||
.geometries = Geometriler: __ocs_fmt_0__
|
||||
@@ -5926,3 +5926,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
@@ -5938,3 +5938,4 @@ count-feature =
|
||||
.toolbar-count = Count: __ocs_fmt_0__
|
||||
.overlap = Overlapping object
|
||||
.show-instances = Show Count Instances
|
||||
.geometries = Geometries: __ocs_fmt_0__
|
||||
+31
-17
@@ -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);
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -3035,6 +3035,7 @@ impl OpenCADStudio {
|
||||
&self.count_palette,
|
||||
tab.count.as_ref(),
|
||||
&tab.scene.document,
|
||||
tab.scene.geometry_epoch,
|
||||
width,
|
||||
auto_collapse,
|
||||
),
|
||||
|
||||
@@ -5545,6 +5545,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
|
||||
"Count: {}" => Some(("count-feature", "toolbar-count")),
|
||||
"Overlapping object" => Some(("count-feature", "overlap")),
|
||||
"Show Count Instances" => Some(("count-feature", "show-instances")),
|
||||
"Geometries: {}" => Some(("count-feature", "geometries")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -144,8 +144,16 @@ impl CadCommand for CountCommand {
|
||||
}
|
||||
|
||||
fn on_selection_complete(&mut self, handles: Vec<Handle>) -> CmdResult {
|
||||
// The selection arrives whole after every pick: `1 found`, then
|
||||
// `1 found, 2 total`.
|
||||
let found = handles.iter().filter(|h| !self.targets.contains(h)).count();
|
||||
let first = self.targets.is_empty();
|
||||
self.targets = handles;
|
||||
CmdResult::ReportMeasurement(format!("{} found", self.targets.len()))
|
||||
CmdResult::ReportMeasurement(if first {
|
||||
format!("{found} found")
|
||||
} else {
|
||||
format!("{found} found, {} total", self.targets.len())
|
||||
})
|
||||
}
|
||||
|
||||
fn needs_entity_pick(&self) -> bool {
|
||||
@@ -162,11 +170,13 @@ impl CadCommand for CountCommand {
|
||||
|
||||
fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult {
|
||||
match self.picked.take() {
|
||||
Some(EntityType::LwPolyline(pl)) if pl.is_closed && pl.vertices.len() >= 3 => {
|
||||
Some(EntityType::LwPolyline(pl)) if codec::count::valid_boundary(&pl) => {
|
||||
self.area_done(format!("O {:X}", handle.value()))
|
||||
}
|
||||
// Anything else: the prompt again.
|
||||
_ => CmdResult::NeedPoint,
|
||||
_ => CmdResult::CancelWithMessage(
|
||||
"Invalid count area boundary object. Select a closed polyline consisting of line segments and does not intersect itself."
|
||||
.into(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,6 +254,8 @@ pub struct CountTableCommand {
|
||||
blocks: Vec<String>,
|
||||
others: [usize; 3],
|
||||
names: Vec<String>,
|
||||
/// Listing lines still to show after `Press ENTER to continue:`.
|
||||
pending: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
@@ -251,38 +263,75 @@ enum TableStep {
|
||||
Names,
|
||||
List,
|
||||
Point,
|
||||
Page,
|
||||
}
|
||||
|
||||
impl CountTableCommand {
|
||||
pub fn new(blocks: Vec<String>, others: [usize; 3]) -> Self {
|
||||
Self { step: TableStep::Names, blocks, others, names: Vec::new() }
|
||||
Self { step: TableStep::Names, blocks, others, names: Vec::new(), pending: Vec::new() }
|
||||
}
|
||||
|
||||
/// The palette's Create Table: the blocks are chosen, only the point is asked.
|
||||
pub fn placing(names: Vec<String>) -> Self {
|
||||
Self { step: TableStep::Point, blocks: Vec::new(), others: [0; 3], names }
|
||||
Self { step: TableStep::Point, blocks: Vec::new(), others: [0; 3], names, pending: Vec::new() }
|
||||
}
|
||||
|
||||
/// `?`: the defined blocks matching the pattern and the summary.
|
||||
fn listing(&self, pattern: &str) -> String {
|
||||
/// `?`: the defined blocks matching the pattern, paged by 22 lines
|
||||
/// (`Press ENTER to continue:` in between), then the summary.
|
||||
fn listing(&mut self, pattern: &str) -> CmdResult {
|
||||
let pattern = if pattern.trim().is_empty() { "*" } else { pattern.trim() };
|
||||
let mut out = String::from("Defined blocks.");
|
||||
for name in &self.blocks {
|
||||
if pattern.split(',').any(|p| crate::io::xref_model::wildcard_match(name, p.trim())) {
|
||||
out.push_str(&format!("\n \"{name}\""));
|
||||
}
|
||||
let mut names: Vec<&String> = self
|
||||
.blocks
|
||||
.iter()
|
||||
.filter(|name| pattern.split(',').any(|p| crate::io::xref_model::wildcard_match(name, p.trim())))
|
||||
.collect();
|
||||
names.sort_by_key(|name| list_order(name));
|
||||
self.pending = std::iter::once(String::new())
|
||||
.chain(std::iter::once("Defined blocks.".to_string()))
|
||||
.chain(names.iter().map(|name| format!(" {:<31}", format!("\"{name}\""))))
|
||||
.collect();
|
||||
self.next_page()
|
||||
}
|
||||
|
||||
fn next_page(&mut self) -> CmdResult {
|
||||
let page: Vec<String> = self.pending.drain(..self.pending.len().min(22)).collect();
|
||||
if !self.pending.is_empty() {
|
||||
self.step = TableStep::Page;
|
||||
return CmdResult::ReportMeasurement(page.join("
|
||||
"));
|
||||
}
|
||||
out.push_str(&format!(
|
||||
"\n\nUser External Dependent Unnamed\nBlocks References Blocks Blocks\n{:>5}{:>11}{:>13}{:>12}",
|
||||
CmdResult::Measurement(format!(
|
||||
"{}
|
||||
|
||||
User External Dependent Unnamed
|
||||
Blocks References Blocks Blocks
|
||||
{:>5}{:>10}{:>12}{:>12}
|
||||
",
|
||||
page.join("
|
||||
"),
|
||||
self.blocks.len(),
|
||||
self.others[0],
|
||||
self.others[1],
|
||||
self.others[2]
|
||||
));
|
||||
out
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Block listing order: `_` first, then `-`, space and other punctuation,
|
||||
/// digits, and letters without case.
|
||||
fn list_order(name: &str) -> Vec<(u32, u32)> {
|
||||
name.chars()
|
||||
.map(|c| match c {
|
||||
'_' => (0, 0),
|
||||
'-' => (1, 0),
|
||||
' ' => (2, 0),
|
||||
'0'..='9' => (4, c as u32),
|
||||
c if c.is_alphabetic() => (5, c.to_lowercase().next().unwrap_or(c) as u32),
|
||||
c => (3, c as u32),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl CadCommand for CountTableCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"COUNTTABLE"
|
||||
@@ -293,6 +342,7 @@ impl CadCommand for CountTableCommand {
|
||||
TableStep::Names => "Enter block name(s) to include or [?] <all blocks>:".into(),
|
||||
TableStep::List => "Enter block(s) to list <*>:".into(),
|
||||
TableStep::Point => "Specify insertion point:".into(),
|
||||
TableStep::Page => "Press ENTER to continue:".into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +377,8 @@ impl CadCommand for CountTableCommand {
|
||||
self.step = TableStep::Point;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
TableStep::List => CmdResult::Measurement(self.listing(text)),
|
||||
TableStep::List => self.listing(text),
|
||||
TableStep::Page => self.next_page(),
|
||||
TableStep::Point => return None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1451,6 +1451,7 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
}
|
||||
m.color = self.count_color(handle, m.color, self.current_bg());
|
||||
if tint_selected && self.selected.contains(&handle) {
|
||||
m.color = [0.15, 0.55, 1.00, m.color[3]];
|
||||
}
|
||||
@@ -1752,6 +1753,7 @@ impl Scene {
|
||||
)
|
||||
});
|
||||
}
|
||||
model.color = self.count_color(context.root_handle, model.color, hatch_bg);
|
||||
if tint_selected && self.selected.contains(&context.root_handle) {
|
||||
model.color = [0.15, 0.55, 1.00, model.color[3]];
|
||||
}
|
||||
|
||||
+31
-2
@@ -4007,6 +4007,9 @@ impl Scene {
|
||||
);
|
||||
}
|
||||
}
|
||||
if self.meshes.contains_key(&h) {
|
||||
material.diffuse = self.count_color(h, material.diffuse, bg);
|
||||
}
|
||||
(h, material)
|
||||
})
|
||||
})
|
||||
@@ -4108,9 +4111,28 @@ impl Scene {
|
||||
return;
|
||||
}
|
||||
self.count_display = display;
|
||||
self.hatch_cache.borrow_mut().clear();
|
||||
self.recolor_meshes();
|
||||
self.bump_geometry_no_blocks();
|
||||
}
|
||||
|
||||
/// The colour count mode gives an object drawn as part of `handle` (a
|
||||
/// top-level entity): the count colours, else faded; unchanged outside
|
||||
/// count mode.
|
||||
pub(crate) fn count_color(&self, handle: Handle, color: [f32; 4], bg: [f32; 4]) -> [f32; 4] {
|
||||
let Some(count) = &self.count_display else {
|
||||
return color;
|
||||
};
|
||||
let [r, g, b, _] = if count.errors.contains(&handle) {
|
||||
count.error_color
|
||||
} else if count.counted.contains(&handle) {
|
||||
count.color
|
||||
} else {
|
||||
return crate::scene::cache::block_cache::fade_toward_bg(color, bg);
|
||||
};
|
||||
[r, g, b, color[3]]
|
||||
}
|
||||
|
||||
/// Fade the colours of wires that belong to entities outside the REFEDIT
|
||||
/// keep set (no-op when not editing). The geometry is untouched, so
|
||||
/// hit-testing still works on faded entities. In count mode the counted
|
||||
@@ -4118,8 +4140,7 @@ 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() {
|
||||
let h = Self::handle_from_wire_name(&w.name);
|
||||
w.color = match h {
|
||||
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),
|
||||
@@ -8745,6 +8766,14 @@ impl Scene {
|
||||
self.material_base_dir.as_deref(),
|
||||
);
|
||||
}
|
||||
if self.count_display.is_some() {
|
||||
let own = transformed.instance_color.or(set.display_color()).unwrap_or([1.0; 4]);
|
||||
let color = self.count_color(context.root_handle, own, self.current_bg());
|
||||
transformed.instance_color = Some(color);
|
||||
if let Some(material) = transformed.material.as_mut() {
|
||||
material.diffuse = color;
|
||||
}
|
||||
}
|
||||
transformed.instance_handle = Some(context.root_handle);
|
||||
out.push(transformed);
|
||||
},
|
||||
|
||||
@@ -33,7 +33,8 @@ pub enum CountTarget {
|
||||
matching: [bool; 3],
|
||||
picked: bool,
|
||||
},
|
||||
/// Several picked objects counted as one group.
|
||||
/// Picked objects (several, or one that is not a reference): the copies
|
||||
/// of the group are counted.
|
||||
Group(Vec<Handle>),
|
||||
}
|
||||
|
||||
@@ -50,6 +51,10 @@ pub struct CountMode {
|
||||
pub cursor: Option<usize>,
|
||||
/// The scene geometry epoch the colouring was computed at.
|
||||
pub epoch: u64,
|
||||
/// The area Entire model space set aside, for a later Current area.
|
||||
pub remembered: Option<(Vec<[f64; 2]>, Option<(Handle, bool)>)>,
|
||||
/// The result and count at `epoch` (recomputed when the drawing changes).
|
||||
pub cache: Option<(CountResult, usize)>,
|
||||
}
|
||||
|
||||
/// A count mode's result: the counted references and the overlapping
|
||||
@@ -80,13 +85,35 @@ impl CountMode {
|
||||
}
|
||||
|
||||
pub fn result(&self, doc: &CadDocument) -> CountResult {
|
||||
match &self.cache {
|
||||
Some((result, _)) => result.clone(),
|
||||
None => self.compute(doc).0,
|
||||
}
|
||||
}
|
||||
|
||||
/// What the count shows: the counted references, or the copies of a group.
|
||||
pub fn count(&self, doc: &CadDocument) -> usize {
|
||||
match &self.cache {
|
||||
Some((_, n)) => *n,
|
||||
None => self.compute(doc).1,
|
||||
}
|
||||
}
|
||||
|
||||
/// The result and the count, worked out from the drawing.
|
||||
pub fn compute(&self, doc: &CadDocument) -> (CountResult, usize) {
|
||||
if let Some(CountTarget::Group(handles)) = &self.target {
|
||||
let groups = codec::count::group_matches(doc, handles, self.area.as_deref());
|
||||
let result = CountResult { counted: groups.concat(), errors: Vec::new(), overlapped: Vec::new() };
|
||||
return (result, groups.len());
|
||||
}
|
||||
let result = self.block_result(doc);
|
||||
let n = result.counted.len();
|
||||
(result, n)
|
||||
}
|
||||
|
||||
fn block_result(&self, doc: &CadDocument) -> CountResult {
|
||||
match &self.target {
|
||||
None => CountResult::default(),
|
||||
Some(CountTarget::Group(handles)) => CountResult {
|
||||
counted: handles.iter().copied().filter(|h| doc.get_entity(*h).is_some()).collect(),
|
||||
errors: Vec::new(),
|
||||
overlapped: Vec::new(),
|
||||
},
|
||||
None | Some(CountTarget::Group(_)) => CountResult::default(),
|
||||
Some(CountTarget::Block { name, .. }) => {
|
||||
let instances = self.instances(doc);
|
||||
let key = self.key(&instances);
|
||||
@@ -100,15 +127,6 @@ impl CountMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// What the count shows: the counted references, or 1 for a group whose
|
||||
/// objects all exist.
|
||||
pub fn count(&self, doc: &CadDocument) -> usize {
|
||||
match &self.target {
|
||||
Some(CountTarget::Group(handles)) => usize::from(handles.iter().all(|h| doc.get_entity(*h).is_some())),
|
||||
_ => self.result(doc).counted.len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The name the toolbar and the command line show for the target.
|
||||
pub fn target_name(&self) -> Option<String> {
|
||||
match &self.target {
|
||||
@@ -137,6 +155,8 @@ pub struct CountPalette {
|
||||
pub color: i16,
|
||||
pub error_color: i16,
|
||||
pub service: bool,
|
||||
/// The list's references for (document, geometry epoch, area).
|
||||
pub list_cache: std::cell::RefCell<Option<(usize, u64, Option<Vec<[f64; 2]>>, std::rc::Rc<Vec<BlockInstance>>)>>,
|
||||
}
|
||||
|
||||
impl Default for CountPalette {
|
||||
@@ -153,6 +173,7 @@ impl Default for CountPalette {
|
||||
color: 3,
|
||||
error_color: 1,
|
||||
service: true,
|
||||
list_cache: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -434,7 +455,12 @@ fn mode_view<'a>(palette: &'a CountPalette, mode: &'a CountMode, doc: &CadDocume
|
||||
let mut details = column![button(
|
||||
row![
|
||||
crate::ui::icons::themed_success(CHECK_ICON, 12.0),
|
||||
text(format!("{name}: {}", mode.count(doc))).size(12).width(Fill),
|
||||
text(match mode.target {
|
||||
Some(CountTarget::Group(_)) => crate::tf!("Geometries: {}", mode.count(doc)).into_owned(),
|
||||
_ => format!("{name}: {}", mode.count(doc)),
|
||||
})
|
||||
.size(12)
|
||||
.width(Fill),
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center),
|
||||
@@ -488,16 +514,25 @@ pub fn view<'a>(
|
||||
palette: &'a CountPalette,
|
||||
mode: Option<&'a CountMode>,
|
||||
doc: &'a CadDocument,
|
||||
epoch: u64,
|
||||
width: f32,
|
||||
auto_collapse: bool,
|
||||
) -> Element<'a, Message> {
|
||||
let title_bar = crate::ui::dock::title_bar(PanelId::Count, crate::t!("Count palette").into_owned(), auto_collapse);
|
||||
// ponytail: the list is recounted on every view; cache by geometry epoch
|
||||
// if very large drawings make the palette slow.
|
||||
let body = match mode {
|
||||
Some(mode) if mode.target.is_some() => mode_view(palette, mode, doc),
|
||||
_ => {
|
||||
let instances = codec::count::block_instances(doc, mode.and_then(|m| m.area.as_deref()));
|
||||
let area = mode.and_then(|m| m.area.clone());
|
||||
let key = (doc as *const CadDocument as usize, epoch);
|
||||
let mut cache = palette.list_cache.borrow_mut();
|
||||
let instances = match cache.as_ref() {
|
||||
Some((d, e, a, list)) if (*d, *e) == key && *a == area => list.clone(),
|
||||
_ => {
|
||||
let list = std::rc::Rc::new(codec::count::block_instances(doc, area.as_deref()));
|
||||
*cache = Some((key.0, key.1, area, list.clone()));
|
||||
list
|
||||
}
|
||||
};
|
||||
list_view(palette, &instances)
|
||||
}
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user