use super::SearchPatternImpl;
use crate::{
HronnError, ProbeMode,
meshanalyzer::MeshAnalyzer,
prelude::*,
probe::QueryParameters,
searchpattern::{
SearchPatternConfig, adaptivesamplebuffer::AdaptiveSampleBuffer,
meanderpath::ConvexHullMeanderPath,
},
triangulation::{DelaunayContainer, DelaunayPos},
};
use krakel::PointTrait;
#[allow(unused_imports)]
use rayon::prelude::*;
use spade::{DelaunayTriangulation, Triangulation};
use std::marker::PhantomData;
use vector_traits::{
num_traits::{AsPrimitive, real::Real},
prelude::{Aabb2, Approx, GenericScalar, GenericVector2, GenericVector3, HasXY, HasXYZ},
};
#[derive(Default)]
pub struct TriangulatePattern<T: GenericVector3, MESH: HasXYZ>
where
MESH: ConvertTo<T>,
{
aabb: Option<<<T as GenericVector3>::Vector2 as GenericVector2>::Aabb>,
convex_hull: Option<Vec<T::Vector2>>,
step: Option<T::Scalar>,
#[doc(hidden)]
_pd: PhantomData<fn(MESH) -> MESH>,
}
impl<T: GenericVector3, MESH: HasXYZ> TriangulatePattern<T, MESH>
where
T::Vector2: PointTrait<PScalar = T::Scalar>,
T: ConvertTo<MESH>,
MESH: ConvertTo<T>,
{
pub fn new(
aabb: <<T as GenericVector3>::Vector2 as GenericVector2>::Aabb,
convex_hull: Vec<T::Vector2>,
step: T::Scalar,
) -> Result<Self, HronnError> {
Ok(Self {
aabb: Some(aabb),
convex_hull: Some(convex_hull),
step: Some(step),
_pd: PhantomData,
})
}
pub fn convex_hull(mut self, convex_hull: Vec<T::Vector2>) -> Result<Self, HronnError> {
self.convex_hull = Some(convex_hull);
Ok(self)
}
pub fn aabb(
mut self,
aabb: <<T as GenericVector3>::Vector2 as GenericVector2>::Aabb,
) -> Result<Self, HronnError> {
self.aabb = Some(aabb);
Ok(self)
}
pub fn step(mut self, step: T::Scalar) -> Result<Self, HronnError> {
self.step = Some(step);
Ok(self)
}
}
impl<T: GenericVector3, MESH: HasXYZ> SearchPattern<T, MESH> for TriangulatePattern<T, MESH>
where
T::Vector2: PointTrait<PScalar = T::Scalar>,
T: ConvertTo<MESH>,
MESH: ConvertTo<T> + Approx,
u32: AsPrimitive<MESH::Scalar>,
u32: AsPrimitive<T::Scalar>,
T::Scalar: AsPrimitive<MESH::Scalar>,
{
fn search(
&self,
context: &MeshAnalyzer<'_, T, MESH>,
config: &SearchPatternConfig<'_, T, MESH>,
) -> Result<StrategyResult<MESH>, HronnError> {
if config.probe._mesh_analyzer() != context {
return Err(HronnError::Mismatch("".to_string()));
}
if config.probe._mesh_analyzer() != context {
return Err(HronnError::Mismatch("".to_string()));
}
if self.convex_hull.is_none() {
return Err(HronnError::MissingParameter("convex_hull".to_string()));
}
if self.step.is_none() {
return Err(HronnError::MissingParameter("step".to_string()));
}
if self.step.is_none() {
return Err(HronnError::MissingParameter("step".to_string()));
}
self.search_impl(context, config)
}
}
impl<T: GenericVector3, MESH: HasXYZ> SearchPatternImpl<T, MESH> for TriangulatePattern<T, MESH>
where
T::Vector2: PointTrait<PScalar = T::Scalar>,
T: ConvertTo<MESH>,
MESH: ConvertTo<T> + Approx,
T::Scalar: AsPrimitive<MESH::Scalar>,
u32: AsPrimitive<MESH::Scalar>,
u32: AsPrimitive<T::Scalar>,
{
fn search_impl(
&self,
context: &MeshAnalyzer<'_, T, MESH>,
config: &SearchPatternConfig<'_, T, MESH>,
) -> Result<StrategyResult<MESH>, HronnError> {
let search_radius = Self::calculate_search_radius(
config.probe.probe_radius(),
context.triangle_side_length,
);
let convex_hull = self.convex_hull.as_ref().unwrap();
let minimum_z = config.minimum_z;
let step = self.step.unwrap();
let qp = {
let ma = context;
QueryParameters {
vertices: ma.vertices.as_ref(),
indices: ma.indices.as_ref(),
meta_data: config.probe._meta_data(),
probe_radius: config.probe.probe_radius(),
probe_height: config.probe.probe_slope(),
search_radius,
}
};
let collision_fn = config.probe._collision_fn();
let search_fn = match context.mode {
ProbeMode::BruteForce => {
println!("Probing the mesh by brute force");
Self::brute_force_query_point
}
ProbeMode::OriginalTriangles => Self::query_nonsplit_point,
ProbeMode::SplitTriangles => Self::query_split_point,
};
let aabb = self.aabb.unwrap();
let zag_unit_vector = T::Vector2::new_2d(T::Scalar::ONE, T::Scalar::ZERO);
let zig_unit_vector = T::Vector2::new_2d(T::Scalar::ZERO, T::Scalar::ONE);
let path = ConvexHullMeanderPath::<T, MESH>::new(
convex_hull,
aabb,
aabb.min(),
zig_unit_vector,
zag_unit_vector,
step,
);
let result: Result<Vec<_>, HronnError> = if let Some(adaptive_mesh_config) =
config.adaptive_mesh_config
{
println!(
"config.adaptive_mesh_config:{:?}",
config.adaptive_mesh_config
);
let cached_reduce_setting = config.adaptive_mesh_config.unwrap().reduce;
let xy_dist_criteria_sq = adaptive_mesh_config.xy_sample_dist.powi(2);
let z_dist_criteria = adaptive_mesh_config.z_jump_threshold;
path.into_par_iter()?
.map(|chunk| {
let mut buffer: AdaptiveSampleBuffer<T, MESH, _> = AdaptiveSampleBuffer::new(
adaptive_mesh_config.buffer_size,
xy_dist_criteria_sq,
z_dist_criteria,
|pos: T::Vector2| -> MESH {
let rv: Option<T> = search_fn(self, context, pos, &qp, collision_fn)
.map(|z| pos.to_3d(z.z_value.max(minimum_z)));
rv.unwrap_or_else(|| pos.to_3d(minimum_z)).to()
},
);
let buffer_fn = if cached_reduce_setting {
AdaptiveSampleBuffer::sample_and_refine_reduce
} else {
AdaptiveSampleBuffer::sample_and_refine
};
for zag_iter in chunk {
for pos in zag_iter? {
buffer_fn(&mut buffer, pos);
}
buffer.drain();
}
Ok(buffer.finalize())
})
.collect()
} else {
path.into_par_iter()?
.map(|chunk| {
let mut result = Vec::<MESH>::new();
for zag_iter in chunk {
for pos in zag_iter? {
let rv = search_fn(self, context, pos, &qp, collision_fn)
.map(|z| pos.to_3d(z.z_value.max(minimum_z)));
result.push(rv.unwrap_or_else(|| pos.to_3d(minimum_z)).to());
}
}
Ok(result)
})
.collect()
};
let result = result?;
let convex_hull: Vec<MESH> = convex_hull
.iter()
.map(|v| {
let r = search_fn(self, context, *v, &qp, collision_fn)
.map(|z| z.z_value.max(minimum_z))
.unwrap_or(minimum_z);
v.to_3d(r).to()
})
.collect();
let (vertices, indices) =
self.triangulate_meandering_vertices(aabb, convex_hull, result)?;
Ok(StrategyResult::MeshData(MeshData { vertices, indices }))
}
}
impl<T: GenericVector3, MESH: HasXYZ> TriangulatePattern<T, MESH>
where
T::Vector2: PointTrait<PScalar = T::Scalar>,
T: ConvertTo<MESH>,
MESH: ConvertTo<T>,
T::Scalar: AsPrimitive<MESH::Scalar>,
{
fn triangulate_meandering_vertices(
&self,
aabb2: <<T as GenericVector3>::Vector2 as GenericVector2>::Aabb,
hull_3d: Vec<MESH>,
vertices: Vec<Vec<MESH>>,
) -> Result<(Vec<MESH>, Vec<u32>), HronnError> {
if let Some((min, max, delta)) = Some(aabb2.extents()) {
let min = min.to_3d(T::Scalar::ZERO).to();
let max = max.to_3d(T::Scalar::ZERO).to();
let width: MESH::Scalar = delta.x().as_();
let height: MESH::Scalar = delta.y().as_();
let width = width * (20.0.into());
let height = height * (20.0.into());
let big_triangle_a =
MESH::new_3d(min.x() - width, min.y() - height, MESH::Scalar::ZERO);
let big_triangle_b =
MESH::new_3d(max.x() + width, min.y() - height, MESH::Scalar::ZERO);
let big_triangle_c = MESH::new_3d(
min.x() + width / MESH::Scalar::TWO,
max.y() - height,
MESH::Scalar::ZERO,
);
println!("Big triangle: {big_triangle_a:?},{big_triangle_b:?},{big_triangle_c:?},",);
let mut tris = DelaunayContainer {
delaunay: DelaunayTriangulation::<DelaunayPos<MESH>>::new(),
vertices: Vec::<MESH>::with_capacity(4 + hull_3d.len() + vertices.len()),
indices: Vec::<u32>::with_capacity((4 + hull_3d.len() + vertices.len()) * 3),
};
let mut last_vertex = tris.insert_vertex(big_triangle_a)?;
last_vertex = tris.insert_vertex_with_hint(big_triangle_b, last_vertex)?;
let _ = tris.insert_vertex_with_hint(big_triangle_c, last_vertex)?;
last_vertex = tris.insert_vertex(*hull_3d.first().unwrap())?;
for v in hull_3d.iter().skip(1) {
last_vertex = tris.insert_vertex_with_hint(*v, last_vertex)?;
}
for v in vertices.iter() {
for vv in v {
last_vertex = tris.insert_vertex_with_hint(*vv, last_vertex)?;
}
}
for face in tris.delaunay.inner_faces() {
let vertices = face.vertices();
let id0 = vertices[0].fix().index() as u32; let id1 = vertices[1].fix().index() as u32;
let id2 = vertices[2].fix().index() as u32;
if id0 > 2 && id1 > 2 && id2 > 2 {
let id0 = id0 - 3;
let id1 = id1 - 3;
let id2 = id2 - 3;
tris.indices.push(id0);
tris.indices.push(id1);
tris.indices.push(id2);
} else {
}
}
Ok((tris.vertices, tris.indices))
} else {
Ok((vec![], vec![]))
}
}
}