use acadrust::entities::acis::{SabReader, SatBody, SatDocument}; use acadrust::entities::{Body, Region, Solid3D}; use cadkernel::brep::{self, Body as KernelBody}; use crate::scene::model::mesh_model::{MeshLodSet, MeshModel}; fn tessellate_acis( sat: &SatDocument, name: String, color: [f32; 4], facet_res: f64, chordal_deflection: Option, isolines: usize, ) -> Option { crate::scene::convert::acis_kernel::tessellate_sat( sat, name, color, facet_res, chordal_deflection, isolines, ) } pub(crate) fn body_transform( sat: &SatDocument, body_record: usize, ) -> Result, ()> { let body = SatBody::from_record(sat.record(body_record).ok_or(())?).ok_or(())?; let Some(transform_record) = body.transform().index() else { return Ok(None); }; let transform = sat.record(transform_record).ok_or(())?; if transform.entity_type != "transform" { return Err(()); } let mut values = Vec::with_capacity(13); for token in &transform.tokens { if values.len() >= 13 { break; } if let Some((components, len)) = token.coordinate_components() { values.extend_from_slice(&components[..len]); } else if let Some(value) = token.as_float() { values.push(value); } else if let Some(value) = token.as_integer() { values.push(value as f64); } else if let Some(text) = token.as_string() { for word in text.split_ascii_whitespace() { let Ok(value) = word.parse::() else { break; }; values.push(value); if values.len() >= 13 { break; } } } } (values.len() >= 13 && values[..13].iter().all(|value| value.is_finite()) && values[12] > 0.0) .then(|| { ( [ values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], ], [values[9], values[10], values[11]], values[12], ) }) .map(Some) .ok_or(()) } pub(crate) fn finalize_mesh( name: String, verts: Vec<[f64; 3]>, normals: Vec<[f32; 3]>, indices: Vec, triangle_material_handles: Vec>, triangle_colors: Vec>, color: [f32; 4], xform: Option<([f64; 9], [f64; 3], f64)>, ) -> MeshModel { let mut verts_high = Vec::with_capacity(verts.len()); let mut verts_low = Vec::with_capacity(verts.len()); for [x, y, z] in verts { let [x, y, z] = match xform { Some((matrix, translation, scale)) => [ scale * (x * matrix[0] + y * matrix[3] + z * matrix[6]) + translation[0], scale * (x * matrix[1] + y * matrix[4] + z * matrix[7]) + translation[1], scale * (x * matrix[2] + y * matrix[5] + z * matrix[8]) + translation[2], ], None => [x, y, z], }; let high = [x as f32, y as f32, z as f32]; verts_high.push(high); verts_low.push([ (x - high[0] as f64) as f32, (y - high[1] as f64) as f32, (z - high[2] as f64) as f32, ]); } let normals = match xform { Some((matrix, _, _)) => normals .iter() .map(|normal| { let [x, y, z] = [normal[0] as f64, normal[1] as f64, normal[2] as f64]; let transformed = [ x * matrix[0] + y * matrix[3] + z * matrix[6], x * matrix[1] + y * matrix[4] + z * matrix[7], x * matrix[2] + y * matrix[5] + z * matrix[8], ]; let length = (transformed[0] * transformed[0] + transformed[1] * transformed[1] + transformed[2] * transformed[2]) .sqrt(); if length > 1e-9 { [ (transformed[0] / length) as f32, (transformed[1] / length) as f32, (transformed[2] / length) as f32, ] } else { *normal } }) .collect(), None => normals, }; MeshModel { name, verts: verts_high, verts_low, normals, indices, triangle_material_handles, triangle_colors, color, selected: false, } } fn parse_acis( sat_fn: impl FnOnce() -> Option, is_binary: bool, sab_data: &[u8], ) -> Option { sat_fn().or_else(|| { (is_binary && !sab_data.is_empty()) .then(|| SabReader::read(sab_data).ok()) .flatten() }) } pub fn kernel_body(solid: &Solid3D) -> Option { let sat = parse_acis( || solid.parse_sat(), solid.acis_data.is_binary, &solid.acis_data.sab_data, )?; let (mut bodies, loss) = cadkernel::acis::lift(&sat); if !loss.is_empty() || bodies.len() != 1 { return None; } let body = bodies.pop()?; let source = body.provenance.source()?; let Some((matrix, translation, scale)) = body_transform(&sat, source.index() as usize).ok()? else { return Some(body); }; brep::transform( &body, &brep::Placement { x_axis: [scale * matrix[0], scale * matrix[1], scale * matrix[2]], y_axis: [scale * matrix[3], scale * matrix[4], scale * matrix[5]], z_axis: [scale * matrix[6], scale * matrix[7], scale * matrix[8]], origin: translation, }, ) } fn remap_acis_material_bindings( set: &mut MeshLodSet, acis: &acadrust::entities::AcisData, ) { for lod in &mut set.lods { for handle in lod.triangle_material_handles.iter_mut().flatten() { let reference = handle.value() as i32; if let Some(material_handle) = acis.materials.iter().find_map(|binding| { (binding.absolute_reference == reference || binding.array_index == reference) .then_some(binding.material_handle) .flatten() }) { *handle = material_handle; } } } } fn finish( sat: SatDocument, name: String, color: [f32; 4], facet_res: f64, chordal_deflection: Option, isolines: usize, acis: &acadrust::entities::AcisData, ) -> Option { let mut set = tessellate_acis( &sat, name, color, facet_res, chordal_deflection, isolines, )?; remap_acis_material_bindings(&mut set, acis); Some(set) } pub fn tessellate_region( region: &Region, color: [f32; 4], facet_res: f64, chordal_deflection: Option, isolines: usize, ) -> Option { let sat = parse_acis( || region.parse_sat(), region.acis_data.is_binary, ®ion.acis_data.sab_data, )?; finish( sat, region.common.handle.value().to_string(), color, facet_res, chordal_deflection, isolines, ®ion.acis_data, ) } pub fn tessellate_body( body: &Body, color: [f32; 4], facet_res: f64, chordal_deflection: Option, isolines: usize, ) -> Option { let sat = parse_acis( || body.parse_sat(), body.acis_data.is_binary, &body.acis_data.sab_data, )?; finish( sat, body.common.handle.value().to_string(), color, facet_res, chordal_deflection, isolines, &body.acis_data, ) } pub fn tessellate_surface( surface: &acadrust::entities::Surface, color: [f32; 4], facet_res: f64, chordal_deflection: Option, isolines: usize, ) -> Option { let sat = parse_acis( || surface.parse_sat(), surface.acis_data.is_binary, &surface.acis_data.sab_data, )?; finish( sat, surface.common.handle.value().to_string(), color, facet_res, chordal_deflection, isolines, &surface.acis_data, ) } pub fn tessellate_solid3d( solid: &Solid3D, color: [f32; 4], facet_res: f64, chordal_deflection: Option, isolines: usize, ) -> Option { let sat = parse_acis( || solid.parse_sat(), solid.acis_data.is_binary, &solid.acis_data.sab_data, )?; finish( sat, solid.common.handle.value().to_string(), color, facet_res, chordal_deflection, isolines, &solid.acis_data, ) }