use std::collections::VecDeque;
use crate::primitive::Primitive;
use crate::segment::{ArcSegment, LineSegment, Segment, SegmentRef};
use crate::{DEFAULT_EPSILON, DEFAULT_MAX_ULPS};
use crate::{Transformation, composite::*};
use approx::ulps_eq;
use bounding_box::{BoundingBox, ToBoundingBox};
use rayon::prelude::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[doc = ""]
#[cfg_attr(
feature = "doc-images",
doc = "![Polysegment example][example_polysegment]"
)]
#[cfg_attr(
feature = "doc-images",
embed_doc_image::embed_doc_image("example_polysegment", "docs/img/example_polysegment.svg")
)]
#[cfg_attr(
not(feature = "doc-images"),
doc = "**Doc images not enabled**. Compile docs with `cargo doc --features 'doc-images'` and Rust version >= 1.54."
)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Polysegment(VecDeque<Segment>);
impl Polysegment {
pub fn new() -> Self {
return Self(VecDeque::new());
}
pub fn with_capacity(capacity: usize) -> Self {
return Self(VecDeque::with_capacity(capacity));
}
pub fn from_points(points: &[[f64; 2]]) -> Self {
let mut segments: VecDeque<Segment> = VecDeque::with_capacity(points.len());
for window in points.windows(2) {
let start = window[0];
let stop = window[1];
match segments.back() {
Some(last_segment) => {
if last_segment.stop() == stop {
continue;
}
}
None => (),
}
if let Ok(ls) = LineSegment::new(start, stop, DEFAULT_EPSILON, DEFAULT_MAX_ULPS) {
segments.push_back(ls.into());
}
}
return Self(segments);
}
pub fn close(&mut self) -> () {
if let Some(start) = self.0.front() {
if let Some(stop) = self.0.back() {
if let Ok(ls) = LineSegment::new(
stop.stop(),
start.start(),
DEFAULT_EPSILON,
DEFAULT_MAX_ULPS,
) {
self.0.push_back(ls.into())
}
}
}
}
pub fn vec_deque(&self) -> &VecDeque<Segment> {
return &self.0;
}
pub fn as_slices(&self) -> (&[Segment], &[Segment]) {
return self.0.as_slices();
}
pub fn make_contiguous(&mut self) -> &[Segment] {
return self.0.make_contiguous();
}
pub fn push_back(&mut self, segment: Segment) {
let opt_stop = self.back().map(Segment::stop);
if let Some(stop) = opt_stop {
if let Ok(ls) =
LineSegment::new(stop, segment.start(), DEFAULT_EPSILON, DEFAULT_MAX_ULPS)
{
self.0.push_back(ls.into());
}
}
self.0.push_back(segment);
}
pub fn push_front(&mut self, segment: Segment) {
let opt_start = self.front().map(Segment::start);
if let Some(start) = opt_start {
if let Ok(ls) =
LineSegment::new(segment.stop(), start, DEFAULT_EPSILON, DEFAULT_MAX_ULPS)
{
self.0.push_front(ls.into());
}
}
self.0.push_front(segment);
}
pub fn pop_back(&mut self) -> Option<Segment> {
return self.0.pop_back();
}
pub fn pop_front(&mut self) -> Option<Segment> {
return self.0.pop_front();
}
pub fn back(&self) -> Option<&Segment> {
return self.0.back();
}
pub fn front(&self) -> Option<&Segment> {
return self.0.front();
}
pub fn extend_back(&mut self, point: [f64; 2]) {
let endpoint = self.back().map(|s| s.stop());
if let Some(endpoint) = endpoint {
if let Ok(ls) = LineSegment::new(endpoint, point, DEFAULT_EPSILON, DEFAULT_MAX_ULPS) {
self.push_back(ls.into());
}
}
}
pub fn extend_front(&mut self, point: [f64; 2]) {
let endpoint = self.front().map(|s| s.start());
if let Some(endpoint) = endpoint {
if let Ok(ls) = LineSegment::new(point, endpoint, DEFAULT_EPSILON, DEFAULT_MAX_ULPS) {
self.push_front(ls.into());
}
}
}
pub fn is_empty(&self) -> bool {
return self.0.is_empty();
}
pub fn get(&self, index: usize) -> Option<&Segment> {
return self.0.get(index);
}
pub fn len(&self) -> usize {
return self.0.len();
}
pub fn segments(&self) -> std::collections::vec_deque::Iter<'_, Segment> {
return self.0.iter();
}
pub fn segments_par(&self) -> rayon::collections::vec_deque::Iter<'_, Segment> {
return self.0.par_iter();
}
pub fn append(&mut self, other: &mut Polysegment) -> () {
if let Some(s) = other.0.front() {
let start = s.start();
if let Some(s) = self.0.back() {
let stop = s.stop();
if let Ok(ls) = LineSegment::new(stop, start, DEFAULT_EPSILON, DEFAULT_MAX_ULPS) {
self.0.push_back(ls.into())
}
}
}
self.0.append(&mut other.0);
}
pub fn points(&self) -> PointIterator<'_> {
return PointIterator::new(self, false, Polygonizer::default());
}
pub fn polygonize(&self, polygonizer: Polygonizer) -> PointIterator<'_> {
return PointIterator::new(self, false, polygonizer);
}
pub fn length(&self) -> f64 {
return self.segments_par().map(|segment| segment.length()).sum();
}
pub fn intersection_cut(
&self,
other: &Polysegment,
epsilon: f64,
max_ulps: u32,
) -> Vec<Polysegment> {
fn create_cut_segment(
segment: &Segment,
start: [f64; 2],
stop: [f64; 2],
center: [f64; 2],
epsilon: f64,
max_ulps: u32,
) -> Option<Segment> {
if !ulps_eq!(start, stop, epsilon = epsilon, max_ulps = max_ulps) {
match segment {
Segment::LineSegment(_) => {
if let Ok(ls) = LineSegment::new(start, stop, epsilon, max_ulps) {
return Some(ls.into());
} else {
return None;
}
}
Segment::ArcSegment(_) => {
let normalized_stop = [stop[0] - center[0], stop[1] - center[1]];
let normalized_start = [start[0] - center[0], start[1] - center[1]];
let stop_angle = normalized_stop[1].atan2(normalized_stop[0]);
let start_angle = normalized_start[1].atan2(normalized_start[0]);
let offset_angle = stop_angle - start_angle;
if let Ok(arc) = ArcSegment::from_start_center_angle(
start,
center,
offset_angle,
epsilon,
max_ulps,
) {
return Some(arc.into());
} else {
return None;
}
}
};
} else {
return None;
}
}
let mut lines: Vec<Polysegment> = Vec::new();
let mut segments_of_current_line: Vec<Segment> = Vec::new();
let mut center: [f64; 2] = [0.0, 0.0];
let mut intersections: Vec<[f64; 2]> = Vec::new();
for segment_self in self.0.iter() {
match segment_self {
Segment::LineSegment(_) => (),
Segment::ArcSegment(arc) => {
center = arc.center();
}
}
intersections.clear();
for segment_other in other.0.iter() {
let intersections_segment_other =
segment_self.intersections_primitive(segment_other, epsilon, max_ulps);
intersections.extend(
intersections_segment_other
.into_iter()
.map::<[f64; 2], _>(From::from),
);
}
if intersections.is_empty() {
segments_of_current_line.push(segment_self.clone())
} else {
intersections.sort_unstable_by(|a, b| {
let start = segment_self.start();
let dist_a = (start[0] - a[0]).powi(2) + (start[1] - a[1]).powi(2);
let dist_b = (start[0] - b[0]).powi(2) + (start[1] - b[1]).powi(2);
dist_a
.partial_cmp(&dist_b)
.unwrap_or(std::cmp::Ordering::Equal)
});
for (idx, intersection) in intersections.iter().enumerate() {
if idx == 0 {
let start = segment_self.start();
let stop = *intersection;
if let Some(segment) = create_cut_segment(
&segment_self,
start,
stop,
center,
epsilon,
max_ulps,
) {
if segments_of_current_line.is_empty() {
lines.push(Polysegment::from(segment));
} else {
let mut polysegment =
Polysegment::with_capacity(segments_of_current_line.len());
for s in segments_of_current_line.into_iter() {
polysegment.push_back(s);
}
polysegment.push_back(segment);
lines.push(polysegment);
}
} else {
if !segments_of_current_line.is_empty() {
let mut polysegment =
Polysegment::with_capacity(segments_of_current_line.len());
for s in segments_of_current_line.into_iter() {
polysegment.push_back(s);
}
lines.push(polysegment);
}
}
segments_of_current_line = Vec::new();
} else {
let start = unsafe { *intersections.get_unchecked(idx - 1) };
let stop = *intersection;
if let Some(segment) = create_cut_segment(
&segment_self,
start,
stop,
center,
epsilon,
max_ulps,
) {
lines.push(Polysegment::from(segment));
}
};
}
if let Some(start) = intersections.last() {
if let Some(segment) = create_cut_segment(
&segment_self,
start.clone(),
segment_self.stop(),
center,
epsilon,
max_ulps,
) {
segments_of_current_line.push(segment);
}
}
};
}
let mut polysegment = Polysegment::with_capacity(segments_of_current_line.len());
for s in segments_of_current_line.into_iter() {
polysegment.push_back(s);
}
lines.push(polysegment);
return lines;
}
pub fn reverse(&mut self) -> () {
self.make_contiguous();
let (slice, _) = self.0.as_mut_slices();
for segment in slice.iter_mut() {
segment.reverse();
}
slice.reverse();
}
pub fn rotational_pattern(&mut self, center: [f64; 2], angle: f64, repetitions: usize) -> () {
let num_segments = self.num_segments();
for rep in 0..repetitions {
for seg_idx in 0..num_segments {
let mut segment = self[seg_idx].clone();
segment.rotate(center, angle * (rep as f64 + 1.0));
self.push_back(segment);
}
}
}
pub fn translational_pattern(&mut self, shift: [f64; 2], repetitions: usize) -> () {
let num_segments = self.num_segments();
for rep in 0..repetitions {
let f = rep as f64 + 1.0; for seg_idx in 0..num_segments {
let mut segment = self[seg_idx].clone();
segment.translate([shift[0] * f, shift[1] * f]);
self.push_back(segment);
}
}
}
}
impl FromIterator<Segment> for Polysegment {
fn from_iter<T: IntoIterator<Item = Segment>>(iter: T) -> Self {
let mut polysegment = Polysegment::new();
for segment in iter {
polysegment.push_back(segment);
}
return polysegment;
}
}
impl crate::composite::private::Sealed for Polysegment {}
impl Composite for Polysegment {
fn segment(&self, key: SegmentKey) -> Option<&crate::segment::Segment> {
return self.get(key.segment_idx);
}
fn num_segments(&self) -> usize {
return self.len();
}
fn centroid(&self) -> [f64; 2] {
return crate::CentroidData::from(self).into();
}
fn iter<'a>(&'a self) -> impl Iterator<Item = (SegmentKey, &'a crate::segment::Segment)> {
return self
.segments()
.enumerate()
.map(|(i, s)| (SegmentKey::from_segment_idx(i), s));
}
fn par_iter<'a>(
&'a self,
) -> impl ParallelIterator<Item = (SegmentKey, &'a crate::segment::Segment)> {
return self
.segments_par()
.enumerate()
.map(|(i, s)| (SegmentKey::from_segment_idx(i), s));
}
fn intersections_polysegment<'a>(
&'a self,
other: &'a Self,
epsilon: f64,
max_ulps: u32,
) -> impl Iterator<Item = Intersection> + 'a {
let same_polysegment_len = if std::ptr::eq(self, other) {
Some(self.num_segments())
} else {
None
};
return other
.0
.iter()
.enumerate()
.flat_map(move |(right_idx, right_seg)| {
intersections_between_polysegment_and_segment_priv(
self.0.iter(),
right_idx,
right_seg,
same_polysegment_len,
epsilon,
max_ulps,
)
});
}
fn intersections_polysegment_par<'a>(
&'a self,
other: &'a Self,
epsilon: f64,
max_ulps: u32,
) -> impl ParallelIterator<Item = Intersection> + 'a {
let same_polysegment_len = if std::ptr::eq(self, other) {
Some(self.num_segments())
} else {
None
};
return other
.0
.par_iter()
.enumerate()
.flat_map(move |(right_idx, right_seg)| {
intersections_between_polysegment_and_segment_priv_par(
self.0.par_iter(),
right_idx,
right_seg,
same_polysegment_len,
epsilon,
max_ulps,
)
});
}
fn intersections_shape<'a>(
&'a self,
shape: &'a crate::prelude::Shape,
epsilon: f64,
max_ulps: u32,
) -> impl Iterator<Item = Intersection> {
shape
.intersections_polysegment(self, epsilon, max_ulps)
.map(Intersection::switch)
}
fn intersections_shape_par<'a>(
&'a self,
shape: &'a crate::prelude::Shape,
epsilon: f64,
max_ulps: u32,
) -> impl ParallelIterator<Item = Intersection> {
shape
.intersections_polysegment_par(self, epsilon, max_ulps)
.map(Intersection::switch)
}
fn intersections_composite<'a, T: Composite>(
&'a self,
other: &'a T,
epsilon: f64,
max_ulps: u32,
) -> impl Iterator<Item = Intersection> + 'a
where
Self: Sized,
{
return other
.intersections_polysegment(self, epsilon, max_ulps)
.map(Intersection::switch);
}
fn intersections_composite_par<'a, T: Composite>(
&'a self,
other: &'a T,
epsilon: f64,
max_ulps: u32,
) -> impl ParallelIterator<Item = Intersection> + 'a
where
Self: Sized,
{
return other
.intersections_polysegment_par(self, epsilon, max_ulps)
.map(Intersection::switch);
}
fn covers_point(&self, point: [f64; 2], epsilon: f64, max_ulps: u32) -> bool {
return self
.intersections_primitive(&point, epsilon, max_ulps)
.next()
.is_some();
}
fn covers_segment<'a, T: Into<SegmentRef<'a>>>(
&self,
segment: T,
epsilon: f64,
max_ulps: u32,
) -> bool {
let segment: SegmentRef = segment.into();
return covers_segment(self.0.iter(), segment, epsilon, max_ulps);
}
fn contains_point(&self, _: [f64; 2], _: f64, _: u32) -> bool {
return false;
}
fn contains_segment<'a, T: Into<SegmentRef<'a>>>(&self, _: T, _: f64, _: u32) -> bool {
return false;
}
fn covers_composite<'a, T: Composite>(
&'a self,
other: &'a T,
epsilon: f64,
max_ulps: u32,
) -> bool {
return other.covers_polysegment(self, epsilon, max_ulps);
}
fn contains_composite<'a, T: Composite>(
&'a self,
other: &'a T,
epsilon: f64,
max_ulps: u32,
) -> bool {
return other.contains_polysegment(self, epsilon, max_ulps);
}
fn overlaps_segment<'a, T: Into<SegmentRef<'a>>>(
&self,
_segment: T,
_epsilon: f64,
_max_ulps: u32,
) -> bool {
return false;
}
fn overlaps_contour(
&self,
_contour: &crate::prelude::Contour,
_epsilon: f64,
_max_ulps: u32,
) -> bool {
return false;
}
fn overlaps_shape(
&self,
_shape: &crate::prelude::Shape,
_epsilon: f64,
_max_ulps: u32,
) -> bool {
return false;
}
fn overlaps_composite<'a, T: Composite>(
&'a self,
_other: &'a T,
_epsilon: f64,
_max_ulps: u32,
) -> bool {
return false;
}
}
impl std::ops::Index<usize> for Polysegment {
type Output = Segment;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
impl From<Segment> for Polysegment {
fn from(value: Segment) -> Self {
let mut polysegment = Polysegment::new();
polysegment.push_back(value);
return polysegment;
}
}
impl From<LineSegment> for Polysegment {
fn from(value: LineSegment) -> Self {
return Polysegment::from(Segment::LineSegment(value));
}
}
impl From<ArcSegment> for Polysegment {
fn from(value: ArcSegment) -> Self {
return Polysegment::from(Segment::ArcSegment(value));
}
}
impl crate::Transformation for Polysegment {
fn rotate(&mut self, center: [f64; 2], angle: f64) -> () {
self.0.par_iter_mut().for_each(|segment| {
segment.rotate(center, angle);
})
}
fn translate(&mut self, shift: [f64; 2]) -> () {
self.0.par_iter_mut().for_each(|segment| {
segment.translate(shift);
})
}
fn scale(&mut self, factor: f64) -> () {
self.0.par_iter_mut().for_each(|segment| {
segment.scale(factor);
})
}
fn line_reflection(&mut self, start: [f64; 2], stop: [f64; 2]) -> () {
self.0.par_iter_mut().for_each(|segment| {
segment.line_reflection(start, stop);
})
}
}
impl ToBoundingBox for Polysegment {
fn bounding_box(&self) -> BoundingBox {
return self
.segments_par()
.skip(1)
.map(|segment| BoundingBox::from(segment))
.reduce(
|| {
if let Some(s) = self.get(0) {
BoundingBox::from(s)
} else {
BoundingBox::new(0.0, 0.0, 0.0, 0.0)
}
},
|prev, curr| prev.union(&curr),
);
}
}
impl IntoIterator for Polysegment {
type Item = Segment;
type IntoIter = std::collections::vec_deque::IntoIter<Segment>;
fn into_iter(self) -> Self::IntoIter {
return self.0.into_iter();
}
}
impl From<Polysegment> for VecDeque<Segment> {
fn from(line: Polysegment) -> Self {
return line.0;
}
}
impl From<BoundingBox> for Polysegment {
fn from(bounding_box: BoundingBox) -> Self {
return Self::from(&bounding_box);
}
}
impl From<&BoundingBox> for Polysegment {
fn from(bounding_box: &BoundingBox) -> Self {
return Polysegment::from_points(&[
[bounding_box.xmin(), bounding_box.ymin()],
[bounding_box.xmax(), bounding_box.ymin()],
[bounding_box.xmax(), bounding_box.ymax()],
[bounding_box.xmin(), bounding_box.ymax()],
[bounding_box.xmin(), bounding_box.ymin()],
]);
}
}
impl From<&Polysegment> for crate::CentroidData {
fn from(value: &Polysegment) -> Self {
return value
.segments_par()
.map(|segment| match segment {
Segment::LineSegment(line) => Self::from(line),
Segment::ArcSegment(arc) => Self::from(arc),
})
.reduce(
|| Self {
area: 0.0,
x: 0.0,
y: 0.0,
},
|prev, curr| prev.union(&curr),
);
}
}
pub fn area_signed<'a, I>(mut points: I) -> f64
where
I: Iterator<Item = [f64; 2]>,
{
let first_vertex = match points.next() {
Some(vertex) => vertex,
None => return 0.0,
};
let mut previous_vertex = first_vertex.clone();
let mut area = 0.0;
for current_vertex in points.chain(std::iter::once(first_vertex)) {
area += (previous_vertex[1] + current_vertex[1]) * (previous_vertex[0] - current_vertex[0]);
previous_vertex = current_vertex.clone();
}
return 0.5 * area; }
pub(crate) fn covers_segment<'a, 'b, I>(
iterator: I,
segment: SegmentRef<'b>,
epsilon: f64,
max_ulps: u32,
) -> bool
where
I: Iterator<Item = &'a Segment>,
{
match segment {
SegmentRef::LineSegment(line_segment) => {
let mut combined: Option<LineSegment> = None;
for seg in iterator {
if let Segment::LineSegment(sl) = seg {
combined = match combined {
Some(c) => {
if ulps_eq!(
c.angle(),
sl.angle(),
epsilon = epsilon,
max_ulps = max_ulps
) {
if let Ok(new_combined) =
LineSegment::new(c.start(), sl.stop(), epsilon, max_ulps)
{
if new_combined.covers(line_segment, epsilon, max_ulps) {
return true;
}
Some(new_combined)
} else {
None
}
} else {
if sl.covers(line_segment, epsilon, max_ulps) {
return true;
}
Some(sl.clone())
}
}
None => {
if sl.covers(line_segment, epsilon, max_ulps) {
return true;
}
Some(sl.clone())
}
};
} else {
combined = None;
}
}
return false;
}
SegmentRef::ArcSegment(arc_segment) => {
let mut combined: Option<ArcSegment> = None;
for seg in iterator {
if let Segment::ArcSegment(sa) = seg {
combined = match combined {
Some(c) => {
if ulps_eq!(
c.center(),
sa.center(),
epsilon = epsilon,
max_ulps = max_ulps
) && ulps_eq!(
c.radius(),
sa.radius(),
epsilon = epsilon,
max_ulps = max_ulps
) {
if let Ok(new_combined) =
ArcSegment::from_center_radius_start_offset_angle(
c.center(),
c.radius(),
c.start_angle(),
c.offset_angle() + sa.offset_angle(),
epsilon,
max_ulps,
)
{
if new_combined.covers(arc_segment, epsilon, max_ulps) {
return true;
}
Some(new_combined)
} else {
None
}
} else {
if sa.covers(arc_segment, epsilon, max_ulps) {
return true;
}
Some(sa.clone())
}
}
None => {
if sa.covers(arc_segment, epsilon, max_ulps) {
return true;
}
Some(sa.clone())
}
};
} else {
combined = None;
}
}
return false;
}
}
}
fn intersections_between_polysegment_and_segment_priv<'a, L>(
left: L,
right_idx: usize,
right_seg: &'a Segment,
same_polysegment_len: Option<usize>,
epsilon: f64,
max_ulps: u32,
) -> impl Iterator<Item = Intersection> + 'a
where
L: Iterator<Item = &'a Segment> + 'a,
{
left.enumerate()
.filter_map(move |(left_idx, left_seg)| {
segment_intersections(
left_seg,
left_idx,
right_seg,
right_idx,
same_polysegment_len,
epsilon,
max_ulps,
)
})
.flatten()
}
fn intersections_between_polysegment_and_segment_priv_par<'a, L>(
left: L,
right_idx: usize,
right_seg: &'a Segment,
same_polysegment_len: Option<usize>,
epsilon: f64,
max_ulps: u32,
) -> impl ParallelIterator<Item = Intersection> + 'a
where
L: IndexedParallelIterator<Item = &'a Segment> + 'a,
{
left.enumerate()
.filter_map(move |(left_idx, left_seg)| {
segment_intersections(
left_seg,
left_idx,
right_seg,
right_idx,
same_polysegment_len,
epsilon,
max_ulps,
)
.map(|iter| iter.par_bridge())
})
.flatten()
}
fn segment_intersections<'a>(
left_seg: &'a Segment,
left_idx: usize,
right_seg: &'a Segment,
right_idx: usize,
same_polysegment_len: Option<usize>,
epsilon: f64,
max_ulps: u32,
) -> Option<impl Iterator<Item = Intersection> + 'a> {
if same_polysegment_len.is_some() {
if left_idx == right_idx {
return None;
}
if left_idx >= right_idx {
return None;
}
} else {
if left_seg == right_seg {
return None;
}
}
let intersection_iter = left_seg
.intersections_primitive(right_seg, epsilon, max_ulps)
.into_iter()
.filter_map(move |point| {
let point: [f64; 2] = point.into();
if let Some(len) = same_polysegment_len {
if left_idx + 1 == right_idx || (left_idx == 0 && right_idx == len - 1) {
if approx::ulps_eq!(
point,
left_seg.stop(),
epsilon = epsilon,
max_ulps = max_ulps
) {
return None;
}
}
}
Some(Intersection {
point,
left: SegmentKey::from_segment_idx(left_idx),
right: SegmentKey::from_segment_idx(right_idx),
})
});
return Some(intersection_iter);
}