///|
fn discrete_kind(kind : HitKind) -> Bool {
kind == Step ||
kind == Stairs ||
kind == Ecdf ||
kind == Stem ||
kind == Quiver ||
kind == Barbs ||
kind == Contour ||
kind == Streamplot
}
///|
// Real source Points are registered only on the artist's actual stroke.
// Fractional meet-viewport inversion may move an exact endpoint a few binary64
// rounding steps into its adjacent riser. This bound is numerical roundoff in
// SVG coordinates, independent of the caller's CSS hover radius.
fn discrete_source_roundoff(target : HitTarget, x : Double, y : Double) -> Bool {
if !discrete_kind(target.info.kind) || target.shape != Point {
return false
}
let point = target.info.point
let factor = 4.0 * 2.220446049250313e-16
let tx = factor *
x.abs().max(point.screen_x.abs()).max(target.bounds.2.abs()).max(1.0)
let ty = factor *
y.abs().max(point.screen_y.abs()).max(target.bounds.3.abs()).max(1.0)
(point.screen_x - x).abs() <= tx && (point.screen_y - y).abs() <= ty
}
///|
// Zero path edges can discover an earlier slot after a later one. Compare
// coincident canonical Points by source order, not incidental capture order.
// Other positions and different artists retain their existing tie rules.
fn discrete_coincident_source_tie(
candidate : HitTarget,
previous : HitTarget,
) -> Bool? {
let a = previous.info.point
let b = candidate.info.point
if discrete_kind(previous.info.kind) &&
discrete_kind(candidate.info.kind) &&
previous.shape == Point &&
candidate.shape == Point &&
a.panel_index == b.panel_index &&
a.artist_index == b.artist_index &&
a.screen_x == b.screen_x &&
a.screen_y == b.screen_y {
Some(b.point_index > a.point_index)
} else {
None
}
}
///|
// Later paths resolve transition ties. At a real source endpoint of the same
// artist, a source Point retains its original sample/return slot.
fn proximity_tie_later(candidate : HitTarget, previous : HitTarget?) -> Bool {
guard previous is Some(old) else { return true }
if old.info.kind == Violin || candidate.info.kind == Violin {
return true
}
if !discrete_kind(old.info.kind) && !discrete_kind(candidate.info.kind) {
return false
}
let a = old.info.point
let b = candidate.info.point
if a.panel_index == b.panel_index &&
a.artist_index == b.artist_index &&
discrete_kind(old.info.kind) &&
discrete_kind(candidate.info.kind) {
if discrete_coincident_source_tie(candidate, old) is Some(later) {
return later
}
if old.shape == Point && candidate.shape != Point {
return false
}
if candidate.shape == Point && old.shape != Point {
return true
}
}
true
}