#[allow(clippy::float_cmp, clippy::manual_midpoint)]
pub(crate) fn clip_line(
line: [f64; 4],
w: f64,
h: f64,
mut draw: impl FnMut((f32, f32), (f32, f32)),
) {
let [x0, y0, x1, y1] = line;
if (y0 <= 0.0 && y1 <= 0.0) || (y0 >= h && y1 >= h) {
return;
}
let mut cuts = [0.0, 1.0, 1.0, 1.0];
let mut count = 1;
for edge in [0.0, w] {
if (x0 < edge) != (x1 < edge) {
let t = (edge - x0) / (x1 - x0);
if t > 0.0 && t < 1.0 {
cuts[count] = t;
count += 1;
}
}
}
cuts[count] = 1.0;
if count == 3 && cuts[1] > cuts[2] {
cuts.swap(1, 2);
}
let at = |t: f64| -> (f64, f64) {
if t == 0.0 {
(x0, y0)
} else if t == 1.0 {
(x1, y1)
} else {
(x0 + (x1 - x0) * t, y0 + (y1 - y0) * t)
}
};
for piece in 0..count {
let (a, b) = (at(cuts[piece]), at(cuts[piece + 1]));
let middle = 0.5 * (a.0 + b.0);
if middle > w {
continue;
}
let (xa, xb) = if middle < 0.0 {
(0.0, 0.0)
} else {
(a.0.clamp(0.0, w), b.0.clamp(0.0, w))
};
#[allow(clippy::cast_possible_truncation)]
draw((xa as f32, a.1 as f32), (xb as f32, b.1 as f32));
}
}
#[allow(
clippy::too_many_arguments,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss,
clippy::manual_midpoint
)]
pub(crate) fn accumulate_line(
acc: &mut [f32],
stride: usize,
base: usize,
rows: (usize, usize),
w: f32,
p0: (f32, f32),
p1: (f32, f32),
mut touched: impl FnMut(usize, usize, usize),
) {
if (p0.1 - p1.1).abs() <= f32::EPSILON {
return;
}
let (dir, p0, p1) = if p0.1 < p1.1 {
(1.0f32, p0, p1)
} else {
(-1.0f32, p1, p0)
};
let dxdy = (p1.0 - p0.0) / (p1.1 - p0.1);
let first = if p0.1 < 0.0 { 0 } else { p0.1 as usize };
let last = p1.1.ceil().max(0.0) as usize;
for y in first.max(rows.0)..last.min(rows.1) {
let row = (y - base) * stride;
let top = (y as f32).max(p0.1);
let bottom = ((y + 1) as f32).min(p1.1);
let x = p0.0 + dxdy * (top - p0.1);
let xnext = p0.0 + dxdy * (bottom - p0.1);
let d = (bottom - top) * dir;
let (lo, hi) = if x < xnext { (x, xnext) } else { (xnext, x) };
let (x0, x1) = (lo.clamp(0.0, w), hi.clamp(0.0, w));
let x0floor = x0.floor();
let x0i = x0floor as usize;
let x1ceil = x1.ceil();
let x1i = x1ceil as usize;
if x1i <= x0i + 1 {
let xmf = 0.5 * (x0 + x1) - x0floor;
acc[row + x0i] += d - d * xmf;
acc[row + x0i + 1] += d * xmf;
touched(y, x0i, x0i + 1);
} else {
let s = 1.0 / (x1 - x0);
let x0f = x0 - x0floor;
let a0 = 0.5 * s * (1.0 - x0f) * (1.0 - x0f);
let x1f = x1 - x1ceil + 1.0;
let am = 0.5 * s * x1f * x1f;
acc[row + x0i] += d * a0;
if x1i == x0i + 2 {
acc[row + x0i + 1] += d * (1.0 - a0 - am);
} else {
let a1 = s * (1.5 - x0f);
acc[row + x0i + 1] += d * (a1 - a0);
for xi in x0i + 2..x1i - 1 {
acc[row + xi] += d * s;
}
let a2 = a1 + (x1i - x0i - 3) as f32 * s;
acc[row + x1i - 1] += d * (1.0 - a2 - am);
}
acc[row + x1i] += d * am;
touched(y, x0i, x1i);
}
}
}