use crate::cli::KicadVersion;
use crate::error::{ConversionError, Result};
pub struct Converter {
_kicad_version: KicadVersion,
}
impl Converter {
pub fn new(kicad_version: KicadVersion) -> Self {
Self { _kicad_version: kicad_version }
}
pub fn px_to_mil(&self, px: f64) -> i32 {
(10.0 * px) as i32
}
pub fn px_to_mm(&self, px: f64) -> f64 {
10.0 * px * 0.0254
}
pub fn flip_y(&self, y: f64) -> f64 {
-y
}
pub fn normalize_to_bbox(&self, coord: f64, bbox_origin: f64) -> f64 {
coord - bbox_origin
}
pub fn deg_to_rad(&self, degrees: f64) -> f64 {
degrees * std::f64::consts::PI / 180.0
}
pub fn rad_to_deg(&self, radians: f64) -> f64 {
radians * 180.0 / std::f64::consts::PI
}
pub fn compute_arc_center(
&self,
start: (f64, f64),
end: (f64, f64),
radii: (f64, f64),
x_axis_rotation: f64,
large_arc: bool,
sweep: bool,
) -> Result<(f64, f64, f64, f64)> {
let (x1, y1) = start;
let (x2, y2) = end;
let (mut rx, mut ry) = radii;
if (x1 - x2).abs() < 1e-10 && (y1 - y2).abs() < 1e-10 {
return Err(ConversionError::ArcConversion("Start and end points are identical".to_string()).into());
}
if rx.abs() < 1e-10 || ry.abs() < 1e-10 {
return Err(ConversionError::ArcConversion("Radii are too small".to_string()).into());
}
rx = rx.abs();
ry = ry.abs();
let phi = self.deg_to_rad(x_axis_rotation);
let cos_phi = phi.cos();
let sin_phi = phi.sin();
let dx = (x1 - x2) / 2.0;
let dy = (y1 - y2) / 2.0;
let x1_prime = cos_phi * dx + sin_phi * dy;
let y1_prime = -sin_phi * dx + cos_phi * dy;
let lambda = (x1_prime / rx).powi(2) + (y1_prime / ry).powi(2);
if lambda > 1.0 {
rx *= lambda.sqrt();
ry *= lambda.sqrt();
}
let sq = ((rx * ry).powi(2) - (rx * y1_prime).powi(2) - (ry * x1_prime).powi(2))
/ ((rx * y1_prime).powi(2) + (ry * x1_prime).powi(2));
let sq = if sq < 0.0 { 0.0 } else { sq.sqrt() };
let sign = if large_arc == sweep { -1.0 } else { 1.0 };
let cx_prime = sign * sq * rx * y1_prime / ry;
let cy_prime = -sign * sq * ry * x1_prime / rx;
let cx = cos_phi * cx_prime - sin_phi * cy_prime + (x1 + x2) / 2.0;
let cy = sin_phi * cx_prime + cos_phi * cy_prime + (y1 + y2) / 2.0;
let ux = (x1_prime - cx_prime) / rx;
let uy = (y1_prime - cy_prime) / ry;
let vx = (-x1_prime - cx_prime) / rx;
let vy = (-y1_prime - cy_prime) / ry;
let n = (ux.powi(2) + uy.powi(2)).sqrt();
let p = ux;
let sign = if uy < 0.0 { -1.0 } else { 1.0 };
let mut theta1 = sign * (p / n).acos();
let n = ((ux.powi(2) + uy.powi(2)) * (vx.powi(2) + vy.powi(2))).sqrt();
let p = ux * vx + uy * vy;
let sign = if ux * vy - uy * vx < 0.0 { -1.0 } else { 1.0 };
let mut dtheta = sign * (p / n).acos();
if !sweep && dtheta > 0.0 {
dtheta -= 2.0 * std::f64::consts::PI;
} else if sweep && dtheta < 0.0 {
dtheta += 2.0 * std::f64::consts::PI;
}
let theta2 = theta1 + dtheta;
theta1 = self.rad_to_deg(theta1);
let mut theta2 = self.rad_to_deg(theta2);
while theta1 < 0.0 {
theta1 += 360.0;
}
while theta2 < 0.0 {
theta2 += 360.0;
}
while theta1 >= 360.0 {
theta1 -= 360.0;
}
while theta2 >= 360.0 {
theta2 -= 360.0;
}
Ok((cx, cy, theta1, theta2))
}
pub fn calculate_bbox(&self, points: &[(f64, f64)]) -> Option<(f64, f64, f64, f64)> {
if points.is_empty() {
return None;
}
let mut min_x = points[0].0;
let mut max_x = points[0].0;
let mut min_y = points[0].1;
let mut max_y = points[0].1;
for &(x, y) in points.iter().skip(1) {
min_x = min_x.min(x);
max_x = max_x.max(x);
min_y = min_y.min(y);
max_y = max_y.max(y);
}
Some((min_x, min_y, max_x, max_y))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_px_to_mil() {
let converter = Converter::new(KicadVersion::V6);
assert_eq!(converter.px_to_mil(10.0), 100);
assert_eq!(converter.px_to_mil(5.5), 55);
}
#[test]
fn test_px_to_mm() {
let converter = Converter::new(KicadVersion::V6);
let result = converter.px_to_mm(10.0);
assert!((result - 2.54).abs() < 0.001);
}
#[test]
fn test_flip_y() {
let converter = Converter::new(KicadVersion::V6);
assert_eq!(converter.flip_y(10.0), -10.0);
assert_eq!(converter.flip_y(-5.0), 5.0);
}
#[test]
fn test_deg_to_rad() {
let converter = Converter::new(KicadVersion::V6);
let result = converter.deg_to_rad(180.0);
assert!((result - std::f64::consts::PI).abs() < 0.001);
}
}