use agg_rust::trans_affine::TransAffine;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum EllipseOp {
MoveTo(f64, f64),
CubicTo(f64, f64, f64, f64, f64, f64),
ClosePath,
}
#[allow(clippy::too_many_arguments)]
pub fn ellipse_ops(
cx: f64,
cy: f64,
rx: f64,
ry: f64,
rotation: f64,
start_angle: f64,
end_angle: f64,
ccw: bool,
) -> Vec<EllipseOp> {
use std::f64::consts::{FRAC_PI_2, TAU};
let (sin_r, cos_r) = rotation.sin_cos();
let point = |t: f64| {
let (st, ct) = t.sin_cos();
let (ex, ey) = (rx * ct, ry * st);
(cx + cos_r * ex - sin_r * ey, cy + sin_r * ex + cos_r * ey)
};
let deriv = |t: f64| {
let (st, ct) = t.sin_cos();
let (ex, ey) = (-rx * st, ry * ct);
(cos_r * ex - sin_r * ey, sin_r * ex + cos_r * ey)
};
let full = if ccw {
start_angle - end_angle >= TAU
} else {
end_angle - start_angle >= TAU
};
let sweep = if full {
if ccw {
-TAU
} else {
TAU
}
} else if ccw {
-((start_angle - end_angle).rem_euclid(TAU))
} else {
(end_angle - start_angle).rem_euclid(TAU)
};
let (sx, sy) = point(start_angle);
let mut ops = vec![EllipseOp::MoveTo(sx, sy)];
if sweep.abs() < 1e-12 {
return ops;
}
let segments = (sweep.abs() / FRAC_PI_2).ceil().max(1.0) as usize;
let step = sweep / segments as f64;
let k = 4.0 / 3.0 * (step / 4.0).tan();
for i in 0..segments {
let a = start_angle + step * i as f64;
let b = a + step;
let (p0x, p0y) = point(a);
let (d0x, d0y) = deriv(a);
let (p3x, p3y) = point(b);
let (d3x, d3y) = deriv(b);
ops.push(EllipseOp::CubicTo(
p0x + k * d0x,
p0y + k * d0y,
p3x - k * d3x,
p3y - k * d3y,
p3x,
p3y,
));
}
if full {
ops.push(EllipseOp::ClosePath);
}
ops
}
pub fn pixel_snap_offset(t: &TransAffine) -> Option<(f64, f64)> {
let fx = t.tx - t.tx.floor();
let fy = t.ty - t.ty.floor();
if fx != 0.0 || fy != 0.0 {
Some((fx, fy))
} else {
None
}
}
pub fn corners_bounding_rect(corners: [(f64, f64); 4]) -> (f64, f64, f64, f64) {
let (min_x, min_y, max_x, max_y) = corners
.iter()
.fold((f64::MAX, f64::MAX, f64::MIN, f64::MIN), |a, c| {
(a.0.min(c.0), a.1.min(c.1), a.2.max(c.0), a.3.max(c.1))
});
(
min_x,
min_y,
(max_x - min_x).max(0.0),
(max_y - min_y).max(0.0),
)
}
pub fn collapse_lcd_planes(color: &[u8], alpha: &[u8], w: u32, h: u32) -> Option<Vec<u8>> {
let w_u = w as usize;
let h_u = h as usize;
if color.len() < w_u * h_u * 3 || alpha.len() < w_u * h_u * 3 {
return None;
}
let mut rgba = vec![0u8; w_u * h_u * 4];
for i in 0..(w_u * h_u) {
let ci = i * 3;
let di = i * 4;
let px = crate::lcd_coverage::collapse_lcd_pixel(
[color[ci], color[ci + 1], color[ci + 2]],
[alpha[ci], alpha[ci + 1], alpha[ci + 2]],
);
rgba[di..di + 4].copy_from_slice(&px);
}
Some(rgba)
}