use crate::{
type_utils, GrowingVob, PB_Command, PB_Face, PB_KeyValuePair, PB_Model, PB_Reply, PB_Vertex,
TBError,
};
use boostvoronoi as BV;
use cgmath::{EuclideanSpace, MetricSpace, SquareMatrix, Transform, UlpsEq};
use itertools::Itertools;
use linestring::linestring_2d::{Aabb2, VoronoiParabolicArc};
use std::collections::HashMap;
#[allow(dead_code)]
#[inline(always)]
fn transmute_to_f64(a: &(u64, u64)) -> cgmath::Point2<f64> {
cgmath::Point2 {
x: f64::from_bits(a.0),
y: f64::from_bits(a.1),
}
}
#[inline(always)]
#[allow(dead_code)]
fn transmute_to_u64(a: &cgmath::Point2<f64>) -> (u64, u64) {
(a.x.to_bits(), a.y.to_bits())
}
struct DiagramHelperRw {
vertex_map: ahash::AHashMap<(u64, u64), usize>,
pb_vertices: Vec<PB_Vertex>,
}
impl Default for DiagramHelperRw {
fn default() -> Self {
Self {
vertex_map: ahash::AHashMap::new(),
pb_vertices: Vec::new(),
}
}
}
impl DiagramHelperRw {
#[inline(always)]
fn transmute_xy_to_u64(x: f64, y: f64) -> (u64, u64) {
(x.to_bits(), y.to_bits())
}
#[inline(always)]
#[allow(dead_code)]
fn get_dup_checked_vertex(&self, vertex: &[f64; 2]) -> Result<u64, TBError> {
let key = Self::transmute_xy_to_u64(vertex[0], vertex[1]);
if let Some(n) = self.vertex_map.get(&key) {
Ok(*n as u64)
} else {
Err(TBError::InternalError(format!(
"Could not find mapped vertex index of: ({:.12?},{:.12?})",
vertex[0], vertex[1]
)))
}
}
#[inline(always)]
fn place_new_pb_vertex_dup_check(
&mut self,
vertex: &[f64; 3],
inverted_transform: &cgmath::Matrix4<f64>,
) -> usize {
let mut pb_vertices = Vec::<PB_Vertex>::with_capacity(0);
std::mem::swap(&mut self.pb_vertices, &mut pb_vertices);
let key = Self::transmute_xy_to_u64(vertex[0], vertex[1]);
let rv = *self.vertex_map.entry(key).or_insert_with(|| {
let n = pb_vertices.len();
let transformed_v = inverted_transform.transform_point(cgmath::Point3 {
x: vertex[0],
y: vertex[1],
z: vertex[2],
});
pb_vertices.push(PB_Vertex {
x: transformed_v.x,
y: transformed_v.y,
z: transformed_v.z,
});
n
});
std::mem::swap(&mut self.pb_vertices, &mut pb_vertices);
rv
}
#[inline(always)]
#[allow(dead_code)]
fn place_new_pb_vertex_unchecked(
&mut self,
vertex: &[f64; 2],
z_coord: f64,
inverted_transform: &cgmath::Matrix4<f64>,
) -> usize {
let v = inverted_transform.transform_point(cgmath::Point3 {
x: vertex[0],
y: vertex[1],
z: z_coord,
});
let n = self.pb_vertices.len();
self.pb_vertices.push(PB_Vertex {
x: v.x,
y: v.y,
z: v.z,
});
n
}
}
struct DiagramHelperRo {
diagram: BV::Diagram<f64>,
vertices: Vec<BV::Point<i64>>,
segments: Vec<BV::Line<i64>>,
rejected_edges: vob::Vob<u32>,
internal_vertices: vob::Vob<u32>,
inverted_transform: cgmath::Matrix4<f64>,
}
impl DiagramHelperRo {
#[inline(always)]
fn retrieve_point(&self, cell_id: BV::CellIndex) -> Result<BV::Point<i64>, TBError> {
let (index, cat) = self.diagram.get_cell(cell_id)?.get().source_index_2();
Ok(match cat {
BV::SourceCategory::SinglePoint => self.vertices[index],
BV::SourceCategory::SegmentStart => self.segments[index - self.vertices.len()].start,
BV::SourceCategory::Segment | BV::SourceCategory::SegmentEnd => {
self.segments[index - self.vertices.len()].end
}
})
}
#[inline(always)]
fn retrieve_segment(&self, cell_id: BV::CellIndex) -> Result<&BV::Line<i64>, TBError> {
let cell = self.diagram.get_cell(cell_id)?.get();
let index = cell.source_index() - self.vertices.len();
Ok(&self.segments[index])
}
fn convert_secondary_edge(&self, edge: &BV::Edge) -> Result<Vec<[f64; 3]>, TBError> {
let edge_id = edge.id();
let edge_twin_id = self.diagram.edge_get_twin(edge_id)?;
let cell_id = self.diagram.edge_get_cell(edge_id)?;
let cell = self.diagram.get_cell(cell_id)?.get();
let twin_cell_id = self.diagram.get_edge(edge_twin_id)?.get().cell().unwrap();
let segment = if cell.contains_point() {
self.retrieve_segment(twin_cell_id)?
} else {
self.retrieve_segment(cell_id)?
};
let vertex0 = edge.vertex0();
let vertex1 = self.diagram.edge_get_vertex1(edge_id)?;
let start_point = if let Some(vertex0) = vertex0 {
let vertex0 = self.diagram.vertex_get(vertex0)?.get();
if !self.internal_vertices.get_f(vertex0.get_id().0) {
None
} else if vertex0.is_site_point() {
Some(cgmath::Point3 {
x: vertex0.x(),
y: vertex0.y(),
z: 0.0,
})
} else {
Some(cgmath::Point3 {
x: vertex0.x(),
y: vertex0.y(),
z: f64::NAN,
})
}
} else {
None
};
let end_point = if let Some(vertex1) = vertex1 {
let vertex1 = self.diagram.vertex_get(vertex1)?.get();
if !self.internal_vertices.get_f(vertex1.get_id().0) {
None
} else if vertex1.is_site_point() {
Some(cgmath::Point3 {
x: vertex1.x(),
y: vertex1.y(),
z: 0.0,
})
} else {
Some(cgmath::Point3 {
x: vertex1.x(),
y: vertex1.y(),
z: f64::NAN,
})
}
} else {
None
};
let cell_point = {
let cell_point = if cell.contains_point() {
self.retrieve_point(cell_id)?
} else {
self.retrieve_point(twin_cell_id)?
};
cgmath::Point2 {
x: cell_point.x as f64,
y: cell_point.y as f64,
}
};
let segment = linestring::linestring_2d::Line2::from([
segment.start.x as f64,
segment.start.y as f64,
segment.end.x as f64,
segment.end.y as f64,
]);
let mut samples = Vec::<[f64; 3]>::new();
if let Some(start_point) = start_point {
if start_point.z.is_finite() {
samples.push([start_point.x, start_point.y, start_point.z]);
} else {
let z_comp = if cell.contains_point() {
-cell_point.distance(cgmath::Point2::new(start_point.x, start_point.y))
} else {
-linestring::linestring_2d::distance_to_line_squared_safe(
segment.start,
segment.end,
cgmath::Point2 {
x: start_point.x,
y: start_point.y,
},
)
.sqrt()
};
samples.push([start_point.x, start_point.y, z_comp]);
}
}
samples.push([cell_point.x, cell_point.y, 0.0]);
if let Some(end_point) = end_point {
if end_point.z.is_finite() {
samples.push([end_point.x, end_point.y, end_point.z]);
} else {
let z_comp = if cell.contains_point() {
-cell_point.distance(cgmath::Point2::new(end_point.x, end_point.y))
} else {
-linestring::linestring_2d::distance_to_line_squared_safe(
segment.start,
segment.end,
cgmath::Point2::new(end_point.x, end_point.y),
)
.sqrt()
};
samples.push([end_point.x, end_point.y, z_comp]);
}
}
Ok(samples)
}
fn convert_edge(
&self,
edge: &BV::Edge,
discretization_dist: f64,
) -> Result<Vec<[f64; 3]>, TBError> {
let edge_id = edge.id();
let edge_twin_id = self.diagram.edge_get_twin(edge_id)?;
let cell_id = self.diagram.edge_get_cell(edge_id)?;
let cell = self.diagram.get_cell(cell_id)?.get();
let twin_cell_id = self.diagram.get_edge(edge_twin_id)?.get().cell()?;
let segment = if cell.contains_point() {
self.retrieve_segment(twin_cell_id)?
} else {
self.retrieve_segment(cell_id)?
};
let (start_point, startpoint_is_internal) = if let Some(vertex0) = edge.vertex0() {
let vertex0 = self.diagram.vertex_get(vertex0)?.get();
let start_point = if vertex0.is_site_point() {
cgmath::Point3 {
x: vertex0.x(),
y: vertex0.y(),
z: 0.0,
}
} else {
cgmath::Point3 {
x: vertex0.x(),
y: vertex0.y(),
z: f64::NAN,
}
};
(
start_point,
self.internal_vertices.get_f(vertex0.get_id().0),
)
} else {
return Err(TBError::InternalError(format!(
"Edge vertex0 could not be found. {}:{}",
file!(),
line!()
)));
};
let (end_point, end_point_is_internal) =
if let Some(vertex1) = self.diagram.edge_get_vertex1(edge_id)? {
let vertex1 = self.diagram.vertex_get(vertex1)?.get();
let end_point = if vertex1.is_site_point() {
cgmath::Point3 {
x: vertex1.x(),
y: vertex1.y(),
z: 0.0,
}
} else {
cgmath::Point3 {
x: vertex1.x(),
y: vertex1.y(),
z: f64::NAN,
}
};
(end_point, self.internal_vertices.get_f(vertex1.get_id().0))
} else {
return Err(TBError::InternalError(format!(
"Edge vertex1 could not be found. {}:{}",
file!(),
line!()
)));
};
let cell_point = if cell.contains_point() {
self.retrieve_point(cell_id)?
} else {
self.retrieve_point(twin_cell_id)?
};
let cell_point = cgmath::Point2 {
x: cell_point.x as f64,
y: cell_point.y as f64,
};
let segment = linestring::linestring_2d::Line2::from([
segment.start.x as f64,
segment.start.y as f64,
segment.end.x as f64,
segment.end.y as f64,
]);
let mut samples = Vec::<[f64; 3]>::new();
if edge.is_curved() {
let arc = VoronoiParabolicArc::new(
segment,
cell_point,
cgmath::Point2 {
x: start_point.x,
y: start_point.y,
},
cgmath::Point2 {
x: end_point.x,
y: end_point.y,
},
);
for p in arc.discretise_3d(discretization_dist).points().iter() {
samples.push([p.x, p.y, p.z]);
}
} else {
if startpoint_is_internal {
if start_point.z.is_finite() {
samples.push([start_point.x, start_point.y, start_point.z]);
} else {
let z_comp = if cell.contains_point() {
-cell_point.distance(cgmath::Point2::new(start_point.x, start_point.y))
} else {
-linestring::linestring_2d::distance_to_line_squared_safe(
segment.start,
segment.end,
cgmath::Point2 {
x: start_point.x,
y: start_point.y,
},
)
.sqrt()
};
samples.push([start_point.x, start_point.y, z_comp]);
}
}
if end_point_is_internal {
if end_point.z.is_finite() {
samples.push([end_point.x, end_point.y, end_point.z]);
} else {
let z_comp = if cell.contains_point() {
-cell_point.distance(cgmath::Point2::new(end_point.x, end_point.y))
} else {
-linestring::linestring_2d::distance_to_line_squared_safe(
segment.start,
segment.end,
cgmath::Point2::new(end_point.x, end_point.y),
)
.sqrt()
};
samples.push([end_point.x, end_point.y, z_comp]);
}
}
}
Ok(samples)
}
fn convert_edges(
&self,
discretization_dist: f64,
) -> Result<(DiagramHelperRw, ahash::AHashMap<usize, Vec<usize>>), TBError> {
let mut hrw = DiagramHelperRw::default();
let mut rv = ahash::AHashMap::<usize, Vec<usize>>::new();
for edge in self.diagram.edges() {
let edge = edge.get();
let edge_id = edge.id();
if !edge.is_secondary() && self.rejected_edges.get_f(edge_id.0) {
continue;
}
let twin_id = edge.twin()?;
if !rv.contains_key(&twin_id.0) {
let samples = if edge.is_secondary() {
self.convert_secondary_edge(&edge)?
} else {
self.convert_edge(&edge, discretization_dist)?
};
let mut pb_edge: Vec<usize> = Vec::with_capacity(samples.len());
for v in samples.into_iter().map(|coord| {
hrw.place_new_pb_vertex_dup_check(&coord, &self.inverted_transform)
}) {
if !pb_edge.contains(&v) {
pb_edge.push(v);
}
}
let _ = rv.insert(edge_id.0, pb_edge);
} else {
}
}
Ok((hrw, rv))
}
fn split_pb_face_by_segment(
&self,
v0n: u64,
v1n: u64,
pb_face: &PB_Face,
) -> Result<Option<(PB_Face, PB_Face)>, TBError> {
if let Some(v0i) = pb_face.vertices.iter().position(|x| x == &v0n) {
if let Some(v1i) = pb_face.vertices.iter().position(|x| x == &v1n) {
let mut a = Vec::<u64>::new();
let mut b = Vec::<u64>::new();
if v0i < v1i {
for i in (0..=v0i).chain(v1i..pb_face.vertices.len()) {
a.push(pb_face.vertices[i]);
}
for i in v0i..=v1i {
b.push(pb_face.vertices[i]);
}
} else {
for i in (0..=v1i).chain(v0i..pb_face.vertices.len()) {
a.push(pb_face.vertices[i]);
}
for i in v1i..=v0i {
b.push(pb_face.vertices[i]);
}
};
return Ok(Some((PB_Face { vertices: a }, PB_Face { vertices: b })));
}
}
Ok(None)
}
fn iterate_cells(
&self,
mut dhrw: DiagramHelperRw,
edge_map: ahash::AHashMap<usize, Vec<usize>>,
) -> Result<(Vec<PB_Face>, Vec<PB_Vertex>), TBError> {
let mut pb_faces = Vec::<PB_Face>::new();
for cell in self.diagram.cells().iter() {
let cell = cell.get();
let cell_id = cell.id();
if cell.contains_point() {
let cell_point = {
let cp = self.retrieve_point(cell_id)?;
dhrw.place_new_pb_vertex_dup_check(
&[cp.x as f64, cp.y as f64, 0.0],
&self.inverted_transform,
) as u64
};
for edge_id in self.diagram.cell_edge_iterator(cell_id) {
let edge = self.diagram.get_edge(edge_id)?.get();
let twin_id = edge.twin()?;
if self.rejected_edges.get_f(edge_id.0) && !edge.is_secondary() {
continue;
}
let mod_edge: Box<dyn ExactSizeIterator<Item = &usize>> = {
if let Some(e) = edge_map.get(&edge_id.0) {
Box::new(e.iter())
} else {
Box::new(
edge_map
.get(&twin_id.0)
.ok_or_else(|| {
TBError::InternalError(format!(
"could not get twin edge, {}, {}",
file!(),
line!()
))
})?
.iter()
.rev(),
)
}
};
for (a, b) in mod_edge.tuple_windows::<(_, _)>() {
let a = *a as u64;
let b = *b as u64;
if a != cell_point && b != cell_point {
let mut pb_face = PB_Face {
vertices: Vec::new(),
};
let mut face = vec![a, b, cell_point];
pb_face.vertices.append(&mut face);
if pb_face.vertices.len() > 2 {
pb_faces.push(pb_face);
} else {
}
}
}
}
}
if cell.contains_segment() {
let segment = self.retrieve_segment(cell_id)?;
let v0n = dhrw.place_new_pb_vertex_dup_check(
&[segment.start.x as f64, segment.start.y as f64, 0.0],
&self.inverted_transform,
);
let v1n = dhrw.place_new_pb_vertex_dup_check(
&[segment.end.x as f64, segment.end.y as f64, 0.0],
&self.inverted_transform,
);
let mut new_face = PB_Face {
vertices: Vec::new(),
};
for edge_id in self.diagram.cell_edge_iterator(cell_id) {
let edge = self.diagram.get_edge(edge_id)?.get();
let twin_id = edge.twin()?;
let mod_edge: Box<dyn ExactSizeIterator<Item = &usize>> = {
if let Some(e) = edge_map.get(&edge_id.0) {
Box::new(e.iter())
} else if let Some(e) = edge_map.get(&twin_id.0) {
Box::new(e.iter().rev())
} else {
Box::new(None.iter())
}
};
for v in mod_edge.map(|x| *x as u64) {
if !new_face.vertices.contains(&v) {
new_face.vertices.push(v);
}
}
}
if let Some((split_a, split_b)) =
self.split_pb_face_by_segment(v0n as u64, v1n as u64, &new_face)?
{
if split_a.vertices.len() > 2 {
pb_faces.push(split_a);
}
if split_b.vertices.len() > 2 {
pb_faces.push(split_b);
}
} else if new_face.vertices.len() > 2 {
pb_faces.push(new_face);
}
}
}
Ok((pb_faces, dhrw.pb_vertices))
}
}
#[allow(clippy::type_complexity)]
fn parse_input(
input_pb_model: &PB_Model,
cmd_arg_max_voronoi_dimension: f64,
) -> Result<
(
Vec<BV::Point<i64>>,
Vec<BV::Line<i64>>,
Aabb2<f64>,
cgmath::Matrix4<f64>,
),
TBError,
> {
let mut aabb = linestring::linestring_3d::Aabb3::<f64>::default();
for v in input_pb_model.vertices.iter() {
aabb.update_point(cgmath::Point3::new(v.x as f64, v.y as f64, v.z as f64))
}
let (plane, transform, vor_aabb)= centerline::get_transform_relaxed(
aabb,
cmd_arg_max_voronoi_dimension,
super::EPSILON,
f64::default_max_ulps(),
).map_err(|_|{
let aabb_d = aabb.get_high().unwrap() - aabb.get_low().unwrap();
let aabb_c = (aabb.get_high().unwrap().to_vec() + aabb.get_low().unwrap().to_vec())/2.0;
TBError::InputNotPLane(format!(
"Input data not in one plane and/or plane not intersecting origin: Δ({},{},{}) C({},{},{})",
aabb_d.x, aabb_d.y, aabb_d.z, aabb_c.x, aabb_c.y, aabb_c.z))
})?;
let invers_transform = transform
.invert()
.ok_or(TBError::CouldNotCalculateInvertMatrix)?;
println!("voronoi: data was in plane:{:?} aabb:{:?}", plane, aabb);
let mut vor_lines = Vec::<BV::Line<i64>>::with_capacity(input_pb_model.faces.len());
let vor_vertices: Vec<BV::Point<i64>> = input_pb_model
.vertices
.iter()
.map(|vertex| {
let p = type_utils::xy_to_2d(&transform.transform_point(cgmath::Point3 {
x: vertex.x,
y: vertex.y,
z: vertex.z,
}));
BV::Point {
x: p.x as i64,
y: p.y as i64,
}
})
.collect();
let mut used_vertices = vob::Vob::<u32>::fill(vor_vertices.len());
for face in input_pb_model.faces.iter() {
match face.vertices.len() {
3..=usize::MAX => return Err(TBError::ModelContainsFaces("Model can't contain any faces, only edges and points. Use the 2d_outline tool to remove faces".to_string())),
2 => {
let v0 = face.vertices[0] as usize;
let v1 = face.vertices[1] as usize;
vor_lines.push(BV::Line {
start: vor_vertices[v0],
end: vor_vertices[v1],
});
let _ = used_vertices.set(v0, true);
let _ = used_vertices.set(v1, true);
},
_ => (),
}
}
let vor_vertices: Vec<BV::Point<i64>> = vor_vertices
.into_iter()
.enumerate()
.filter(|x| !used_vertices.get_f(x.0))
.map(|x| x.1)
.collect();
Ok((vor_vertices, vor_lines, vor_aabb, invers_transform))
}
fn find_internal_vertices(
diagram: &BV::Diagram<f64>,
rejected_edges: &vob::Vob<u32>,
) -> Result<vob::Vob<u32>, TBError> {
let mut internal_vertices = vob::Vob::<u32>::fill(diagram.vertices().len());
for (_, e) in diagram
.edges()
.iter()
.enumerate()
.filter(|(eid, _)| !rejected_edges.get_f(*eid))
{
let e = e.get();
if e.is_primary() {
if let Some(v0) = e.vertex0() {
let _ = internal_vertices.set(v0.0, true);
}
if let Some(v1) = diagram.edge_get_vertex1(e.id())? {
let _ = internal_vertices.set(v1.0, true);
}
}
}
for (vid, _) in diagram
.vertices()
.iter()
.enumerate()
.filter(|(_, v)| v.get().is_site_point())
{
let _ = internal_vertices.set(vid, true);
}
Ok(internal_vertices)
}
fn voronoi_mesh(
input_pb_model: &PB_Model,
cmd_arg_max_voronoi_dimension: f64,
cmd_discretization_distance: f64,
) -> Result<PB_Model, TBError> {
let (vor_vertices, vor_lines, vor_aabb2, inverted_transform) =
parse_input(input_pb_model, cmd_arg_max_voronoi_dimension)?;
let vor_diagram = {
BV::Builder::default()
.with_vertices(vor_vertices.iter())?
.with_segments(vor_lines.iter())?
.build()?
};
let discretization_dist = {
let max_dist = vor_aabb2.get_high().unwrap() - vor_aabb2.get_low().unwrap();
let max_dist = max_dist.x.max(max_dist.y);
cmd_discretization_distance * max_dist / 100.0
};
let reject_edges = super::voronoi_utils::reject_external_edges(&vor_diagram)?;
let internal_vertices = find_internal_vertices(&vor_diagram, &reject_edges)?;
let diagram_helper = DiagramHelperRo {
vertices: vor_vertices,
segments: vor_lines,
diagram: vor_diagram,
rejected_edges: reject_edges,
internal_vertices,
inverted_transform,
};
let (dhrw, mod_edges) = diagram_helper.convert_edges(discretization_dist)?;
let (pb_faces, pb_vertices) = diagram_helper.iterate_cells(dhrw, mod_edges)?;
Ok(PB_Model {
name: input_pb_model.name.clone(),
world_orientation: input_pb_model.world_orientation.clone(),
faces: pb_faces,
vertices: pb_vertices,
})
}
pub(crate) fn command(
a_command: PB_Command,
options: HashMap<String, String>,
) -> Result<PB_Reply, TBError> {
println!(
r#" __ /\
___ __ ____ _______ ____ ____ ____ |__| _____ ____ ______ |__
\ \/ // __ \\_ __ \/ __ \ / \ / __ \| | / \_/ __ \ / ___/ | \
\ /( \_\ )| | \/ \_\ ) | \ \_\ ) | | | | \ ___/_\___ \| \
\_/ \____/ |__| \____/|___| /\____/|__| |__|_| /\___ /____ \___| /
\/ \/ \/ \/ \/"#
);
if a_command.models.is_empty() {
return Err(TBError::InvalidInputData(
"This operation requires ome input model".to_string(),
));
}
if a_command.models.len() > 1 {
return Err(TBError::InvalidInputData(
"This operation only supports one model as input".to_string(),
));
}
let cmd_arg_max_voronoi_dimension = {
let tmp_value = super::DEFAULT_MAX_VORONOI_DIMENSION.to_string();
let value = options.get("MAX_VORONOI_DIMENSION").unwrap_or(&tmp_value);
value.parse::<f64>().map_err(|_| {
TBError::InvalidInputData(format!(
"Could not parse the MAX_VORONOI_DIMENSION parameter {:?}",
value
))
})?
};
if !(super::DEFAULT_MAX_VORONOI_DIMENSION..100_000_000.0)
.contains(&cmd_arg_max_voronoi_dimension)
{
return Err(TBError::InvalidInputData(format!(
"The valid range of MAX_VORONOI_DIMENSION is [{}..100_000_000[% :({})",
super::DEFAULT_MAX_VORONOI_DIMENSION,
cmd_arg_max_voronoi_dimension
)));
}
let cmd_arg_discretization_distance = {
let tmp_value = super::DEFAULT_VORONOI_DISCRETE_DISTANCE.to_string();
let value = options.get("DISTANCE").unwrap_or(&tmp_value);
value.parse::<f64>().map_err(|_| {
TBError::InvalidInputData(format!(
"Could not parse the DISTANCE parameter {:?}",
value
))
})?
};
if !(super::DEFAULT_VORONOI_DISCRETE_DISTANCE..5.0).contains(&cmd_arg_discretization_distance) {
return Err(TBError::InvalidInputData(format!(
"The valid range of DISTANCE is [{}..5.0[% :({})",
super::DEFAULT_VORONOI_DISCRETE_DISTANCE,
cmd_arg_discretization_distance
)));
}
let max_distance = cmd_arg_max_voronoi_dimension * cmd_arg_discretization_distance / 100.0;
for model in a_command.models.iter().take(1) {
println!("model.name:{:?}, ", model.name);
println!("model.vertices:{:?}, ", model.vertices.len());
println!("model.faces:{:?}, ", model.faces.len());
println!(
"model.world_orientation:{:?}, ",
model.world_orientation.as_ref().map_or(0, |_| 16)
);
println!("MAX_VORONOI_DIMENSION:{:?}", cmd_arg_max_voronoi_dimension);
println!(
"VORONOI_DISCRETE_DISTANCE:{:?}%",
cmd_arg_discretization_distance
);
println!("max_distance:{:?}", max_distance);
println!();
}
let input_model = &a_command.models[0];
let output_model = voronoi_mesh(
input_model,
cmd_arg_max_voronoi_dimension,
cmd_arg_discretization_distance,
)?;
let mut reply = PB_Reply {
options: vec![PB_KeyValuePair {
key: "ONLY_EDGES".to_string(),
value: "False".to_string(),
}],
models: Vec::with_capacity(1),
models32: Vec::with_capacity(0),
};
println!("Total number of vertices: {}", output_model.vertices.len());
println!("Total number of faces: {}", output_model.faces.len());
reply.models.push(output_model);
Ok(reply)
}