use crate::error::Error;
use crate::ffi;
use crate::iterators::{EdgeIterator, FaceIterator};
use crate::mesh::Mesh;
use crate::stream::{RustReader, RustWriter};
use glam::{DVec2, DVec3};
use std::io::{Read, Write};
#[cfg(feature = "color")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TShapeId(pub u64);
#[cfg(feature = "color")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rgb {
pub r: f32,
pub g: f32,
pub b: f32,
}
pub struct BooleanShape {
pub shape: Shape,
pub new_faces: Shape,
}
impl From<BooleanShape> for Shape {
fn from(r: BooleanShape) -> Shape {
r.shape
}
}
pub struct Shape {
pub(crate) inner: cxx::UniquePtr<ffi::TopoDS_Shape>,
#[cfg(feature = "color")]
pub colormap: std::collections::HashMap<TShapeId, Rgb>,
}
impl Shape {
pub fn read_step(reader: &mut impl Read) -> Result<Shape, Error> {
let mut rust_reader = RustReader::from_ref(reader);
let inner = ffi::read_step_stream(&mut rust_reader);
if inner.is_null() {
return Err(Error::StepReadFailed);
}
Ok(Shape {
inner,
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
})
}
#[cfg(feature = "color")]
pub fn read_step_with_colors(reader: &mut impl Read) -> Result<Shape, Error> {
let mut rust_reader = RustReader::from_ref(reader);
let d = ffi::read_step_color_stream(&mut rust_reader);
if d.is_null() {
return Err(Error::StepReadFailed);
}
let inner = ffi::colored_step_shape(&d);
if inner.is_null() {
return Err(Error::StepReadFailed);
}
let ids = ffi::colored_step_ids(&d);
let r = ffi::colored_step_colors_r(&d);
let g = ffi::colored_step_colors_g(&d);
let b = ffi::colored_step_colors_b(&d);
let mut colormap = std::collections::HashMap::new();
for i in 0..ids.len() {
colormap.insert(TShapeId(ids[i]), Rgb { r: r[i], g: g[i], b: b[i] });
}
Ok(Shape { inner, colormap })
}
#[cfg(feature = "color")]
pub fn write_step_with_colors(&self, writer: &mut impl Write) -> Result<(), Error> {
let ids: Vec<u64> = self.colormap.keys().map(|k| k.0).collect();
let r: Vec<f32> = ids.iter().map(|&id| self.colormap[&TShapeId(id)].r).collect();
let g: Vec<f32> = ids.iter().map(|&id| self.colormap[&TShapeId(id)].g).collect();
let b: Vec<f32> = ids.iter().map(|&id| self.colormap[&TShapeId(id)].b).collect();
let mut rust_writer = RustWriter::from_ref(writer);
if ffi::write_step_color_stream(&self.inner, &ids, &r, &g, &b, &mut rust_writer) {
Ok(())
} else {
Err(Error::StepWriteFailed)
}
}
#[cfg(feature = "color")]
pub fn read_brep_color(reader: &mut impl Read) -> Result<Shape, Error> {
let mut magic = [0u8; 4];
reader
.read_exact(&mut magic)
.map_err(|_| Error::BrepReadFailed)?;
if &magic != b"CHJC" {
return Err(Error::BrepReadFailed);
}
let mut ver = [0u8; 1];
reader
.read_exact(&mut ver)
.map_err(|_| Error::BrepReadFailed)?;
if ver[0] != 1 {
return Err(Error::BrepReadFailed);
}
let mut buf4 = [0u8; 4];
reader
.read_exact(&mut buf4)
.map_err(|_| Error::BrepReadFailed)?;
let color_count = u32::from_le_bytes(buf4) as usize;
let mut entries: Vec<(u32, f32, f32, f32)> = Vec::with_capacity(color_count);
for _ in 0..color_count {
let mut e = [0u8; 16];
reader
.read_exact(&mut e)
.map_err(|_| Error::BrepReadFailed)?;
let idx = u32::from_le_bytes(e[0..4].try_into().unwrap());
let r = f32::from_le_bytes(e[4..8].try_into().unwrap());
let g = f32::from_le_bytes(e[8..12].try_into().unwrap());
let b = f32::from_le_bytes(e[12..16].try_into().unwrap());
entries.push((idx, r, g, b));
}
let mut buf8 = [0u8; 8];
reader
.read_exact(&mut buf8)
.map_err(|_| Error::BrepReadFailed)?;
let mut rust_reader = RustReader::from_ref(reader);
let inner = ffi::read_brep_bin_stream(&mut rust_reader);
if inner.is_null() {
return Err(Error::BrepReadFailed);
}
let index_to_id: Vec<TShapeId> =
FaceIterator::new(ffi::explore_faces(&inner))
.map(|f| f.tshape_id())
.collect();
let colormap = entries
.into_iter()
.filter_map(|(idx, r, g, b)| {
index_to_id
.get(idx as usize)
.map(|&id| (id, Rgb { r, g, b }))
})
.collect();
Ok(Shape { inner, colormap })
}
#[cfg(feature = "color")]
pub fn write_brep_color(&self, writer: &mut impl Write) -> Result<(), Error> {
let mut brep_buf = Vec::new();
self.write_brep_bin(&mut brep_buf)?;
let id_to_index: std::collections::HashMap<TShapeId, u32> =
FaceIterator::new(ffi::explore_faces(&self.inner))
.enumerate()
.map(|(i, f)| (f.tshape_id(), i as u32))
.collect();
let mut entries: Vec<(u32, f32, f32, f32)> = self
.colormap
.iter()
.filter_map(|(id, rgb)| {
id_to_index.get(id).map(|&idx| (idx, rgb.r, rgb.g, rgb.b))
})
.collect();
entries.sort_by_key(|e| e.0);
writer
.write_all(b"CHJC")
.map_err(|_| Error::BrepWriteFailed)?;
writer
.write_all(&[1u8])
.map_err(|_| Error::BrepWriteFailed)?;
writer
.write_all(&(entries.len() as u32).to_le_bytes())
.map_err(|_| Error::BrepWriteFailed)?;
for (idx, r, g, b) in &entries {
writer
.write_all(&idx.to_le_bytes())
.map_err(|_| Error::BrepWriteFailed)?;
writer
.write_all(&r.to_le_bytes())
.map_err(|_| Error::BrepWriteFailed)?;
writer
.write_all(&g.to_le_bytes())
.map_err(|_| Error::BrepWriteFailed)?;
writer
.write_all(&b.to_le_bytes())
.map_err(|_| Error::BrepWriteFailed)?;
}
writer
.write_all(&(brep_buf.len() as u64).to_le_bytes())
.map_err(|_| Error::BrepWriteFailed)?;
writer
.write_all(&brep_buf)
.map_err(|_| Error::BrepWriteFailed)?;
Ok(())
}
pub fn read_brep_bin(reader: &mut impl Read) -> Result<Shape, Error> {
let mut rust_reader = RustReader::from_ref(reader);
let inner = ffi::read_brep_bin_stream(&mut rust_reader);
if inner.is_null() {
return Err(Error::BrepReadFailed);
}
Ok(Shape {
inner,
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
})
}
pub fn write_step(&self, writer: &mut impl Write) -> Result<(), Error> {
let mut rust_writer = RustWriter::from_ref(writer);
if ffi::write_step_stream(&self.inner, &mut rust_writer) {
Ok(())
} else {
Err(Error::StepWriteFailed)
}
}
pub fn write_brep_bin(&self, writer: &mut impl Write) -> Result<(), Error> {
let mut rust_writer = RustWriter::from_ref(writer);
if ffi::write_brep_bin_stream(&self.inner, &mut rust_writer) {
Ok(())
} else {
Err(Error::BrepWriteFailed)
}
}
pub fn read_brep_text(reader: &mut impl Read) -> Result<Shape, Error> {
let mut rust_reader = RustReader::from_ref(reader);
let inner = ffi::read_brep_text_stream(&mut rust_reader);
if inner.is_null() {
return Err(Error::BrepReadFailed);
}
Ok(Shape {
inner,
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
})
}
pub fn write_brep_text(&self, writer: &mut impl Write) -> Result<(), Error> {
let mut rust_writer = RustWriter::from_ref(writer);
if ffi::write_brep_text_stream(&self.inner, &mut rust_writer) {
Ok(())
} else {
Err(Error::BrepWriteFailed)
}
}
pub fn half_space(plane_origin: DVec3, plane_normal: DVec3) -> Shape {
let inner = ffi::make_half_space(
plane_origin.x,
plane_origin.y,
plane_origin.z,
plane_normal.x,
plane_normal.y,
plane_normal.z,
);
Shape {
inner,
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
}
}
pub fn box_from_corners(corner_1: DVec3, corner_2: DVec3) -> Shape {
let inner = ffi::make_box(
corner_1.x, corner_1.y, corner_1.z, corner_2.x, corner_2.y, corner_2.z,
);
Shape {
inner,
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
}
}
pub fn cylinder(p: DVec3, r: f64, dir: DVec3, h: f64) -> Shape {
let inner = ffi::make_cylinder(p.x, p.y, p.z, dir.x, dir.y, dir.z, r, h);
Shape {
inner,
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
}
}
pub fn empty() -> Shape {
let inner = ffi::make_empty();
Shape {
inner,
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
}
}
pub fn deep_copy(&self) -> Shape {
let inner = ffi::deep_copy(&self.inner);
#[cfg(feature = "color")]
{
let colormap = remap_colormap_by_order(&self.inner, &inner, &self.colormap);
return Shape { inner, colormap };
}
#[cfg(not(feature = "color"))]
Shape {
inner,
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
}
}
}
#[cfg(feature = "color")]
fn remap_colormap_by_order(
old_inner: &ffi::TopoDS_Shape,
new_inner: &ffi::TopoDS_Shape,
old_colormap: &std::collections::HashMap<TShapeId, Rgb>,
) -> std::collections::HashMap<TShapeId, Rgb> {
use crate::iterators::FaceIterator;
let mut colormap = std::collections::HashMap::new();
let old_faces = FaceIterator::new(ffi::explore_faces(old_inner));
let new_faces = FaceIterator::new(ffi::explore_faces(new_inner));
for (old_face, new_face) in old_faces.zip(new_faces) {
if let Some(&color) = old_colormap.get(&old_face.tshape_id()) {
colormap.insert(new_face.tshape_id(), color);
}
}
colormap
}
#[cfg(feature = "color")]
fn merge_colormaps(
from_a: Vec<u64>,
from_b: Vec<u64>,
colormap_a: &std::collections::HashMap<TShapeId, Rgb>,
colormap_b: &std::collections::HashMap<TShapeId, Rgb>,
) -> std::collections::HashMap<TShapeId, Rgb> {
let mut result = std::collections::HashMap::new();
for pair in from_a.chunks(2) {
if let Some(&color) = colormap_a.get(&TShapeId(pair[1])) {
result.insert(TShapeId(pair[0]), color);
}
}
for pair in from_b.chunks(2) {
if let Some(&color) = colormap_b.get(&TShapeId(pair[1])) {
result.insert(TShapeId(pair[0]), color);
}
}
result
}
impl Shape {
pub fn union(&self, other: &Shape) -> Result<BooleanShape, Error> {
let r = ffi::boolean_fuse(&self.inner, &other.inner);
if r.is_null() {
return Err(Error::BooleanOperationFailed);
}
#[cfg(feature = "color")]
let colormap = merge_colormaps(
ffi::boolean_shape_from_a(&r),
ffi::boolean_shape_from_b(&r),
&self.colormap,
&other.colormap,
);
Ok(BooleanShape {
shape: Shape {
inner: ffi::boolean_shape_shape(&r),
#[cfg(feature = "color")]
colormap,
},
new_faces: Shape {
inner: ffi::boolean_shape_new_faces(&r),
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
},
})
}
pub fn subtract(&self, other: &Shape) -> Result<BooleanShape, Error> {
let r = ffi::boolean_cut(&self.inner, &other.inner);
if r.is_null() {
return Err(Error::BooleanOperationFailed);
}
#[cfg(feature = "color")]
let colormap = merge_colormaps(
ffi::boolean_shape_from_a(&r),
ffi::boolean_shape_from_b(&r),
&self.colormap,
&other.colormap,
);
Ok(BooleanShape {
shape: Shape {
inner: ffi::boolean_shape_shape(&r),
#[cfg(feature = "color")]
colormap,
},
new_faces: Shape {
inner: ffi::boolean_shape_new_faces(&r),
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
},
})
}
pub fn intersect(&self, other: &Shape) -> Result<BooleanShape, Error> {
let r = ffi::boolean_common(&self.inner, &other.inner);
if r.is_null() {
return Err(Error::BooleanOperationFailed);
}
#[cfg(feature = "color")]
let colormap = merge_colormaps(
ffi::boolean_shape_from_a(&r),
ffi::boolean_shape_from_b(&r),
&self.colormap,
&other.colormap,
);
Ok(BooleanShape {
shape: Shape {
inner: ffi::boolean_shape_shape(&r),
#[cfg(feature = "color")]
colormap,
},
new_faces: Shape {
inner: ffi::boolean_shape_new_faces(&r),
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
},
})
}
}
impl Shape {
pub fn clean(&self) -> Result<Shape, Error> {
#[cfg(feature = "color")]
{
let r = ffi::clean_shape_full(&self.inner);
if r.is_null() {
return Err(Error::CleanFailed);
}
let inner = ffi::clean_shape_get(&r);
if inner.is_null() {
return Err(Error::CleanFailed);
}
let mapping = ffi::clean_shape_mapping(&r);
let mut colormap = std::collections::HashMap::new();
for pair in mapping.chunks(2) {
let new_id = TShapeId(pair[0]);
let old_id = TShapeId(pair[1]);
if let Some(&color) = self.colormap.get(&old_id) {
colormap.entry(new_id).or_insert(color);
}
}
return Ok(Shape { inner, colormap });
}
#[cfg(not(feature = "color"))]
{
let inner = ffi::clean_shape(&self.inner);
if inner.is_null() {
return Err(Error::CleanFailed);
}
Ok(Shape {
inner,
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
})
}
}
pub fn translated(&self, translation: DVec3) -> Shape {
let inner = ffi::translate_shape(&self.inner, translation.x, translation.y, translation.z);
#[cfg(feature = "color")]
{
let colormap = remap_colormap_by_order(&self.inner, &inner, &self.colormap);
return Shape { inner, colormap };
}
#[cfg(not(feature = "color"))]
Shape {
inner,
#[cfg(feature = "color")]
colormap: std::collections::HashMap::new(),
}
}
pub fn set_global_translation(&mut self, translation: DVec3) {
let translated =
ffi::translate_shape(&self.inner, translation.x, translation.y, translation.z);
self.inner = translated;
}
pub fn mesh_with_tolerance(&self, tol: f64) -> Result<Mesh, Error> {
let data = ffi::mesh_shape(&self.inner, tol);
if !data.success {
return Err(Error::TriangulationFailed);
}
let vertex_count = data.vertices.len() / 3;
let vertices: Vec<DVec3> = (0..vertex_count)
.map(|i| {
DVec3::new(
data.vertices[i * 3],
data.vertices[i * 3 + 1],
data.vertices[i * 3 + 2],
)
})
.collect();
let uvs: Vec<DVec2> = (0..vertex_count)
.map(|i| DVec2::new(data.uvs[i * 2], data.uvs[i * 2 + 1]))
.collect();
let normals: Vec<DVec3> = (0..vertex_count)
.map(|i| {
DVec3::new(
data.normals[i * 3],
data.normals[i * 3 + 1],
data.normals[i * 3 + 2],
)
})
.collect();
let indices: Vec<usize> = data.indices.iter().map(|&i| i as usize).collect();
let face_ids = data.face_tshape_ids;
Ok(Mesh {
vertices,
uvs,
normals,
indices,
face_ids,
})
}
pub fn faces(&self) -> FaceIterator {
let explorer = ffi::explore_faces(&self.inner);
FaceIterator::new(explorer)
}
pub fn edges(&self) -> EdgeIterator {
let explorer = ffi::explore_edges(&self.inner);
EdgeIterator::new(explorer)
}
pub fn is_null(&self) -> bool {
ffi::shape_is_null(&self.inner)
}
pub fn shell_count(&self) -> u32 {
ffi::shape_shell_count(&self.inner)
}
pub fn volume(&self) -> f64 {
ffi::shape_volume(&self.inner)
}
#[cfg(feature = "color")]
pub fn paint(&mut self, color: Rgb) {
let ids: Vec<TShapeId> = self.faces().map(|f| f.tshape_id()).collect();
for id in ids {
self.colormap.insert(id, color);
}
}
}