perf(hash): swap std HashMap/HashSet for rustc-hash FxHash
Phase 4.1. Handle / u64 keys dominate the hot maps (block_defn, hatch / image / mesh caches, draw-depth map, viewport wire caches); the default SipHash is wasted work on integer keys. Alias the std types to rustc_hash::FxHashMap / FxHashSet crate-wide so every map and set — including the cross-module draw-depth map shared between scene and the GPU pipelines — uses the faster hasher consistently. Mechanical: no behavioural change. Constructors moved from ::new() to ::default() (Fx variants carry no RandomState). Build + 13 tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
3098523b8d
commit
70702d063f
30 changed files with 102 additions and 98 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -28,6 +28,7 @@ dependencies = [
|
|||
"printpdf",
|
||||
"rayon",
|
||||
"rfd",
|
||||
"rustc-hash 2.1.2",
|
||||
"truck-meshalgo",
|
||||
"truck-modeling",
|
||||
"truck-polymesh",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ rayon = "1"
|
|||
ureq = { version = "3", default-features = false, features = ["rustls"] }
|
||||
inventory = "0.3"
|
||||
truck-shapeops = "0.4"
|
||||
# Fast non-cryptographic hasher. Handle/u64 keys dominate the hot block,
|
||||
# hatch, draw-depth and wire caches; the default SipHash is overkill there.
|
||||
rustc-hash = "2"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] }
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ matters.
|
|||
|
||||
## Phase 4 — Allocation & Memory
|
||||
|
||||
### 4.1 Swap `HashMap` for `rustc-hash::FxHashMap`
|
||||
### 4.1 Swap `HashMap` for `rustc-hash::FxHashMap` ✅ DONE
|
||||
|
||||
`Handle` is an integer wrapper; the default `SipHash` is overkill.
|
||||
`FxHashMap` gives 20-40 % in hash-heavy sites (block_cache, hatches /
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ impl OpenCADStudio {
|
|||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
} else {
|
||||
let layers: std::collections::HashSet<String> = self.tabs[i]
|
||||
let layers: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
|
|
@ -153,7 +153,7 @@ impl OpenCADStudio {
|
|||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
} else {
|
||||
let layers: std::collections::HashSet<String> = self.tabs[i]
|
||||
let layers: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
|
|
@ -188,7 +188,7 @@ impl OpenCADStudio {
|
|||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
} else {
|
||||
let layers: std::collections::HashSet<String> = self.tabs[i]
|
||||
let layers: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
|
|
@ -278,7 +278,7 @@ impl OpenCADStudio {
|
|||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
} else {
|
||||
let layers: std::collections::HashSet<String> = self.tabs[i]
|
||||
let layers: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
|
|
@ -299,7 +299,7 @@ impl OpenCADStudio {
|
|||
|
||||
// LAYISO — turn off all layers except those used by selected entities
|
||||
"LAYISO" => {
|
||||
let sel_layers: std::collections::HashSet<String> = self.tabs[i]
|
||||
let sel_layers: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
|
|
@ -3090,10 +3090,10 @@ impl OpenCADStudio {
|
|||
// including ones not yet in the table, which sort by
|
||||
// their own handle. (min_eff, max_eff) over siblings.
|
||||
let fb_baseline: Option<(u64, u64)> = if to_front_opt.is_some() {
|
||||
let selected_set: std::collections::HashSet<u64> =
|
||||
let selected_set: rustc_hash::FxHashSet<u64> =
|
||||
selected.iter().map(|h| h.value()).collect();
|
||||
let doc_ref = &self.tabs[i].scene.document;
|
||||
let overrides: std::collections::HashMap<u64, u64> = doc_ref
|
||||
let overrides: rustc_hash::FxHashMap<u64, u64> = doc_ref
|
||||
.objects
|
||||
.values()
|
||||
.find_map(|obj| {
|
||||
|
|
@ -4358,7 +4358,7 @@ impl OpenCADStudio {
|
|||
let all = sub == "ALL" || sub.is_empty();
|
||||
|
||||
// Collect names in use (immutable borrows — done in their own scope)
|
||||
let used_layers: std::collections::HashSet<String> = self.tabs[i]
|
||||
let used_layers: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
|
|
@ -4371,7 +4371,7 @@ impl OpenCADStudio {
|
|||
}
|
||||
})
|
||||
.collect();
|
||||
let used_text_styles: std::collections::HashSet<String> = self.tabs[i]
|
||||
let used_text_styles: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
|
|
@ -4382,7 +4382,7 @@ impl OpenCADStudio {
|
|||
})
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
let used_linetypes: std::collections::HashSet<String> = self.tabs[i]
|
||||
let used_linetypes: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ pub(super) fn entities_centroid(wires: &[WireModel]) -> glam::Vec3 {
|
|||
|
||||
/// Generate the next available auto group name ("*A1", "*A2", …).
|
||||
pub(super) fn next_group_auto_name(scene: &crate::scene::Scene) -> String {
|
||||
let existing: std::collections::HashSet<String> =
|
||||
let existing: rustc_hash::FxHashSet<String> =
|
||||
scene.groups().map(|g| g.name.clone()).collect();
|
||||
for n in 1..=9999 {
|
||||
let name = format!("*A{n}");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use super::{document::HistorySnapshot, OpenCADStudio};
|
||||
use std::collections::HashSet;
|
||||
use rustc_hash::FxHashSet as HashSet;
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(super) fn history_label_from_active_cmd(&self, i: usize, fallback: &'static str) -> String {
|
||||
|
|
|
|||
|
|
@ -378,7 +378,7 @@ pub(super) struct OpenCADStudio {
|
|||
|
||||
// ── Keyboard Shortcut Editor ──────────────────────────────────────────
|
||||
/// User-defined function-key overrides: "F3" → command string.
|
||||
shortcut_overrides: std::collections::HashMap<String, String>,
|
||||
shortcut_overrides: rustc_hash::FxHashMap<String, String>,
|
||||
|
||||
// ── Layout Manager Panel ──────────────────────────────────────────────
|
||||
layout_manager_selected: String,
|
||||
|
|
@ -1324,7 +1324,7 @@ impl OpenCADStudio {
|
|||
// Color scheme (default: dark CAD-style)
|
||||
active_theme: Theme::Dark,
|
||||
// Keyboard shortcuts
|
||||
shortcut_overrides: std::collections::HashMap::new(),
|
||||
shortcut_overrides: rustc_hash::FxHashMap::default(),
|
||||
// Layout Manager
|
||||
layout_manager_selected: "Model".to_string(),
|
||||
layout_manager_rename_buf: String::new(),
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ impl OpenCADStudio {
|
|||
self.tabs[i].scene.meshes = caches.meshes;
|
||||
// Invalidate the wire cache so the new document is tessellated.
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
self.tabs[i].scene.selected = std::collections::HashSet::new();
|
||||
self.tabs[i].scene.selected = rustc_hash::FxHashSet::default();
|
||||
self.tabs[i].scene.preview_wires = vec![];
|
||||
self.tabs[i].scene.current_layout = "Model".to_string();
|
||||
crate::linetypes::populate_document(&mut self.tabs[i].scene.document);
|
||||
|
|
|
|||
|
|
@ -808,7 +808,7 @@ pub trait DimensionTess {
|
|||
line_weight_px: f32,
|
||||
world_offset: [f64; 3],
|
||||
anno_scale: f32,
|
||||
selected_set: &std::collections::HashSet<acadrust::Handle>,
|
||||
selected_set: &rustc_hash::FxHashSet<acadrust::Handle>,
|
||||
active_viewport: Option<acadrust::Handle>,
|
||||
bg_color: [f32; 4],
|
||||
view_aabb: Option<[f32; 4]>,
|
||||
|
|
@ -826,7 +826,7 @@ impl DimensionTess for Dimension {
|
|||
line_weight_px: f32,
|
||||
world_offset: [f64; 3],
|
||||
anno_scale: f32,
|
||||
selected_set: &std::collections::HashSet<acadrust::Handle>,
|
||||
selected_set: &rustc_hash::FxHashSet<acadrust::Handle>,
|
||||
active_viewport: Option<acadrust::Handle>,
|
||||
bg_color: [f32; 4],
|
||||
view_aabb: Option<[f32; 4]>,
|
||||
|
|
@ -862,7 +862,7 @@ fn tessellate_dimension_inner(
|
|||
// LOD hints — when present, synthesised dim text routes through the
|
||||
// top-level LOD ladder (baseline / greek / full) instead of the truck
|
||||
// path so far-out drawings collapse to a colored rect or baseline.
|
||||
selected_set: &std::collections::HashSet<acadrust::Handle>,
|
||||
selected_set: &rustc_hash::FxHashSet<acadrust::Handle>,
|
||||
active_viewport: Option<acadrust::Handle>,
|
||||
bg_color: [f32; 4],
|
||||
view_aabb: Option<[f32; 4]>,
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ impl TruckConvertible for Table {
|
|||
// hidden borders disappear from the grid. Cells with no style still
|
||||
// emit the standard four borders. To avoid drawing each shared edge
|
||||
// twice we coalesce the segments by their (start, end) coordinates.
|
||||
use std::collections::HashSet;
|
||||
let mut emitted: HashSet<(i32, i32, i32, i32)> = HashSet::new();
|
||||
use rustc_hash::FxHashSet as HashSet;
|
||||
let mut emitted: HashSet<(i32, i32, i32, i32)> = HashSet::default();
|
||||
let try_add = |a: Vec3, b: Vec3, vis: bool, emitted: &mut HashSet<(i32, i32, i32, i32)>, pts: &mut Vec<[f32; 3]>| {
|
||||
if !vis {
|
||||
return;
|
||||
|
|
@ -334,7 +334,7 @@ pub fn tessellate_table(
|
|||
use crate::scene::tess_util::aci_to_rgba;
|
||||
use crate::scene::wire_model::WireModel;
|
||||
use acadrust::types::Color;
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
|
||||
if tab.rows.is_empty() || tab.columns.is_empty() {
|
||||
return Vec::new();
|
||||
|
|
@ -438,10 +438,10 @@ pub fn tessellate_table(
|
|||
};
|
||||
|
||||
// Accumulators keyed by quantised colour (+ weight for borders).
|
||||
let mut fills: HashMap<[u8; 4], ([f32; 4], Vec<[f32; 3]>)> = HashMap::new();
|
||||
let mut texts: HashMap<[u8; 4], ([f32; 4], Vec<[f32; 3]>)> = HashMap::new();
|
||||
let mut borders: HashMap<([u8; 4], u32), ([f32; 4], f32, Vec<[f32; 3]>)> = HashMap::new();
|
||||
let mut emitted: std::collections::HashSet<(i32, i32, i32, i32)> = std::collections::HashSet::new();
|
||||
let mut fills: HashMap<[u8; 4], ([f32; 4], Vec<[f32; 3]>)> = HashMap::default();
|
||||
let mut texts: HashMap<[u8; 4], ([f32; 4], Vec<[f32; 3]>)> = HashMap::default();
|
||||
let mut borders: HashMap<([u8; 4], u32), ([f32; 4], f32, Vec<[f32; 3]>)> = HashMap::default();
|
||||
let mut emitted: rustc_hash::FxHashSet<(i32, i32, i32, i32)> = rustc_hash::FxHashSet::default();
|
||||
let sel_col = WireModel::SELECTED;
|
||||
|
||||
let mut add_edge =
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
//! STB files follow the same format but use named styles instead of
|
||||
//! ACI indices; they are read into a `Vec<NamedPlotStyle>`.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ impl PlotStyleTable {
|
|||
name: name.into(),
|
||||
is_stb: false,
|
||||
aci_entries: (0..=255).map(|_| PlotStyleEntry::default()).collect(),
|
||||
named_entries: HashMap::new(),
|
||||
named_entries: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +202,7 @@ fn compress_ctb(text: &[u8]) -> Result<Vec<u8>, String> {
|
|||
fn parse_plot_style_text(text: &str, name: String, is_stb: bool) -> Result<PlotStyleTable, String> {
|
||||
let mut aci_entries: Vec<PlotStyleEntry> =
|
||||
(0..=255).map(|_| PlotStyleEntry::default()).collect();
|
||||
let mut named_entries: HashMap<String, PlotStyleEntry> = HashMap::new();
|
||||
let mut named_entries: HashMap<String, PlotStyleEntry> = HashMap::default();
|
||||
let mut style_index: usize = 1; // CTB: 1-based ACI index
|
||||
let mut current: Option<PlotStyleEntry> = None;
|
||||
let mut current_name: String = String::new();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use acadrust::entities::{Block, BlockEnd};
|
|||
use acadrust::tables::TableEntry;
|
||||
use acadrust::types::{Handle, Vector3};
|
||||
use acadrust::{CadDocument, EntityType};
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Status of an external reference block.
|
||||
|
|
@ -160,7 +160,7 @@ fn merge_xref_into_block(
|
|||
// Prefix every xref layer (including "0"). Entity layer references are
|
||||
// remapped below so the resolver finds the merged copy. Host layers
|
||||
// (incl. its own "0") are untouched — no collisions.
|
||||
let mut layer_map: HashMap<String, String> = HashMap::new();
|
||||
let mut layer_map: HashMap<String, String> = HashMap::default();
|
||||
for layer in xref_doc.layers.iter() {
|
||||
let old = layer.name.clone();
|
||||
let new = format!("{}|{}", prefix, old);
|
||||
|
|
@ -174,7 +174,7 @@ fn merge_xref_into_block(
|
|||
// ── Linetypes ───────────────────────────────────────────────────────
|
||||
// Skip the three sentinel names — "ByLayer" / "ByBlock" / "Continuous"
|
||||
// are magic strings the resolver matches verbatim in both docs.
|
||||
let mut linetype_map: HashMap<String, String> = HashMap::new();
|
||||
let mut linetype_map: HashMap<String, String> = HashMap::default();
|
||||
for lt in xref_doc.line_types.iter() {
|
||||
let old = lt.name.clone();
|
||||
if is_sentinel_linetype(&old) {
|
||||
|
|
@ -195,8 +195,8 @@ fn merge_xref_into_block(
|
|||
//
|
||||
// Tracks (xref-doc BR handle → host BR handle) so entities owned by
|
||||
// a nested xref block can be routed to the right host block_record.
|
||||
let mut br_handle_map: HashMap<Handle, Handle> = HashMap::new();
|
||||
let mut block_name_map: HashMap<String, String> = HashMap::new();
|
||||
let mut br_handle_map: HashMap<Handle, Handle> = HashMap::default();
|
||||
let mut block_name_map: HashMap<String, String> = HashMap::default();
|
||||
for br in xref_doc.block_records.iter() {
|
||||
// Skip layout block records (*Model_Space, *Paper_Space, *Paper_Space0…)
|
||||
// and any further-nested xrefs (we don't recurse into xref-of-xref).
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
//!
|
||||
//! [`complex_lt`] returns the complex segment catalog for CPU-side rendering.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use acadrust::tables::linetype::{LineType, LineTypeElement};
|
||||
|
|
@ -217,7 +217,7 @@ fn push_element(token: &str, out: &mut Vec<LineTypeElement>) {
|
|||
/// A linetype is "complex" when its A-line contains at least one `[SHAPE,...]`
|
||||
/// element. Simple (dash-only) linetypes are excluded.
|
||||
fn parse_complex(src: &str) -> HashMap<String, ComplexLt> {
|
||||
let mut catalog: HashMap<String, ComplexLt> = HashMap::new();
|
||||
let mut catalog: HashMap<String, ComplexLt> = HashMap::default();
|
||||
let mut current: Option<(String, String)> = None; // (name, description)
|
||||
|
||||
for raw in src.lines() {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
// a visited set so a self-referential block produces a marker rather than
|
||||
// recursing forever.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use acadrust::types::{Color as AcadColor, LineWeight, Transform, Vector3};
|
||||
|
|
@ -206,8 +206,8 @@ impl BlockCache {
|
|||
/// Walk all entities + all block_record contents collecting every distinct
|
||||
/// `block_name` that appears in an Insert (transitively).
|
||||
fn collect_referenced_blocks(doc: &CadDocument) -> Vec<String> {
|
||||
use std::collections::HashSet;
|
||||
let mut seen: HashSet<String> = HashSet::new();
|
||||
use rustc_hash::FxHashSet as HashSet;
|
||||
let mut seen: HashSet<String> = HashSet::default();
|
||||
let mut queue: Vec<String> = Vec::new();
|
||||
|
||||
for entity in doc.entities() {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
//! to 2-D pixel coordinates, then compared against the cursor or selection box.
|
||||
//! This matches the visual result the user sees.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
|
||||
use acadrust::Handle;
|
||||
use glam::{Mat4, Vec3};
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
// Cap height is 9 glyph units, matching the `height / 9.0` text scale used
|
||||
// throughout the renderer.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
// ── Embedded fonts ─────────────────────────────────────────────────────────
|
||||
|
|
@ -154,7 +154,7 @@ fn warn_missing_glyph(font_name: &str, ch: char) {
|
|||
if ch.is_ascii() {
|
||||
return;
|
||||
}
|
||||
let set = WARNED_GLYPHS.get_or_init(|| Mutex::new(HashSet::new()));
|
||||
let set = WARNED_GLYPHS.get_or_init(|| Mutex::new(HashSet::default()));
|
||||
if let Ok(mut guard) = set.lock() {
|
||||
if guard.insert((font_name.to_string(), ch)) {
|
||||
eprintln!(
|
||||
|
|
@ -167,7 +167,7 @@ fn warn_missing_glyph(font_name: &str, ch: char) {
|
|||
|
||||
fn fonts_map() -> &'static HashMap<String, Font> {
|
||||
FONTS.get_or_init(|| {
|
||||
let mut map = HashMap::new();
|
||||
let mut map = HashMap::default();
|
||||
// Register every font under its stem and its `# Name:` header.
|
||||
for (stem, src) in FONTS_SRC {
|
||||
let f = parse_lff(src);
|
||||
|
|
@ -461,12 +461,12 @@ fn parse_lff(src: &str) -> Font {
|
|||
letter_spacing: 3.0,
|
||||
word_spacing: 6.75,
|
||||
line_spacing: 1.0,
|
||||
glyphs: HashMap::new(),
|
||||
shapes: HashMap::new(),
|
||||
glyphs: HashMap::default(),
|
||||
shapes: HashMap::default(),
|
||||
};
|
||||
|
||||
let mut raw: HashMap<char, RawGlyph> = HashMap::new();
|
||||
let mut raw_shapes: HashMap<String, RawGlyph> = HashMap::new();
|
||||
let mut raw: HashMap<char, RawGlyph> = HashMap::default();
|
||||
let mut raw_shapes: HashMap<String, RawGlyph> = HashMap::default();
|
||||
let mut cur: Option<char> = None;
|
||||
let mut cur_name: Option<String> = None;
|
||||
let mut cur_glyph = RawGlyph::default();
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ use truck_modeling::{
|
|||
|
||||
use iced::time::Duration;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
|
@ -256,9 +256,9 @@ struct OffsetPrep {
|
|||
/// `Some` when the model BlockRecord enumerates its entities; the offset
|
||||
/// scan uses this set directly. `None` falls back to the legacy
|
||||
/// permissive owner-based interpretation.
|
||||
mspace_set: Option<std::collections::HashSet<Handle>>,
|
||||
mspace_set: Option<rustc_hash::FxHashSet<Handle>>,
|
||||
any_enumerated: bool,
|
||||
owned_by_other_block: std::collections::HashSet<Handle>,
|
||||
owned_by_other_block: rustc_hash::FxHashSet<Handle>,
|
||||
}
|
||||
|
||||
fn offset_prep(doc: &acadrust::CadDocument, model_block: Handle) -> OffsetPrep {
|
||||
|
|
@ -266,21 +266,21 @@ fn offset_prep(doc: &acadrust::CadDocument, model_block: Handle) -> OffsetPrep {
|
|||
.block_records
|
||||
.iter()
|
||||
.find(|br| br.handle == model_block);
|
||||
let mspace_set: Option<std::collections::HashSet<Handle>> = model_br
|
||||
let mspace_set: Option<rustc_hash::FxHashSet<Handle>> = model_br
|
||||
.filter(|br| !br.entity_handles.is_empty())
|
||||
.map(|br| br.entity_handles.iter().copied().collect());
|
||||
let any_enumerated = doc
|
||||
.block_records
|
||||
.iter()
|
||||
.any(|br| !br.entity_handles.is_empty());
|
||||
let owned_by_other_block: std::collections::HashSet<Handle> = if mspace_set.is_none() {
|
||||
let owned_by_other_block: rustc_hash::FxHashSet<Handle> = if mspace_set.is_none() {
|
||||
doc.block_records
|
||||
.iter()
|
||||
.filter(|br| br.handle != model_block)
|
||||
.flat_map(|br| br.entity_handles.iter().copied())
|
||||
.collect()
|
||||
} else {
|
||||
std::collections::HashSet::new()
|
||||
rustc_hash::FxHashSet::default()
|
||||
};
|
||||
OffsetPrep { mspace_set, any_enumerated, owned_by_other_block }
|
||||
}
|
||||
|
|
@ -718,31 +718,31 @@ impl Scene {
|
|||
active_model_tile: std::cell::Cell::new(0),
|
||||
selection: Rc::new(RefCell::new(SelectionState::default())),
|
||||
document: CadDocument::new(),
|
||||
selected: HashSet::new(),
|
||||
hidden: HashSet::new(),
|
||||
selected: HashSet::default(),
|
||||
hidden: HashSet::default(),
|
||||
hover_highlight: None,
|
||||
transparency_display: true,
|
||||
selection_filter: HashSet::new(),
|
||||
selection_filter: HashSet::default(),
|
||||
preview_wires: vec![],
|
||||
interim_wire: None,
|
||||
camera_generation: 0,
|
||||
geometry_epoch: GEOMETRY_EPOCH.fetch_add(1, Ordering::Relaxed),
|
||||
wire_cache: RefCell::new(None),
|
||||
model_tile_wire_cache: RefCell::new(HashMap::new()),
|
||||
model_tile_wire_cache: RefCell::new(HashMap::default()),
|
||||
sort_cache: RefCell::new(None),
|
||||
draw_depth_cache: RefCell::new(None),
|
||||
hatch_cache: RefCell::new(None),
|
||||
wipeout_cache: RefCell::new(None),
|
||||
image_cache: RefCell::new(None),
|
||||
mesh_cache: RefCell::new(None),
|
||||
viewport_wire_cache: RefCell::new(HashMap::new()),
|
||||
viewport_wire_cache: RefCell::new(HashMap::default()),
|
||||
paper_sheet_cache: RefCell::new(None),
|
||||
paper_projected_cache: RefCell::new(HashMap::new()),
|
||||
paper_projected_cache: RefCell::new(HashMap::default()),
|
||||
current_layout: "Model".to_string(),
|
||||
hatches: HashMap::new(),
|
||||
meshes: HashMap::new(),
|
||||
model_solids: HashMap::new(),
|
||||
images: HashMap::new(),
|
||||
hatches: HashMap::default(),
|
||||
meshes: HashMap::default(),
|
||||
model_solids: HashMap::default(),
|
||||
images: HashMap::default(),
|
||||
active_viewport: None,
|
||||
bg_color: [0.11, 0.11, 0.11, 1.0],
|
||||
paper_bg_color: [1.0, 1.0, 1.0, 1.0],
|
||||
|
|
@ -1455,7 +1455,7 @@ impl Scene {
|
|||
// Deduplicate by name: prefer the entry with a non-null block_record (the
|
||||
// real layout from the file) over the default placeholder created by
|
||||
// CadDocument::new().
|
||||
let mut by_name: std::collections::HashMap<String, (i16, Handle)> = Default::default();
|
||||
let mut by_name: rustc_hash::FxHashMap<String, (i16, Handle)> = Default::default();
|
||||
for obj in self.document.objects.values() {
|
||||
if let ObjectType::Layout(l) = obj {
|
||||
if l.name == "Model" || l.name.is_empty() {
|
||||
|
|
@ -1628,7 +1628,7 @@ impl Scene {
|
|||
}
|
||||
use acadrust::objects::ObjectType;
|
||||
// Per-block SortEntitiesTable overrides: block -> (entity_val -> sort_val).
|
||||
let mut overrides: HashMap<Handle, HashMap<u64, u64>> = HashMap::new();
|
||||
let mut overrides: HashMap<Handle, HashMap<u64, u64>> = HashMap::default();
|
||||
for obj in self.document.objects.values() {
|
||||
if let ObjectType::SortEntitiesTable(t) = obj {
|
||||
if !t.is_empty() {
|
||||
|
|
@ -1643,7 +1643,7 @@ impl Scene {
|
|||
}
|
||||
let ms = self.model_space_block_handle();
|
||||
// Group entities by owning block, carrying each entity's effective key.
|
||||
let mut by_block: HashMap<Handle, Vec<(u64, u64)>> = HashMap::new();
|
||||
let mut by_block: HashMap<Handle, Vec<(u64, u64)>> = HashMap::default();
|
||||
for e in self.document.entities() {
|
||||
let c = e.common();
|
||||
// 3D meshes keep real geometric depth — exclude them from
|
||||
|
|
@ -1667,7 +1667,7 @@ impl Scene {
|
|||
.unwrap_or(hv);
|
||||
by_block.entry(block).or_default().push((hv, eff));
|
||||
}
|
||||
let mut depth_map: HashMap<u64, f32> = HashMap::new();
|
||||
let mut depth_map: HashMap<u64, f32> = HashMap::default();
|
||||
for (_block, mut v) in by_block {
|
||||
v.sort_by_key(|(_, eff)| *eff);
|
||||
let denom = (v.len() as f32) + 1.0;
|
||||
|
|
@ -1928,7 +1928,7 @@ impl Scene {
|
|||
.unwrap_or(true);
|
||||
|
||||
if needs_rebuild {
|
||||
let mut idx: HashMap<Handle, HashMap<u64, u64>> = HashMap::new();
|
||||
let mut idx: HashMap<Handle, HashMap<u64, u64>> = HashMap::default();
|
||||
for obj in self.document.objects.values() {
|
||||
if let ObjectType::SortEntitiesTable(t) = obj {
|
||||
if !t.is_empty() {
|
||||
|
|
@ -2187,7 +2187,7 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
}
|
||||
let mut map: HashMap<Handle, Handle> = HashMap::new();
|
||||
let mut map: HashMap<Handle, Handle> = HashMap::default();
|
||||
for br in self.document.block_records.iter() {
|
||||
for &eh in &br.entity_handles {
|
||||
map.insert(eh, br.handle);
|
||||
|
|
@ -4098,11 +4098,11 @@ impl Scene {
|
|||
|
||||
pub fn clear(&mut self) {
|
||||
self.document = CadDocument::new();
|
||||
self.selected = HashSet::new();
|
||||
self.selected = HashSet::default();
|
||||
self.preview_wires = vec![];
|
||||
self.current_layout = "Model".to_string();
|
||||
self.hatches = HashMap::new();
|
||||
self.meshes = HashMap::new();
|
||||
self.hatches = HashMap::default();
|
||||
self.meshes = HashMap::default();
|
||||
*self.camera.borrow_mut() = Camera::default();
|
||||
self.camera_generation += 1;
|
||||
self.bump_geometry();
|
||||
|
|
@ -4201,7 +4201,7 @@ impl Scene {
|
|||
if self.selected.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
let pairs: std::collections::HashSet<(&'static str, String)> = self
|
||||
let pairs: rustc_hash::FxHashSet<(&'static str, String)> = self
|
||||
.selected
|
||||
.iter()
|
||||
.filter_map(|h| self.document.get_entity(*h))
|
||||
|
|
@ -6276,7 +6276,7 @@ impl Scene {
|
|||
vp_handle: Handle,
|
||||
screen_height_px: f32,
|
||||
) -> Vec<WireModel> {
|
||||
use std::collections::HashSet as HSet;
|
||||
use rustc_hash::FxHashSet as HSet;
|
||||
|
||||
let (frozen, vp_anno_scale, vp_aspect) = match self.document.get_entity(vp_handle) {
|
||||
Some(EntityType::Viewport(vp)) => {
|
||||
|
|
@ -6295,7 +6295,7 @@ impl Scene {
|
|||
};
|
||||
(f, anno, aspect)
|
||||
}
|
||||
_ => (HSet::new(), 1.0_f32, 1.0_f32),
|
||||
_ => (HSet::default(), 1.0_f32, 1.0_f32),
|
||||
};
|
||||
|
||||
// Drive the per-viewport view_aabb / wpp from the *effective* camera
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ impl Face3DGpu {
|
|||
face3d_wires: &[WireModel],
|
||||
all_wires: &[WireModel],
|
||||
keep_3d_mesh_fills: bool,
|
||||
depth_map: &std::collections::HashMap<u64, f32>,
|
||||
depth_map: &rustc_hash::FxHashMap<u64, f32>,
|
||||
) -> Self {
|
||||
let depth_of = |w: &WireModel| -> f32 {
|
||||
w.name
|
||||
|
|
|
|||
|
|
@ -891,7 +891,7 @@ impl Pipeline {
|
|||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
wires: &[WireModel],
|
||||
depth_map: &std::collections::HashMap<u64, f32>,
|
||||
depth_map: &rustc_hash::FxHashMap<u64, f32>,
|
||||
) {
|
||||
let depth_of = |w: &WireModel| -> f32 {
|
||||
w.name
|
||||
|
|
@ -987,7 +987,7 @@ impl Pipeline {
|
|||
face3d_wires: &[WireModel],
|
||||
all_wires: &[WireModel],
|
||||
wireframe_only: bool,
|
||||
depth_map: &std::collections::HashMap<u64, f32>,
|
||||
depth_map: &rustc_hash::FxHashMap<u64, f32>,
|
||||
) {
|
||||
// Edge buffer is always built from `face3d_wires`, so 3DFACE
|
||||
// outlines stay on the screen regardless of mode.
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ fn emit_wire_instances(wire: &WireModel, color: [f32; 4], draw_depth: f32) -> Ve
|
|||
/// Looks up a wire's draw-order depth from the per-entity map using the
|
||||
/// handle encoded in its `name`. Falls back to 0.0 (transient / preview
|
||||
/// wires that carry no document handle).
|
||||
fn wire_draw_depth(wire: &WireModel, depth_map: &std::collections::HashMap<u64, f32>) -> f32 {
|
||||
fn wire_draw_depth(wire: &WireModel, depth_map: &rustc_hash::FxHashMap<u64, f32>) -> f32 {
|
||||
wire
|
||||
.name
|
||||
.parse::<u64>()
|
||||
|
|
@ -290,7 +290,7 @@ impl WireGpu {
|
|||
pub fn from_batch(
|
||||
device: &wgpu::Device,
|
||||
wires: &[WireModel],
|
||||
depth_map: &std::collections::HashMap<u64, f32>,
|
||||
depth_map: &rustc_hash::FxHashMap<u64, f32>,
|
||||
) -> Vec<Self> {
|
||||
let total_segs: usize = wires.iter().map(|w| w.points.len().saturating_sub(1)).sum();
|
||||
if total_segs == 0 {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ pub struct QuadTree {
|
|||
nodes: Vec<Node>,
|
||||
/// `handle → (node_idx, item_idx_within_node)` so removal/update
|
||||
/// is O(1) without walking the tree.
|
||||
locator: std::collections::HashMap<Handle, (u32, u32)>,
|
||||
locator: rustc_hash::FxHashMap<Handle, (u32, u32)>,
|
||||
/// Items whose AABB falls outside the root bounds. Surfaced on
|
||||
/// every query — small set in practice (typically empty for
|
||||
/// well-bounded drawings).
|
||||
|
|
@ -74,7 +74,7 @@ impl QuadTree {
|
|||
};
|
||||
Self {
|
||||
nodes: vec![root],
|
||||
locator: std::collections::HashMap::new(),
|
||||
locator: rustc_hash::FxHashMap::default(),
|
||||
overflow: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ pub struct ViewportData {
|
|||
/// by the wire / face3d pipelines as a clip-z bias. WireModels carry no
|
||||
/// depth field (84 construction sites); the bias is looked up by handle
|
||||
/// at GPU-upload time from this map instead.
|
||||
pub(super) draw_depths: Arc<std::collections::HashMap<u64, f32>>,
|
||||
pub(super) draw_depths: Arc<rustc_hash::FxHashMap<u64, f32>>,
|
||||
pub(super) hatches: Arc<Vec<HatchModel>>,
|
||||
/// Wipeout fills — rendered in a separate pass AFTER wires.
|
||||
pub(super) wipeout_hatches: Arc<Vec<HatchModel>>,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
// All other surface types are silently skipped; partial results are still
|
||||
// returned so the solid renders with at least its planar faces.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use rustc_hash::FxHashSet as HashSet;
|
||||
use std::f64::consts::TAU;
|
||||
|
||||
use acadrust::entities::acis::types::Sense;
|
||||
|
|
@ -237,7 +237,7 @@ fn collect_face_polygon(sat: &SatDocument, face: &SatFace) -> Vec<[f64; 3]> {
|
|||
let first_ptr = sat_loop.first_coedge();
|
||||
let mut cur = first_ptr;
|
||||
let mut pts: Vec<[f64; 3]> = Vec::new();
|
||||
let mut visited: HashSet<i32> = HashSet::new();
|
||||
let mut visited: HashSet<i32> = HashSet::default();
|
||||
|
||||
loop {
|
||||
if cur.is_null() {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ pub struct SnapResult {
|
|||
|
||||
// ── Snapper ───────────────────────────────────────────────────────────────
|
||||
|
||||
use std::collections::HashSet;
|
||||
use rustc_hash::FxHashSet as HashSet;
|
||||
|
||||
pub struct Snapper {
|
||||
/// Global snap on/off toggle. When false, all snapping is bypassed
|
||||
|
|
@ -87,7 +87,7 @@ pub struct Snapper {
|
|||
|
||||
impl Default for Snapper {
|
||||
fn default() -> Self {
|
||||
let mut enabled = HashSet::new();
|
||||
let mut enabled = HashSet::default();
|
||||
enabled.insert(SnapType::Endpoint);
|
||||
enabled.insert(SnapType::Midpoint);
|
||||
enabled.insert(SnapType::Center);
|
||||
|
|
@ -257,7 +257,7 @@ impl Snapper {
|
|||
let tmp = Snapper {
|
||||
snap_enabled: true,
|
||||
enabled: {
|
||||
let mut s = HashSet::new();
|
||||
let mut s = HashSet::default();
|
||||
s.insert(SnapType::Tangent);
|
||||
s
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
//! • Linetype → read-only for now
|
||||
//! • Geometry → text_input per coordinate / dimension field
|
||||
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
use std::fmt;
|
||||
|
||||
use crate::ui::ROW_H;
|
||||
|
|
@ -142,12 +142,12 @@ impl Default for PropertiesPanel {
|
|||
selected_group: None,
|
||||
linetype_items: vec![],
|
||||
selection_group_combo: combo_box::State::new(vec![]),
|
||||
choice_combos: HashMap::new(),
|
||||
choice_combos: HashMap::default(),
|
||||
layer_combo: combo_box::State::new(vec![]),
|
||||
lineweight_combo: combo_box::State::new(lw_options()),
|
||||
linetype_combo: combo_box::State::new(vec![]),
|
||||
hatch_pattern_combo: combo_box::State::new(crate::scene::hatch_patterns::names()),
|
||||
edit_buf: HashMap::new(),
|
||||
edit_buf: HashMap::default(),
|
||||
color_picker_open: false,
|
||||
color_palette_open: false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
// Dropdown items within a group are collected into columns of 3 rows.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
|
||||
use acadrust::types::{Color as AcadColor, LineWeight};
|
||||
use iced::widget::{button, column, container, mouse_area, row, scrollable, svg, text};
|
||||
|
|
@ -73,7 +73,7 @@ impl Ribbon {
|
|||
wireframe: false,
|
||||
ortho_mode: true,
|
||||
open_dropdown: None,
|
||||
last_cmd: HashMap::new(),
|
||||
last_cmd: HashMap::default(),
|
||||
layer_names: vec!["0".to_string()],
|
||||
active_layer: "0".to_string(),
|
||||
layer_infos: vec![LayerInfo {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Shared rendering helpers, button styles, colours, layout constants, and
|
||||
// free functions used by the Ribbon view/overlay methods.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use acadrust::types::{Color as AcadColor, LineWeight};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//! A checked row means that type can be picked; unchecking it excludes the
|
||||
//! type from interactive selection. Opened from the FILTER status pill.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use rustc_hash::FxHashSet as HashSet;
|
||||
|
||||
use iced::widget::{button, column, container, mouse_area, row, text};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Padding, Theme};
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ fn section<'a>(title: &'static str) -> Element<'a, Message> {
|
|||
}
|
||||
|
||||
pub fn view_window<'a>(
|
||||
overrides: &'a std::collections::HashMap<String, String>,
|
||||
overrides: &'a rustc_hash::FxHashMap<String, String>,
|
||||
) -> Element<'a, Message> {
|
||||
// ── Toolbar ───────────────────────────────────────────────────────────
|
||||
let toolbar = container(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
//! adds or removes that pill from the bar. The choice is persisted so it
|
||||
//! survives across sessions.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use rustc_hash::FxHashSet as HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Identifies a toggleable status-bar pill.
|
||||
|
|
@ -121,7 +121,7 @@ pub struct StatusBarConfig {
|
|||
impl StatusBarConfig {
|
||||
/// Load the saved customization, or all-visible when none exists.
|
||||
pub fn load() -> Self {
|
||||
let mut hidden = HashSet::new();
|
||||
let mut hidden = HashSet::default();
|
||||
if let Some(path) = config_path() {
|
||||
if let Ok(body) = std::fs::read_to_string(path) {
|
||||
for line in body.lines() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue