diff --git a/src/app/update.rs b/src/app/update.rs index 219324b8e..83cc67cb8 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -1261,6 +1261,7 @@ impl H7CAD { snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], + aabb: crate::scene::WireModel::UNBOUNDED_AABB, }; previews.push(guide); } diff --git a/src/modules/annotate/aligned_dim.rs b/src/modules/annotate/aligned_dim.rs index df2a066c8..b569abae0 100644 --- a/src/modules/annotate/aligned_dim.rs +++ b/src/modules/annotate/aligned_dim.rs @@ -101,6 +101,7 @@ impl CadCommand for AlignedDimensionCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } @@ -134,5 +135,6 @@ fn preview_aligned(p1: Vec3, p2: Vec3, dim_pt: Vec3) -> WireModel { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, } } diff --git a/src/modules/annotate/angular_dim.rs b/src/modules/annotate/angular_dim.rs index b3b2c2dc9..bc3ad7932 100644 --- a/src/modules/annotate/angular_dim.rs +++ b/src/modules/annotate/angular_dim.rs @@ -124,6 +124,7 @@ fn preview_wire(points: Vec) -> WireModel { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, } } diff --git a/src/modules/annotate/diameter_dim.rs b/src/modules/annotate/diameter_dim.rs index c365b2106..93f530f67 100644 --- a/src/modules/annotate/diameter_dim.rs +++ b/src/modules/annotate/diameter_dim.rs @@ -100,5 +100,6 @@ fn preview_line(a: Vec3, b: Vec3) -> WireModel { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, } } diff --git a/src/modules/annotate/dim_baseline.rs b/src/modules/annotate/dim_baseline.rs index 488b8dd39..3f7ab1712 100644 --- a/src/modules/annotate/dim_baseline.rs +++ b/src/modules/annotate/dim_baseline.rs @@ -142,6 +142,7 @@ impl CadCommand for DimBaselineCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/annotate/dim_continue.rs b/src/modules/annotate/dim_continue.rs index 391d502a6..eb560a604 100644 --- a/src/modules/annotate/dim_continue.rs +++ b/src/modules/annotate/dim_continue.rs @@ -139,6 +139,7 @@ impl CadCommand for DimContinueCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/annotate/dimjogline.rs b/src/modules/annotate/dimjogline.rs index 6da11440c..89b265d82 100644 --- a/src/modules/annotate/dimjogline.rs +++ b/src/modules/annotate/dimjogline.rs @@ -93,6 +93,7 @@ impl CadCommand for DimJogLineCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/annotate/dimtedit.rs b/src/modules/annotate/dimtedit.rs index 3bda91a5c..6a2efc36d 100644 --- a/src/modules/annotate/dimtedit.rs +++ b/src/modules/annotate/dimtedit.rs @@ -101,6 +101,7 @@ impl CadCommand for DimTeditCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } diff --git a/src/modules/annotate/leader_cmd.rs b/src/modules/annotate/leader_cmd.rs index 0e945f928..3e799c435 100644 --- a/src/modules/annotate/leader_cmd.rs +++ b/src/modules/annotate/leader_cmd.rs @@ -201,6 +201,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, } } diff --git a/src/modules/annotate/linear_dim.rs b/src/modules/annotate/linear_dim.rs index 3800aba36..188150711 100644 --- a/src/modules/annotate/linear_dim.rs +++ b/src/modules/annotate/linear_dim.rs @@ -112,6 +112,7 @@ fn preview_wire(points: Vec) -> WireModel { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, } } diff --git a/src/modules/annotate/mleader_cmd.rs b/src/modules/annotate/mleader_cmd.rs index c9141dd42..9ae9c1822 100644 --- a/src/modules/annotate/mleader_cmd.rs +++ b/src/modules/annotate/mleader_cmd.rs @@ -154,6 +154,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, } } diff --git a/src/modules/annotate/mleader_edit.rs b/src/modules/annotate/mleader_edit.rs index f9fd084af..8302a4fd7 100644 --- a/src/modules/annotate/mleader_edit.rs +++ b/src/modules/annotate/mleader_edit.rs @@ -365,6 +365,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, } } diff --git a/src/modules/annotate/qdim.rs b/src/modules/annotate/qdim.rs index 4e0e66f3e..7e11652fd 100644 --- a/src/modules/annotate/qdim.rs +++ b/src/modules/annotate/qdim.rs @@ -98,6 +98,7 @@ impl CadCommand for QdimCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/annotate/radius_dim.rs b/src/modules/annotate/radius_dim.rs index 109827530..0f0970696 100644 --- a/src/modules/annotate/radius_dim.rs +++ b/src/modules/annotate/radius_dim.rs @@ -111,5 +111,6 @@ fn preview_wire(points: Vec) -> WireModel { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, } } diff --git a/src/modules/annotate/table_cmd.rs b/src/modules/annotate/table_cmd.rs index ecb52fec3..e57c9c8f9 100644 --- a/src/modules/annotate/table_cmd.rs +++ b/src/modules/annotate/table_cmd.rs @@ -144,6 +144,7 @@ impl CadCommand for TableCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } else { None diff --git a/src/modules/annotate/tolerance_cmd.rs b/src/modules/annotate/tolerance_cmd.rs index 8e02e8a63..c33e3541b 100644 --- a/src/modules/annotate/tolerance_cmd.rs +++ b/src/modules/annotate/tolerance_cmd.rs @@ -97,6 +97,7 @@ impl CadCommand for ToleranceCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/home/draw/attdef.rs b/src/modules/home/draw/attdef.rs index 83aba5cd3..bf3c254fa 100644 --- a/src/modules/home/draw/attdef.rs +++ b/src/modules/home/draw/attdef.rs @@ -133,6 +133,7 @@ impl CadCommand for AttdefCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/home/draw/ellipse.rs b/src/modules/home/draw/ellipse.rs index b87da83ad..bd45561bd 100644 --- a/src/modules/home/draw/ellipse.rs +++ b/src/modules/home/draw/ellipse.rs @@ -526,5 +526,6 @@ fn line_wire(from: Vec3, to: Vec3) -> WireModel { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, } } diff --git a/src/modules/home/draw/line.rs b/src/modules/home/draw/line.rs index 1262a8443..389c2f65b 100644 --- a/src/modules/home/draw/line.rs +++ b/src/modules/home/draw/line.rs @@ -87,6 +87,7 @@ impl CadCommand for LineCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/home/draw/mline.rs b/src/modules/home/draw/mline.rs index 37c9d5fa9..31557db85 100644 --- a/src/modules/home/draw/mline.rs +++ b/src/modules/home/draw/mline.rs @@ -112,6 +112,7 @@ impl CadCommand for MlineCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/home/draw/raster_image.rs b/src/modules/home/draw/raster_image.rs index e207ca711..6d8eac633 100644 --- a/src/modules/home/draw/raster_image.rs +++ b/src/modules/home/draw/raster_image.rs @@ -111,6 +111,7 @@ impl CadCommand for ImageCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/home/draw/ray.rs b/src/modules/home/draw/ray.rs index ee1e77955..7af64eceb 100644 --- a/src/modules/home/draw/ray.rs +++ b/src/modules/home/draw/ray.rs @@ -85,6 +85,7 @@ impl CadCommand for RayCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } @@ -162,6 +163,7 @@ impl CadCommand for XLineCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/home/draw/revcloud.rs b/src/modules/home/draw/revcloud.rs index f4b47a1d7..ec05c373d 100644 --- a/src/modules/home/draw/revcloud.rs +++ b/src/modules/home/draw/revcloud.rs @@ -82,6 +82,7 @@ impl CadCommand for RevCloudCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/home/draw/wipeout.rs b/src/modules/home/draw/wipeout.rs index d6afce669..27344f119 100644 --- a/src/modules/home/draw/wipeout.rs +++ b/src/modules/home/draw/wipeout.rs @@ -118,6 +118,7 @@ impl CadCommand for WipeoutCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } WipeoutMode::Polygonal => { @@ -138,6 +139,7 @@ impl CadCommand for WipeoutCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/home/inquiry/area.rs b/src/modules/home/inquiry/area.rs index 2a79824f3..00dd7d0e4 100644 --- a/src/modules/home/inquiry/area.rs +++ b/src/modules/home/inquiry/area.rs @@ -75,6 +75,7 @@ impl CadCommand for AreaCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/home/inquiry/dist.rs b/src/modules/home/inquiry/dist.rs index 52a30b03c..ec2a23805 100644 --- a/src/modules/home/inquiry/dist.rs +++ b/src/modules/home/inquiry/dist.rs @@ -70,6 +70,7 @@ impl CadCommand for DistCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/home/modify/stretch.rs b/src/modules/home/modify/stretch.rs index 25308c8e5..3df965f6a 100644 --- a/src/modules/home/modify/stretch.rs +++ b/src/modules/home/modify/stretch.rs @@ -133,6 +133,7 @@ impl CadCommand for StretchCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } _ => None, diff --git a/src/modules/layout/mview.rs b/src/modules/layout/mview.rs index 24bb81a43..550d46d52 100644 --- a/src/modules/layout/mview.rs +++ b/src/modules/layout/mview.rs @@ -97,6 +97,7 @@ impl CadCommand for MviewCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/view/plot_window.rs b/src/modules/view/plot_window.rs index a3cfdc8f1..3627facb0 100644 --- a/src/modules/view/plot_window.rs +++ b/src/modules/view/plot_window.rs @@ -62,6 +62,7 @@ impl CadCommand for PlotWindowCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/modules/view/zoom_window.rs b/src/modules/view/zoom_window.rs index 8e3b1ad79..a8cfa5377 100644 --- a/src/modules/view/zoom_window.rs +++ b/src/modules/view/zoom_window.rs @@ -68,6 +68,7 @@ impl CadCommand for ZoomWindowCommand { tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) } } diff --git a/src/scene/complex_lt.rs b/src/scene/complex_lt.rs index 29689b307..e3ce5975f 100644 --- a/src/scene/complex_lt.rs +++ b/src/scene/complex_lt.rs @@ -161,6 +161,7 @@ pub fn apply_along( tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }) .collect() } diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 8a04f704a..e39e051c7 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -2552,9 +2552,11 @@ fn tessellate_entity( } else { (0.0_f32, [0.0f32; 8]) }; - return vec![tessellate::tessellate( + let mut wire = tessellate::tessellate( document, h, e, sel, color, pattern_length, pattern, 1.5, - )]; + ); + wire.aabb = entity_aabb(e); + return vec![wire]; } let (entity_color, pattern_length, pattern, line_weight_px, aci) = @@ -2563,11 +2565,13 @@ fn tessellate_entity( let lt_name = render::linetype_name_for(document, e); if let EntityType::Dimension(dim) = e { + let aabb = entity_aabb(e); let mut wires = tessellate::tessellate_dimension( document, h, dim, sel, entity_color, line_weight_px, ); for w in &mut wires { w.aci = aci; + w.aabb = aabb; } return wires; } @@ -2583,6 +2587,7 @@ fn tessellate_entity( .flat_map(|sub| { let (sub_color, sub_pattern_length, sub_pattern, sub_line_weight_px, sub_aci) = render::render_style_for(document, &sub); + let sub_aabb = entity_aabb(&sub); let mut wire = tessellate::tessellate( document, h, @@ -2595,18 +2600,21 @@ fn tessellate_entity( ); wire.name = h.value().to_string(); wire.aci = sub_aci; + wire.aabb = sub_aabb; vec![wire] }) .collect(); } + let aabb = entity_aabb(e); let mut base = tessellate::tessellate( document, h, e, sel, entity_color, pattern_length, pattern, line_weight_px, ); base.aci = aci; + base.aabb = aabb; if let Some(clt) = crate::linetypes::complex_lt(lt_name) { - let wires = complex_lt::apply_along( + let mut wires = complex_lt::apply_along( &base.name, &base.points, clt, @@ -2616,6 +2624,7 @@ fn tessellate_entity( base.line_weight_px, ); if !wires.is_empty() { + for w in &mut wires { w.aabb = aabb; } return wires; } } @@ -2623,3 +2632,17 @@ fn tessellate_entity( vec![base] } +fn entity_aabb(e: &acadrust::EntityType) -> [f32; 4] { + let bbox = e.as_entity().bounding_box(); + let min_x = bbox.min.x as f32; + let min_y = bbox.min.y as f32; + let max_x = bbox.max.x as f32; + let max_y = bbox.max.y as f32; + // A degenerate box (min == max == 0) means bounding_box() returned Default — + // use UNBOUNDED so the wire is never wrongly pre-rejected. + if min_x == 0.0 && min_y == 0.0 && max_x == 0.0 && max_y == 0.0 { + return WireModel::UNBOUNDED_AABB; + } + [min_x, min_y, max_x, max_y] +} + diff --git a/src/scene/tessellate.rs b/src/scene/tessellate.rs index 5ebc932a5..885f6c96b 100644 --- a/src/scene/tessellate.rs +++ b/src/scene/tessellate.rs @@ -96,6 +96,7 @@ pub fn tessellate( tangent_geoms: te.tangent_geoms, aci: 0, key_vertices: te.key_vertices, + aabb: WireModel::UNBOUNDED_AABB, }; } @@ -122,6 +123,7 @@ pub fn tessellate( tangent_geoms: te.tangent_geoms, aci: 0, key_vertices: te.key_vertices, + aabb: WireModel::UNBOUNDED_AABB, }; } _ => {} @@ -142,6 +144,7 @@ pub fn tessellate( tangent_geoms: te.tangent_geoms, aci: 0, key_vertices: te.key_vertices, + aabb: WireModel::UNBOUNDED_AABB, }; } } @@ -160,6 +163,7 @@ pub fn tessellate( tangent_geoms: te.tangent_geoms, aci: 0, key_vertices: te.key_vertices, + aabb: WireModel::UNBOUNDED_AABB, }; } } @@ -177,6 +181,7 @@ pub fn tessellate( tangent_geoms: te.tangent_geoms, aci: 0, key_vertices: te.key_vertices, + aabb: WireModel::UNBOUNDED_AABB, }; } @@ -205,6 +210,7 @@ pub fn tessellate( snap_pts, tangent_geoms, key_vertices, + aabb: WireModel::UNBOUNDED_AABB, } } @@ -241,6 +247,7 @@ pub fn tessellate_dimension( snap_pts: vec![], tangent_geoms: vec![], key_vertices, + aabb: WireModel::UNBOUNDED_AABB, }]; if let Some(text) = dimension_text_entity(dim) { @@ -287,6 +294,7 @@ fn tessellate_leader( tangent_geoms: vec![], aci: 0, key_vertices: vec![], + aabb: WireModel::UNBOUNDED_AABB, }]; } @@ -354,6 +362,7 @@ fn tessellate_leader( snap_pts: vec![], tangent_geoms: vec![], key_vertices, + aabb: WireModel::UNBOUNDED_AABB, }] } @@ -460,6 +469,7 @@ fn tessellate_multileader( tangent_geoms: vec![], aci: 0, key_vertices: key_verts, + aabb: WireModel::UNBOUNDED_AABB, }]; // Render text content as MText wire diff --git a/src/scene/wire_model.rs b/src/scene/wire_model.rs index c36cc4b95..eb354f279 100644 --- a/src/scene/wire_model.rs +++ b/src/scene/wire_model.rs @@ -55,12 +55,20 @@ pub struct WireModel { /// Non-empty only for entities with distinct vertex positions (Line, LwPolyline). /// Empty for tessellated curves (Circle, Arc, Ellipse) which use snap_pts instead. pub key_vertices: Vec<[f32; 3]>, + /// World-space 2-D bounding box [min_x, min_y, max_x, max_y]. + /// Set from acadrust `bounding_box()` in `tessellate_entity()`. + /// Preview / interim wires use `UNBOUNDED_AABB` so they are never pre-rejected + /// by the snap world-space filter. + pub aabb: [f32; 4], } impl WireModel { pub const WHITE: [f32; 4] = [1.00, 1.00, 1.00, 1.0]; pub const CYAN: [f32; 4] = [0.25, 0.85, 1.00, 1.0]; pub const SELECTED: [f32; 4] = [0.15, 0.55, 1.00, 1.0]; + /// Sentinel AABB that never rejects any snap query. + pub const UNBOUNDED_AABB: [f32; 4] = + [f32::NEG_INFINITY, f32::NEG_INFINITY, f32::INFINITY, f32::INFINITY]; /// Create a solid wire (no dash pattern, 1px weight). pub fn solid(name: String, points: Vec<[f32; 3]>, color: [f32; 4], selected: bool) -> Self { @@ -76,6 +84,7 @@ impl WireModel { snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], + aabb: Self::UNBOUNDED_AABB, } } @@ -159,3 +168,22 @@ impl WireModel { .sum() } } + +impl Default for WireModel { + fn default() -> Self { + Self { + name: String::new(), + points: Vec::new(), + color: Self::WHITE, + selected: false, + pattern_length: 0.0, + pattern: [0.0; 8], + line_weight_px: 1.0, + aci: 0, + snap_pts: Vec::new(), + tangent_geoms: Vec::new(), + key_vertices: Vec::new(), + aabb: Self::UNBOUNDED_AABB, + } + } +} diff --git a/src/snap/mod.rs b/src/snap/mod.rs index d4b5d6beb..c8f6e9012 100644 --- a/src/snap/mod.rs +++ b/src/snap/mod.rs @@ -291,29 +291,16 @@ impl Snapper { if s > 1e-6 { self.snap_radius_px / s } else { f32::MAX } }; - // Returns false when the wire's chord sphere is definitely outside the - // snap circle — safe to skip all vertex work for this wire. - // Uses first/last point of key_vertices (if any) or tessellated points. - // Closed wires (first ≈ last) are always passed through because the - // chord radius would be ~0 and their arc can still pass near the cursor. + // Returns false when the wire's AABB does not overlap the snap circle — + // safe to skip all vertex work for this wire. + // UNBOUNDED_AABB (±infinity) passes through automatically without a + // special-case branch because the arithmetic is exact for infinities. let wire_in_range = |wire: &WireModel| -> bool { - let pts: &[[f32; 3]] = if !wire.key_vertices.is_empty() { - &wire.key_vertices - } else { - &wire.points - }; - let (Some(&f), Some(&l)) = (pts.first(), pts.last()) else { - return false; - }; - let dx = l[0] - f[0]; - let dy = l[1] - f[1]; - let half_chord = (dx * dx + dy * dy).sqrt() * 0.5; - if half_chord < 1e-4 { - return true; // closed or degenerate — can't prune - } - let cx = (f[0] + l[0]) * 0.5 - cursor_world.x; - let cy = (f[1] + l[1]) * 0.5 - cursor_world.y; - cx * cx + cy * cy <= (world_snap_r + half_chord) * (world_snap_r + half_chord) + let r = world_snap_r; + cursor_world.x + r >= wire.aabb[0] + && cursor_world.x - r <= wire.aabb[2] + && cursor_world.y + r >= wire.aabb[1] + && cursor_world.y - r <= wire.aabb[3] }; let mut try_pt = |world: Vec3, snap_type: SnapType| {