pub use crate::plot::plot3d::View3D;
pub struct Projection3D {
rot: [[f64; 3]; 3],
norm_x: (f64, f64),
norm_y: (f64, f64),
norm_z: (f64, f64),
scale: f64,
offset_x: f64,
offset_y: f64,
}
impl Projection3D {
pub fn new(
view: View3D,
x_range: (f64, f64),
y_range: (f64, f64),
z_range: (f64, f64),
plot_cx: f64,
plot_cy: f64,
plot_size: f64,
) -> Self {
let az = view.azimuth.to_radians();
let el = view.elevation.to_radians();
let cos_az = az.cos();
let sin_az = az.sin();
let cos_el = el.cos();
let sin_el = el.sin();
let rot = [
[cos_az, -sin_az, 0.0],
[sin_az * cos_el, cos_az * cos_el, -sin_el],
[sin_az * sin_el, cos_az * sin_el, cos_el],
];
let norm_x = Self::norm_params(x_range);
let norm_y = Self::norm_params(y_range);
let norm_z = Self::norm_params(z_range);
let mut sx_min = f64::INFINITY;
let mut sx_max = f64::NEG_INFINITY;
let mut sy_min = f64::INFINITY;
let mut sy_max = f64::NEG_INFINITY;
for &nx in &[-0.5_f64, 0.5] {
for &ny in &[-0.5_f64, 0.5] {
for &nz in &[-0.5_f64, 0.5] {
let rx = rot[0][0] * nx + rot[0][1] * ny + rot[0][2] * nz;
let rz = rot[2][0] * nx + rot[2][1] * ny + rot[2][2] * nz;
let sx = -rx; let sy = -rz; sx_min = sx_min.min(sx);
sx_max = sx_max.max(sx);
sy_min = sy_min.min(sy);
sy_max = sy_max.max(sy);
}
}
}
let proj_width = sx_max - sx_min;
let proj_height = sy_max - sy_min;
let scale = if proj_width > 0.0 && proj_height > 0.0 {
plot_size * 0.85 / proj_width.max(proj_height)
} else {
plot_size * 0.85
};
let proj_cx = (sx_min + sx_max) / 2.0;
let proj_cy = (sy_min + sy_max) / 2.0;
let offset_x = plot_cx - proj_cx * scale;
let offset_y = plot_cy - proj_cy * scale;
Self {
rot,
norm_x,
norm_y,
norm_z,
scale,
offset_x,
offset_y,
}
}
fn norm_params(range: (f64, f64)) -> (f64, f64) {
let span = range.1 - range.0;
if span.abs() < 1e-15 {
(range.0, 1.0) } else {
(range.0, 1.0 / span)
}
}
#[inline]
pub fn project(&self, x: f64, y: f64, z: f64) -> (f64, f64, f64) {
let nx = (x - self.norm_x.0) * self.norm_x.1 - 0.5;
let ny = (y - self.norm_y.0) * self.norm_y.1 - 0.5;
let nz = (z - self.norm_z.0) * self.norm_z.1 - 0.5;
self.project_normalized(nx, ny, nz)
}
#[inline]
pub fn project_normalized(&self, nx: f64, ny: f64, nz: f64) -> (f64, f64, f64) {
let rx = self.rot[0][0] * nx + self.rot[0][1] * ny + self.rot[0][2] * nz;
let ry = self.rot[1][0] * nx + self.rot[1][1] * ny + self.rot[1][2] * nz;
let rz = self.rot[2][0] * nx + self.rot[2][1] * ny + self.rot[2][2] * nz;
let sx = -rx * self.scale + self.offset_x; let sy = -rz * self.scale + self.offset_y; let depth = ry;
(sx, sy, depth)
}
pub fn view_direction(&self) -> [f64; 3] {
[-self.rot[1][0], -self.rot[1][1], -self.rot[1][2]]
}
}