use egui::{Color32, Response, Ui};
use egui_wgpu::RenderState;
use crate::core::scene3d::mat4::Vec3;
use crate::core::scene3d::pick::picking_segment;
use crate::render::gpu_scene3d::{Scene3dGeometry, Scene3dId};
use crate::render::scene3d_items::ScalarField3D;
use crate::widget::scene_widget::{ScenePickKind, SceneWidget};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FieldPick {
pub position: Vec3,
pub value: Option<f32>,
pub item: FieldPickItem,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FieldPickItem {
Isosurface,
CutPlane,
}
impl FieldPickItem {
pub fn label(self) -> &'static str {
match self {
FieldPickItem::Isosurface => "Isosurface",
FieldPickItem::CutPlane => "CutPlane",
}
}
}
pub struct ScalarFieldView {
scene: SceneWidget,
field: ScalarField3D,
had_data: bool,
}
impl ScalarFieldView {
pub fn new(render_state: &RenderState, id: Scene3dId) -> Self {
let mut scene = SceneWidget::new(render_state, id);
scene.set_light_shininess(32.0);
ScalarFieldView {
scene,
field: ScalarField3D::new(),
had_data: false,
}
}
pub fn set_data(
&mut self,
render_state: &RenderState,
data: &[f32],
depth: usize,
height: usize,
width: usize,
) -> bool {
let first = !self.had_data;
if !self.field.set_data(data, depth, height, width) {
return false;
}
self.had_data = true;
if let Some(bounds) = self.field.bounds() {
if first {
self.scene.set_bounds(render_state, bounds);
} else {
self.scene.set_bounds_keep_view(render_state, bounds);
}
}
self.rebuild(render_state);
true
}
pub fn field(&self) -> &ScalarField3D {
&self.field
}
pub fn field_mut(&mut self) -> &mut ScalarField3D {
&mut self.field
}
pub fn scene(&self) -> &SceneWidget {
&self.scene
}
pub fn scene_mut(&mut self) -> &mut SceneWidget {
&mut self.scene
}
pub fn set_axes_labels(
&mut self,
xlabel: Option<&str>,
ylabel: Option<&str>,
zlabel: Option<&str>,
) {
self.scene.set_axes_labels(xlabel, ylabel, zlabel);
}
pub fn set_scale(&mut self, render_state: &RenderState, sx: f32, sy: f32, sz: f32) {
if self.field.transform().scale() == Vec3::new(sx, sy, sz) {
return;
}
self.field.transform_mut().set_scale(sx, sy, sz);
self.transform_changed(render_state);
}
pub fn scale(&self) -> Vec3 {
self.field.transform().scale()
}
pub fn set_translation(&mut self, render_state: &RenderState, x: f32, y: f32, z: f32) {
if self.field.transform().translation() == Vec3::new(x, y, z) {
return;
}
self.field.transform_mut().set_translation(x, y, z);
self.transform_changed(render_state);
}
pub fn translation(&self) -> Vec3 {
self.field.transform().translation()
}
fn transform_changed(&mut self, render_state: &RenderState) {
if let Some(bounds) = self.field.bounds() {
self.scene.set_bounds(render_state, bounds);
}
self.rebuild(render_state);
}
pub fn add_isosurface(
&mut self,
render_state: &RenderState,
level: f32,
color: Color32,
) -> usize {
let index = self.field.add_isosurface(level, color);
self.rebuild(render_state);
index
}
pub fn add_auto_isosurface(
&mut self,
render_state: &RenderState,
auto: fn(&[f32]) -> f32,
color: Color32,
) -> usize {
let index = self.field.add_auto_isosurface(auto, color);
self.rebuild(render_state);
index
}
pub fn remove_isosurface(&mut self, render_state: &RenderState, index: usize) -> bool {
if self.field.remove_isosurface(index) {
self.rebuild(render_state);
true
} else {
false
}
}
pub fn clear_isosurfaces(&mut self, render_state: &RenderState) {
self.field.clear_isosurfaces();
self.rebuild(render_state);
}
pub fn rebuild(&mut self, render_state: &RenderState) {
let mut geometry = Scene3dGeometry::new();
self.field.append_to(&mut geometry);
self.scene.set_geometry(render_state, geometry);
}
pub fn pick(&self, ndc: (f32, f32)) -> Option<FieldPick> {
let camera = self.scene.camera();
let mvp = camera.matrix();
let mut best: Option<(f32, Vec3, FieldPickItem)> = None;
if let Some(hit) = self.scene.pick(ndc) {
let item = match hit.kind {
ScenePickKind::TexturedSurface { .. } => FieldPickItem::CutPlane,
_ => FieldPickItem::Isosurface,
};
best = Some((hit.ndc_depth, hit.position, item));
}
if let Some(segment) = picking_segment(camera, ndc)
&& let Some(pos) = self.field.pick_cut_plane(segment)
{
let depth = mvp.transform_point(pos, true).z;
if best.is_none_or(|(d, _, _)| depth < d) {
best = Some((depth, pos, FieldPickItem::CutPlane));
}
}
let (_, position, item) = best?;
Some(FieldPick {
position,
value: self.field.value_at(position),
item,
})
}
pub fn show(&mut self, ui: &mut Ui) -> Response {
self.scene.show(ui)
}
}