forked from 1b-agent/opencadstudio-upstream
feat(cursor): add crosshair options
Add persistent cursor sizing, pick aperture, color, pointer mode, isometric drafting planes, and SNAPANG-aligned grid behavior.\n\nRefs #413
This commit is contained in:
1 parent
188ac80b2e
commit
1738f9a79b
25 files changed
+989
-126
No files matched your search
+18
-19
@@ -15,13 +15,10 @@ use crate::scene::model::mesh_model::MeshModel;
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::scene::pick::interaction_index::WireSource;
|
||||
|
||||
/// Pixel radius used for single-click wire detection.
|
||||
pub const CLICK_THRESHOLD_PX: f32 = 8.0;
|
||||
|
||||
/// Pick radius for one wire, in screen pixels.
|
||||
///
|
||||
/// A wire renders as a band `line_weight_px` wide, so testing every wire at the
|
||||
/// bare [`CLICK_THRESHOLD_PX`] would leave the outer part of a heavy line
|
||||
/// configured base radius would leave the outer part of a heavy line
|
||||
/// unselectable — the cursor would sit on solid ink and miss. Widening to the
|
||||
/// rendered half-width keeps "looks like I'm on it" and "picks it" the same
|
||||
/// thing at any zoom: both quantities are screen-space, so the relation holds
|
||||
@@ -35,13 +32,13 @@ pub const CLICK_THRESHOLD_PX: f32 = 8.0;
|
||||
/// renders 7.97 px half-width), so this only bites for out-of-range weights —
|
||||
/// and it keeps the two sides from silently drifting apart if the display boost
|
||||
/// in `view::render::lineweight_to_px` ever changes.
|
||||
pub fn pick_tolerance_px(wire: &WireModel, lw_display: bool) -> f32 {
|
||||
pub fn pick_tolerance_px(wire: &WireModel, lw_display: bool, base_radius_px: f32) -> f32 {
|
||||
let half_width = if lw_display {
|
||||
wire.line_weight_px * 0.5
|
||||
} else {
|
||||
0.5
|
||||
};
|
||||
CLICK_THRESHOLD_PX.max(half_width)
|
||||
base_radius_px.max(1.0).max(half_width)
|
||||
}
|
||||
|
||||
/// Is `aabb` — a wire's world-space XY box — further than `tol` pixels from
|
||||
@@ -199,6 +196,7 @@ pub fn click_hit<'a, W: WireSource + ?Sized>(
|
||||
eye: glam::DVec3,
|
||||
bounds: Rectangle,
|
||||
lw_display: bool,
|
||||
base_radius_px: f32,
|
||||
) -> Option<&'a str> {
|
||||
// A click outside the pane rectangle (e.g. on the paper around a floating
|
||||
// viewport) must not reach geometry scissored out of the viewport.
|
||||
@@ -243,7 +241,7 @@ pub fn click_hit<'a, W: WireSource + ?Sized>(
|
||||
bounds,
|
||||
);
|
||||
let d = dist_point_to_segment(cursor, p0, p1);
|
||||
if d < pick_tolerance_px(wire, lw_display) && d < best_dist {
|
||||
if d < pick_tolerance_px(wire, lw_display, base_radius_px) && d < best_dist {
|
||||
best_dist = d;
|
||||
best = Some(&wire.name);
|
||||
}
|
||||
@@ -251,7 +249,7 @@ pub fn click_hit<'a, W: WireSource + ?Sized>(
|
||||
} else {
|
||||
// Q: lazy projection — no Vec allocation per wire; NaN resets the segment chain.
|
||||
for wire in wires.iter() {
|
||||
let tol = pick_tolerance_px(wire, lw_display);
|
||||
let tol = pick_tolerance_px(wire, lw_display, base_radius_px);
|
||||
// Cheap AABB pre-reject (flat view only; never for the unbounded
|
||||
// sentinel used by previews / greeked text).
|
||||
if z_flat
|
||||
@@ -446,6 +444,7 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||
eye: glam::DVec3,
|
||||
bounds: Rectangle,
|
||||
lw_display: bool,
|
||||
base_radius_px: f32,
|
||||
) -> Vec<&'a str> {
|
||||
if cursor.x < 0.0 || cursor.x > bounds.width || cursor.y < 0.0 || cursor.y > bounds.height {
|
||||
return Vec::new();
|
||||
@@ -474,7 +473,7 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||
bounds,
|
||||
);
|
||||
let d = dist_point_to_segment(cursor, p0, p1);
|
||||
if d < pick_tolerance_px(wire, lw_display) {
|
||||
if d < pick_tolerance_px(wire, lw_display, base_radius_px) {
|
||||
best_by_wire
|
||||
.entry(segment.wire)
|
||||
.and_modify(|best| *best = best.min(d))
|
||||
@@ -488,7 +487,7 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||
}));
|
||||
} else {
|
||||
for wire in wires.iter() {
|
||||
let tol = pick_tolerance_px(wire, lw_display);
|
||||
let tol = pick_tolerance_px(wire, lw_display, base_radius_px);
|
||||
let mut prev: Option<Point> = None;
|
||||
let mut best_for_wire = tol;
|
||||
let mut hit = false;
|
||||
@@ -543,7 +542,7 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
hits.push((CLICK_THRESHOLD_PX, wire.name.as_str()));
|
||||
hits.push((base_radius_px.max(1.0), wire.name.as_str()));
|
||||
matched.insert(triangle.wire);
|
||||
}
|
||||
}
|
||||
@@ -562,7 +561,7 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
hits.push((CLICK_THRESHOLD_PX, wire.name.as_str()));
|
||||
hits.push((base_radius_px.max(1.0), wire.name.as_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -597,7 +596,7 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
hits.push((CLICK_THRESHOLD_PX, wire.name.as_str()));
|
||||
hits.push((base_radius_px.max(1.0), wire.name.as_str()));
|
||||
matched.insert(triangle.wire);
|
||||
}
|
||||
}
|
||||
@@ -616,7 +615,7 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
hits.push((CLICK_THRESHOLD_PX, wire.name.as_str()));
|
||||
hits.push((base_radius_px.max(1.0), wire.name.as_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -641,7 +640,7 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||
continue;
|
||||
};
|
||||
if text_quad_hit_area(cursor, vertices, view_rot, eye, bounds).is_some() {
|
||||
hits.push((CLICK_THRESHOLD_PX, wire.name.as_str()));
|
||||
hits.push((base_radius_px.max(1.0), wire.name.as_str()));
|
||||
matched.insert(glyph.wire);
|
||||
}
|
||||
}
|
||||
@@ -651,7 +650,7 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||
continue;
|
||||
}
|
||||
if text_quad_hit_area(cursor, &wire.text_verts, view_rot, eye, bounds).is_some() {
|
||||
hits.push((CLICK_THRESHOLD_PX, wire.name.as_str()));
|
||||
hits.push((base_radius_px.max(1.0), wire.name.as_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2038,16 +2037,16 @@ mod aabb_reject_tests {
|
||||
|
||||
let eye = glam::DVec3::ZERO;
|
||||
assert_eq!(
|
||||
click_hit(cursor, std::slice::from_ref(&near), vp, eye, bounds, true),
|
||||
click_hit(cursor, std::slice::from_ref(&near), vp, eye, bounds, true, 8.0),
|
||||
Some("5")
|
||||
);
|
||||
assert_eq!(
|
||||
click_hit(cursor, std::slice::from_ref(&far), vp, eye, bounds, true),
|
||||
click_hit(cursor, std::slice::from_ref(&far), vp, eye, bounds, true, 8.0),
|
||||
None
|
||||
);
|
||||
// The far wire must be rejected without hiding the near one.
|
||||
assert_eq!(
|
||||
click_hit(cursor, &[far, near], vp, eye, bounds, true),
|
||||
click_hit(cursor, &[far, near], vp, eye, bounds, true, 8.0),
|
||||
Some("5")
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user