use std::{
fmt::Display,
iter::{Product, Sum},
ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign},
};
use geo_aid_figure::Position;
use serde::Serialize;
#[derive(Debug, Clone, Copy, Serialize)]
pub struct Complex {
pub real: f64,
pub imaginary: f64,
}
impl Complex {
#[must_use]
#[inline]
pub const fn new(real: f64, imaginary: f64) -> Self {
Self { real, imaginary }
}
#[must_use]
pub const fn real(real: f64) -> Self {
Self::new(real, 0.0)
}
#[must_use]
pub fn polar(theta: f64, radius: f64) -> Self {
Self::new(theta.cos(), theta.sin()) * radius
}
#[must_use]
#[inline]
pub const fn zero() -> Self {
Self::new(0.0, 0.0)
}
#[must_use]
#[inline]
pub const fn one() -> Self {
Self::new(1.0, 0.0)
}
#[must_use]
pub const fn i() -> Self {
Self::new(0.0, 1.0)
}
#[must_use]
pub fn mul_i(self) -> Complex {
Complex::new(-self.imaginary, self.real)
}
#[must_use]
pub fn magnitude(self) -> f64 {
f64::sqrt(self.real.powi(2) + self.imaginary.powi(2))
}
#[must_use]
pub fn conjugate(self) -> Complex {
Complex::new(self.real, -self.imaginary)
}
#[must_use]
pub fn partial_mul(self, other: Complex) -> Complex {
Complex::new(self.real * other.real, self.imaginary * other.imaginary)
}
#[must_use]
pub fn partial_div(self, other: Complex) -> Complex {
Complex::new(self.real / other.real, self.imaginary / other.imaginary)
}
#[must_use]
pub fn arg(self) -> f64 {
f64::atan2(self.imaginary, self.real)
}
#[must_use]
pub fn normalize(self) -> Complex {
self / self.magnitude()
}
#[must_use]
pub fn len_squared(self) -> f64 {
self.real * self.real + self.imaginary * self.imaginary
}
#[must_use]
pub fn sqrt(self) -> Complex {
if self.real > 0.0 {
let r = self.magnitude();
r.sqrt() * (self + r).normalize()
} else {
(-self).sqrt().mul_i()
}
}
#[must_use]
pub fn sqrt_norm(self) -> Complex {
if self.real > 0.0 {
let r = self.magnitude();
(self + r).normalize()
} else {
(-self).sqrt_norm().mul_i() }
}
#[must_use]
pub fn inverse(self) -> Self {
self.conjugate() / self.len_squared()
}
}
impl From<Complex> for Position {
fn from(value: Complex) -> Self {
Self {
x: value.real,
y: value.imaginary,
}
}
}
impl From<Complex> for geo_aid_figure::Complex {
fn from(value: Complex) -> Self {
Self {
real: value.real,
imaginary: value.imaginary,
}
}
}
impl From<Position> for Complex {
fn from(value: Position) -> Self {
Self::new(value.x, value.y)
}
}
impl Mul for Complex {
type Output = Complex;
fn mul(self, rhs: Complex) -> Self::Output {
Complex::new(
self.real * rhs.real - self.imaginary * rhs.imaginary,
self.real * rhs.imaginary + rhs.real * self.imaginary,
)
}
}
impl Mul<Complex> for f64 {
type Output = Complex;
fn mul(self, rhs: Complex) -> Self::Output {
Complex::new(self * rhs.real, self * rhs.imaginary)
}
}
impl Mul<f64> for Complex {
type Output = Complex;
fn mul(self, rhs: f64) -> Self::Output {
Complex::new(self.real * rhs, self.imaginary * rhs)
}
}
impl MulAssign<f64> for Complex {
fn mul_assign(&mut self, rhs: f64) {
self.real *= rhs;
self.imaginary *= rhs;
}
}
impl Add<f64> for Complex {
type Output = Complex;
fn add(self, rhs: f64) -> Self::Output {
Complex::new(self.real + rhs, self.imaginary)
}
}
impl Add for Complex {
type Output = Complex;
fn add(self, rhs: Self) -> Self::Output {
Complex::new(self.real + rhs.real, self.imaginary + rhs.imaginary)
}
}
impl Div<f64> for Complex {
type Output = Complex;
fn div(self, rhs: f64) -> Self::Output {
Complex::new(self.real / rhs, self.imaginary / rhs)
}
}
impl Div for Complex {
type Output = Complex;
fn div(self, rhs: Complex) -> Self::Output {
(self * rhs.conjugate()) / (rhs.real * rhs.real + rhs.imaginary * rhs.imaginary)
}
}
impl Sub<f64> for Complex {
type Output = Complex;
fn sub(self, rhs: f64) -> Self::Output {
Complex::new(self.real - rhs, self.imaginary)
}
}
impl Sub for Complex {
type Output = Complex;
fn sub(self, rhs: Self) -> Self::Output {
Complex::new(self.real - rhs.real, self.imaginary - rhs.imaginary)
}
}
impl SubAssign for Complex {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl AddAssign for Complex {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl MulAssign for Complex {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl Display for Complex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(precision) = f.precision() {
write!(
f,
"{2:.*} + {3:.*}i",
precision, precision, self.real, self.imaginary
)
} else {
write!(f, "{} + {}i", self.real, self.imaginary)
}
}
}
impl Neg for Complex {
type Output = Complex;
fn neg(self) -> Self::Output {
Complex::new(-self.real, -self.imaginary)
}
}
impl Default for Complex {
fn default() -> Self {
Self {
real: 0.0,
imaginary: 0.0,
}
}
}
impl Sum for Complex {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let mut v = Complex::zero();
for x in iter {
v += x;
}
v
}
}
impl Product for Complex {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
let mut v = Complex::zero();
for x in iter {
v *= x;
}
v
}
}
#[derive(Debug, Clone, Copy, Serialize)]
pub struct Line {
pub origin: Complex,
pub direction: Complex,
}
impl Line {
#[must_use]
pub fn new(origin: Complex, direction: Complex) -> Self {
Self {
origin,
direction: direction.normalize(),
}
}
}
impl From<Line> for geo_aid_figure::Line {
fn from(value: Line) -> Self {
Self {
origin: value.origin.into(),
direction: value.direction.into(),
}
}
}
#[derive(Debug, Clone, Copy, Serialize)]
pub struct Circle {
pub center: Complex,
pub radius: f64,
}
impl From<Circle> for geo_aid_figure::Circle {
fn from(value: Circle) -> Self {
Self {
center: value.center.into(),
radius: value.radius,
}
}
}
#[derive(Debug, Clone, Copy, Serialize)]
pub enum ValueEnum {
Complex(Complex),
Line(Line),
Circle(Circle),
}
impl ValueEnum {
#[must_use]
pub fn as_complex(self) -> Option<Complex> {
match self {
Self::Complex(c) => Some(c),
_ => None,
}
}
#[must_use]
pub fn as_line(self) -> Option<Line> {
match self {
Self::Line(l) => Some(l),
_ => None,
}
}
#[must_use]
pub fn as_circle(self) -> Option<Circle> {
match self {
Self::Circle(l) => Some(l),
_ => None,
}
}
}
impl From<ValueEnum> for geo_aid_figure::Value {
fn from(value: ValueEnum) -> Self {
match value {
ValueEnum::Complex(c) => Self::Complex(c.into()),
ValueEnum::Line(ln) => Self::Line(ln.into()),
ValueEnum::Circle(c) => Self::Circle(c.into()),
}
}
}
#[must_use]
pub fn get_line(p1: Complex, p2: Complex) -> Line {
Line {
origin: p1,
direction: (p2 - p1).normalize(),
}
}
#[must_use]
pub fn get_intersection(k_ln: Line, l_ln: Line) -> Complex {
let Line {
origin: a,
direction: b,
} = k_ln;
let Line {
origin: c,
direction: d,
} = l_ln;
a - b * ((a - c) / d).imaginary / (b / d).imaginary
}
#[must_use]
pub fn get_angle(arm1: Complex, origin: Complex, arm2: Complex) -> f64 {
let arm1_vec = arm1 - origin;
let arm2_vec = arm2 - origin;
let dot_product = arm1_vec.real * arm2_vec.real + arm1_vec.imaginary * arm2_vec.imaginary;
f64::acos(dot_product / (arm1_vec.magnitude() * arm2_vec.magnitude()))
}
#[must_use]
pub fn get_angle_directed(arm1: Complex, origin: Complex, arm2: Complex) -> f64 {
let arm1_vec = arm1 - origin;
let arm2_vec = arm2 - origin;
let p2_rotated = arm2_vec / arm1_vec;
p2_rotated.arg()
}
#[must_use]
pub fn rotate_around(p: Complex, origin: Complex, angle: f64) -> Complex {
(p - origin) * Complex::new(angle.cos(), angle.sin()) + origin
}
#[must_use]
pub fn distance_pt_ln(point: Complex, line: Line) -> f64 {
let point_rot = (point - line.origin) / line.direction;
point_rot.imaginary.abs()
}
#[must_use]
pub fn distance_pt_pt(p1: Complex, p2: Complex) -> f64 {
((p1.real - p2.real) * (p1.real - p2.real)
+ (p1.imaginary - p2.imaginary) * (p1.imaginary - p2.imaginary))
.sqrt()
}