use crate::bounding_volume::Aabb;
use crate::math::{Pose, Vector};
use crate::partitioning::{Bvh, BvhBuildStrategy};
use crate::query::{PointProjection, PointQueryWithLocation};
use crate::shape::composite_shape::CompositeShape;
use crate::shape::{FeatureId, Segment, SegmentPointLocation, Shape, TypedCompositeShape};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::query::details::NormalConstraints;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
)]
pub struct Polyline {
bvh: Bvh,
vertices: Vec<Vector>,
indices: Vec<[u32; 2]>,
}
impl Polyline {
pub fn new(vertices: Vec<Vector>, indices: Option<Vec<[u32; 2]>>) -> Self {
let indices =
indices.unwrap_or_else(|| (0..vertices.len() as u32 - 1).map(|i| [i, i + 1]).collect());
let leaves = indices.iter().enumerate().map(|(i, idx)| {
let aabb =
Segment::new(vertices[idx[0] as usize], vertices[idx[1] as usize]).local_aabb();
(i, aabb)
});
let bvh = Bvh::from_iter(BvhBuildStrategy::Binned, leaves);
Self {
bvh,
vertices,
indices,
}
}
pub fn aabb(&self, pos: &Pose) -> Aabb {
self.bvh.root_aabb().transform_by(pos)
}
pub fn local_aabb(&self) -> Aabb {
self.bvh.root_aabb()
}
pub fn bvh(&self) -> &Bvh {
&self.bvh
}
pub fn num_segments(&self) -> usize {
self.indices.len()
}
pub fn segments(&self) -> impl ExactSizeIterator<Item = Segment> + '_ {
self.indices.iter().map(move |ids| {
Segment::new(
self.vertices[ids[0] as usize],
self.vertices[ids[1] as usize],
)
})
}
pub fn segment(&self, i: u32) -> Segment {
let idx = self.indices[i as usize];
Segment::new(
self.vertices[idx[0] as usize],
self.vertices[idx[1] as usize],
)
}
pub fn segment_feature_to_polyline_feature(
&self,
segment: u32,
_feature: FeatureId,
) -> FeatureId {
#[cfg(feature = "dim2")]
return FeatureId::Face(segment);
#[cfg(feature = "dim3")]
return FeatureId::Edge(segment);
}
pub fn vertices(&self) -> &[Vector] {
&self.vertices[..]
}
pub fn indices(&self) -> &[[u32; 2]] {
&self.indices
}
pub fn flat_indices(&self) -> &[u32] {
unsafe {
let len = self.indices.len() * 2;
let data = self.indices.as_ptr() as *const u32;
core::slice::from_raw_parts(data, len)
}
}
pub fn scaled(mut self, scale: Vector) -> Self {
self.vertices.iter_mut().for_each(|pt| *pt *= scale);
let mut bvh = self.bvh.clone();
bvh.scale(scale);
Self {
bvh,
vertices: self.vertices,
indices: self.indices,
}
}
pub fn reverse(&mut self) {
for idx in &mut self.indices {
idx.swap(0, 1);
}
self.indices.reverse();
let leaves = self.segments().map(|seg| seg.local_aabb()).enumerate();
let bvh = Bvh::from_iter(BvhBuildStrategy::Binned, leaves);
self.bvh = bvh;
}
pub fn extract_connected_components(&self) -> Vec<Polyline> {
let vertices = self.vertices();
let indices = self.indices();
if indices.is_empty() {
Vec::new()
} else {
let mut components = Vec::new();
let mut start_i = 0; let mut start_node = indices[0][0];
let mut component_vertices = Vec::new();
let mut component_indices: Vec<[u32; 2]> = Vec::new();
for (i, idx) in indices.iter().enumerate() {
component_vertices.push(vertices[idx[0] as usize]);
if idx[1] != start_node {
component_indices.push([(i - start_i) as u32, (i - start_i + 1) as u32]);
} else {
component_indices.push([(i - start_i) as u32, 0]);
components.push(Polyline::new(
core::mem::take(&mut component_vertices),
Some(core::mem::take(&mut component_indices)),
));
if i + 1 < indices.len() {
start_node = indices[i + 1][0];
start_i = i + 1;
}
}
}
components
}
}
pub fn project_local_point_assuming_solid_interior_ccw(
&self,
point: Vector,
#[cfg(feature = "dim3")] axis: u8,
) -> (PointProjection, (u32, SegmentPointLocation)) {
let mut proj = self.project_local_point_and_get_location(point, false);
let segment1 = self.segment((proj.1).0);
#[cfg(feature = "dim2")]
let normal1 = segment1.normal();
#[cfg(feature = "dim3")]
let normal1 = segment1.planar_normal(axis);
if let Some(normal1) = normal1 {
proj.0.is_inside = match proj.1 .1 {
SegmentPointLocation::OnVertex(i) => {
let dir2 = if i == 0 {
let adj_seg = if proj.1 .0 == 0 {
self.indices().len() as u32 - 1
} else {
proj.1 .0 - 1
};
assert_eq!(segment1.a, self.segment(adj_seg).b);
-self.segment(adj_seg).scaled_direction()
} else {
assert_eq!(i, 1);
let adj_seg = (proj.1 .0 + 1) % self.indices().len() as u32;
assert_eq!(segment1.b, self.segment(adj_seg).a);
self.segment(adj_seg).scaled_direction()
};
let dot = normal1.dot(dir2);
let threshold = 1.0e-3 * dir2.length();
if dot.abs() > threshold {
dot >= 0.0
} else {
(point - proj.0.point).dot(normal1) <= 0.0
}
}
SegmentPointLocation::OnEdge(_) => (point - proj.0.point).dot(normal1) <= 0.0,
};
}
proj
}
}
impl CompositeShape for Polyline {
fn map_part_at(
&self,
i: u32,
f: &mut dyn FnMut(Option<&Pose>, &dyn Shape, Option<&dyn NormalConstraints>),
) {
let tri = self.segment(i);
f(None, &tri, None)
}
fn bvh(&self) -> &Bvh {
&self.bvh
}
}
impl TypedCompositeShape for Polyline {
type PartShape = Segment;
type PartNormalConstraints = ();
#[inline(always)]
fn map_typed_part_at<T>(
&self,
i: u32,
mut f: impl FnMut(Option<&Pose>, &Self::PartShape, Option<&Self::PartNormalConstraints>) -> T,
) -> Option<T> {
let seg = self.segment(i);
Some(f(None, &seg, None))
}
#[inline(always)]
fn map_untyped_part_at<T>(
&self,
i: u32,
mut f: impl FnMut(Option<&Pose>, &dyn Shape, Option<&dyn NormalConstraints>) -> T,
) -> Option<T> {
let seg = self.segment(i);
Some(f(None, &seg, None))
}
}