use crate::camera::Aabb;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum FaceKind {
Unknown,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct FaceDisplay {
pub name: String,
pub topo_id: u64,
pub tri_start: u32,
pub tri_count: u32,
pub kind: FaceKind,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EdgeDisplay {
pub name: String,
pub topo_id: u64,
pub polyline: Vec<[f32; 3]>,
pub aux: bool,
pub centerline: bool,
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub struct VertexDisplay {
pub topo_id: u64,
pub position: [f64; 3],
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct DisplayMesh {
pub positions: Vec<[f32; 3]>,
pub normals: Vec<[f32; 3]>,
pub indices: Vec<u32>,
pub face_ids: Vec<u32>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SolidDisplay {
pub name: String,
pub source_handle: u32,
pub visible: bool,
pub color_override: Option<[f32; 3]>,
pub revision: u64,
pub mesh: DisplayMesh,
pub faces: Vec<FaceDisplay>,
pub edges: Vec<EdgeDisplay>,
pub vertices: Vec<VertexDisplay>,
pub visibility: crate::visibility::EntityVisibility,
pub bbox: Aabb,
pub is_sketch: bool,
}
fn next_revision() -> u64 {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(1);
COUNTER.fetch_add(1, Ordering::Relaxed)
}
#[derive(Debug, Default)]
pub struct RenderScene {
solids: Vec<SolidDisplay>,
index: HashMap<String, usize>,
}
impl RenderScene {
pub fn new() -> Self {
Self::default()
}
pub fn insert_solid(&mut self, solid: SolidDisplay) {
match self.index.get(&solid.name) {
Some(&slot) => self.solids[slot] = solid,
None => {
self.index.insert(solid.name.clone(), self.solids.len());
self.solids.push(solid);
}
}
}
pub fn clear(&mut self) {
self.solids.clear();
self.index.clear();
}
pub fn drain(&mut self) -> Vec<SolidDisplay> {
self.index.clear();
std::mem::take(&mut self.solids)
}
pub fn set_color_override(&mut self, name: &str, color: Option<[f32; 3]>) -> bool {
let Some(slot) = self.index.get(name).copied() else {
return false;
};
let solid = &mut self.solids[slot];
if solid.color_override != color {
solid.color_override = color;
solid.revision = next_revision();
}
true
}
pub fn set_visible(&mut self, name: &str, visible: bool) -> bool {
match self.solid_mut(name) {
Some(solid) => {
solid.visible = visible;
true
}
None => false,
}
}
pub fn listing_json(&self) -> String {
let solids: Vec<serde_json::Value> = self
.solids
.iter()
.map(|solid| {
serde_json::json!({
"name": solid.name,
"kind": "SOLID",
"visible": solid.visible,
"faces": solid.faces.len(),
"edges": solid.edges.len(),
"vertices": solid.vertices.len(),
})
})
.collect();
serde_json::Value::Array(solids).to_string()
}
pub fn remove_solid(&mut self, name: &str) -> bool {
let Some(slot) = self.index.remove(name) else {
return false;
};
self.solids.remove(slot);
for value in self.index.values_mut() {
if *value > slot {
*value -= 1;
}
}
true
}
pub fn solid(&self, name: &str) -> Option<&SolidDisplay> {
self.index.get(name).map(|&slot| &self.solids[slot])
}
pub fn solid_mut(&mut self, name: &str) -> Option<&mut SolidDisplay> {
let slot = *self.index.get(name)?;
Some(&mut self.solids[slot])
}
pub fn solids(&self) -> &[SolidDisplay] {
&self.solids
}
pub fn edge_polyline_world(&self, name: &str) -> Option<Vec<[f64; 3]>> {
for solid in &self.solids {
for edge in &solid.edges {
if edge.name == name {
return Some(
edge.polyline
.iter()
.map(|p| [p[0] as f64, p[1] as f64, p[2] as f64])
.collect(),
);
}
}
}
None
}
pub fn edge_solid_name(&self, name: &str) -> Option<&str> {
for solid in &self.solids {
if solid.edges.iter().any(|edge| edge.name == name) {
return Some(&solid.name);
}
}
None
}
pub fn is_empty(&self) -> bool {
self.solids.is_empty()
}
pub fn bbox(&self) -> Aabb {
let mut bbox = Aabb::empty();
for solid in &self.solids {
if solid.visible {
bbox.union(&solid.bbox);
}
}
bbox
}
}
pub fn solid_display_from_payload(
name: &str,
payload: brep_kernel::DisplaySolidPayload,
) -> SolidDisplay {
let mesh_in = payload.mesh;
let vertex_count = mesh_in.positions.len() / 3;
let mut positions = Vec::with_capacity(vertex_count);
let mut normals = Vec::with_capacity(vertex_count);
let mut bbox = Aabb::empty();
for i in 0..vertex_count {
let p = [
mesh_in.positions[i * 3],
mesh_in.positions[i * 3 + 1],
mesh_in.positions[i * 3 + 2],
];
bbox.expand(p);
positions.push([p[0] as f32, p[1] as f32, p[2] as f32]);
normals.push([
mesh_in.normals[i * 3] as f32,
mesh_in.normals[i * 3 + 1] as f32,
mesh_in.normals[i * 3 + 2] as f32,
]);
}
let mut faces: Vec<FaceDisplay> = payload
.faces
.iter()
.map(|(topo_id, name)| FaceDisplay {
name: name.clone().unwrap_or_default(),
topo_id: *topo_id,
tri_start: 0,
tri_count: 0,
kind: FaceKind::Unknown,
})
.collect();
let mut run_start = 0u32;
let mut run_face: Option<u32> = None;
for (tri, &face_id) in mesh_in.face_ids.iter().enumerate() {
if run_face != Some(face_id) {
run_face = Some(face_id);
run_start = tri as u32;
}
if let Some(face) = faces.get_mut(face_id as usize) {
if face.tri_count == 0 {
face.tri_start = run_start;
}
face.tri_count += 1;
}
}
let edges = payload
.edges
.into_iter()
.map(|(topo_id, name, points)| EdgeDisplay {
name: name.unwrap_or_default(),
topo_id,
polyline: points
.iter()
.map(|p| [p.x as f32, p.y as f32, p.z as f32])
.collect(),
aux: false,
centerline: false,
})
.collect();
let vertices = payload
.vertices
.into_iter()
.map(|(topo_id, p)| VertexDisplay {
topo_id,
position: [p.x, p.y, p.z],
})
.collect();
SolidDisplay {
name: name.to_string(),
source_handle: 0, visible: true,
color_override: None,
revision: next_revision(),
mesh: DisplayMesh {
positions,
normals,
indices: mesh_in.indices,
face_ids: mesh_in.face_ids,
},
faces,
edges,
vertices,
visibility: crate::visibility::EntityVisibility::default(),
bbox,
is_sketch: false, }
}