use crate::math_curve::cross;
use crate::math_segment::unsigned_dist_point_to_segment_sqr;
use crate::segment_soa::SegmentSoa;
use super::math_segment::{
Segment, pseudo_signed_dist_point_to_segment, signed_dist_point_to_segment,
};
use glam::Vec2;
const DIST_TOLERANCE: f32 = 0.1;
fn find_closest_seg(pos: Vec2, soa: &SegmentSoa) -> [Option<usize>; 3] {
let mut best_dist = [f32::INFINITY; 3];
let mut best_ortho_sq = [f32::MIN; 3];
let mut best_seg = [0usize; 3];
let n = soa.len();
for i in 0..n {
let from = soa.from[i];
let tangent = soa.tangent[i];
let inv_tsq = soa.inv_tangent_len_sq[i];
let tangent_unit = soa.tangent_unit[i];
let pf = pos - from;
let t = (pf.dot(tangent) * inv_tsq).clamp(0.0, 1.0);
let perp = from + tangent * t - pos;
let dist = perp.length_squared();
let cmask = soa.color_mask[i];
for col in 0..3 {
if (cmask >> col) & 1 == 0 {
continue;
}
let diff = dist - best_dist[col];
let smaller = diff < -DIST_TOLERANCE;
let equal = diff.abs() < DIST_TOLERANCE;
if smaller {
best_dist[col] = dist;
best_seg[col] = i;
let c = cross(tangent_unit, perp);
best_ortho_sq[col] = if dist > 0.0 { c * c / dist } else { 0.0 };
} else if equal {
let c = cross(tangent_unit, perp);
let ortho_sq = if dist > 0.0 { c * c / dist } else { 0.0 };
if ortho_sq > best_ortho_sq[col] {
best_dist[col] = dist;
best_seg[col] = i;
best_ortho_sq[col] = ortho_sq;
}
}
}
}
std::array::from_fn(|i| best_dist[i].is_finite().then_some(best_seg[i]))
}
#[inline]
fn orthogonality_from_soa(pos: Vec2, soa: &SegmentSoa, i: usize) -> f32 {
let from = soa.from[i];
let tangent = soa.tangent[i];
let inv_tsq = soa.inv_tangent_len_sq[i];
let tangent_unit = soa.tangent_unit[i];
let pf = pos - from;
let t = (pf.dot(tangent) * inv_tsq).clamp(0.0, 1.0);
let perp = pos - (from + tangent * t);
let perp_len = perp.length();
if perp_len <= 0.0 {
return 0.0;
}
cross(tangent_unit, perp / perp_len).abs()
}
pub(crate) fn compute_msdf(
pos: Vec2,
soa: &SegmentSoa,
segs: &[Segment],
units_per_em: f32,
) -> [f32; 3] {
let best_seg = find_closest_seg(pos, soa);
let mut result = [0.; 3];
for col in 0..3 {
if let Some(seg) = best_seg[col] {
let seg = &segs[seg];
let pseudo_dist = pseudo_signed_dist_point_to_segment(pos, seg.from, seg.mid, seg.to);
result[col] = pseudo_dist;
}
}
[
norm_dist(result[0], units_per_em),
norm_dist(result[1], units_per_em),
norm_dist(result[2], units_per_em),
]
}
pub(crate) fn compute_mtsdf(
pos: Vec2,
soa: &SegmentSoa,
segs: &[Segment],
units_per_em: f32,
) -> [u8; 4] {
let best_seg = find_closest_seg(pos, soa);
let mut result = [0.; 3];
let mut closest_segment = 0;
let mut closest_distance = f32::INFINITY;
let mut max_ortho = 0.;
for col in 0..3 {
if let Some(seg_index) = best_seg[col] {
let seg = &segs[seg_index];
let pseudo_dist = pseudo_signed_dist_point_to_segment(pos, seg.from, seg.mid, seg.to);
result[col] = pseudo_dist;
let true_dist = unsigned_dist_point_to_segment_sqr(pos, seg.from, seg.to);
let true_ortho = orthogonality_from_soa(pos, soa, seg_index);
let diff = true_dist - closest_distance;
let smaller = diff <= -1.;
let equal = diff.abs() < 1.;
if smaller || (equal && true_ortho > max_ortho) {
closest_distance = true_dist;
closest_segment = seg_index;
max_ortho = true_ortho
}
}
}
if closest_distance.is_finite() {
let seg = segs[closest_segment];
closest_distance = signed_dist_point_to_segment(pos, seg.from, seg.mid, seg.to);
} else {
closest_distance = 0.;
}
[
(norm_dist(result[0], units_per_em) * 255.) as u8,
(norm_dist(result[1], units_per_em) * 255.) as u8,
(norm_dist(result[2], units_per_em) * 255.) as u8,
(norm_dist(closest_distance, units_per_em) * 255.) as u8,
]
}
pub fn median(r: u8, g: u8, b: u8) -> u8 {
u8::max(r.min(g), r.max(g).min(b))
}
#[inline]
fn norm_dist(dist: f32, units_per_em: f32) -> f32 {
(dist / units_per_em + 0.5).clamp(0., 1.)
}