use crate::symbolic::calculus::definite_integrate;
use crate::symbolic::calculus::substitute;
use crate::symbolic::core::Expr;
use crate::symbolic::simplify_dag::simplify;
use crate::symbolic::vector::Vector;
use crate::symbolic::vector::partial_derivative_vector;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ParametricCurve {
pub r: Vector,
pub t_var: String,
pub t_bounds: (Expr, Expr),
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ParametricSurface {
pub r: Vector,
pub u_var: String,
pub u_bounds: (Expr, Expr),
pub v_var: String,
pub v_bounds: (Expr, Expr),
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Volume {
pub z_bounds: (Expr, Expr),
pub y_bounds: (Expr, Expr),
pub x_bounds: (Expr, Expr),
pub vars: (String, String, String),
}
#[must_use]
pub fn line_integral_scalar(
scalar_field: &Expr,
curve: &ParametricCurve,
) -> Expr {
let r_prime = partial_derivative_vector(&curve.r, &curve.t_var);
let r_prime_magnitude = r_prime.magnitude();
let sub = |expr: &Expr| {
let e1 = substitute(expr, "x", &curve.r.x);
let e2 = substitute(&e1, "y", &curve.r.y);
substitute(&e2, "z", &curve.r.z)
};
let field_on_curve = sub(scalar_field);
let integrand = simplify(&Expr::new_mul(field_on_curve, r_prime_magnitude));
let integral = definite_integrate(
&integrand,
&curve.t_var,
&curve.t_bounds.0,
&curve.t_bounds.1,
);
simplify(&integral)
}
#[must_use]
pub fn line_integral_vector(
vector_field: &Vector,
curve: &ParametricCurve,
) -> Expr {
let r_prime = partial_derivative_vector(&curve.r, &curve.t_var);
let sub = |expr: &Expr| {
let e1 = substitute(expr, "x", &curve.r.x);
let e2 = substitute(&e1, "y", &curve.r.y);
substitute(&e2, "z", &curve.r.z)
};
let field_on_curve = Vector::new(
sub(&vector_field.x),
sub(&vector_field.y),
sub(&vector_field.z),
);
let integrand = field_on_curve.dot(&r_prime);
let integral = definite_integrate(
&integrand,
&curve.t_var,
&curve.t_bounds.0,
&curve.t_bounds.1,
);
simplify(&integral)
}
#[must_use]
pub fn surface_integral(
field: &Vector,
surface: &ParametricSurface,
) -> Expr {
let r_u = partial_derivative_vector(&surface.r, &surface.u_var);
let r_v = partial_derivative_vector(&surface.r, &surface.v_var);
let normal_vector = r_u.cross(&r_v);
let sub = |expr: &Expr| {
let e1 = substitute(expr, "x", &surface.r.x);
let e2 = substitute(&e1, "y", &surface.r.y);
substitute(&e2, "z", &surface.r.z)
};
let field_on_surface = Vector::new(sub(&field.x), sub(&field.y), sub(&field.z));
let integrand = field_on_surface.dot(&normal_vector);
let inner_integral = definite_integrate(
&integrand,
&surface.u_var,
&surface.u_bounds.0,
&surface.u_bounds.1,
);
let outer_integral = definite_integrate(
&inner_integral,
&surface.v_var,
&surface.v_bounds.0,
&surface.v_bounds.1,
);
simplify(&outer_integral)
}
#[must_use]
pub fn volume_integral(
scalar_field: &Expr,
volume: &Volume,
) -> Expr {
let (x_var, y_var, z_var) = (&volume.vars.0, &volume.vars.1, &volume.vars.2);
let integral_z =
definite_integrate(scalar_field, z_var, &volume.z_bounds.0, &volume.z_bounds.1);
let integral_y = definite_integrate(&integral_z, y_var, &volume.y_bounds.0, &volume.y_bounds.1);
let integral_x = definite_integrate(&integral_y, x_var, &volume.x_bounds.0, &volume.x_bounds.1);
simplify(&integral_x)
}