1#![deny(
44 rust_2018_compatibility,
45 rust_2018_idioms,
46 nonstandard_style,
47 unused,
48 future_incompatible,
49 non_camel_case_types,
50 unused_parens,
51 non_upper_case_globals,
52 unused_qualifications,
53 unused_results,
54 unused_imports,
55 unused_variables
56)]
57
58use boostvoronoi::prelude as BV;
59use linestring::LinestringError;
60use linestring::linestring_2d::{self, Line2, convex_hull};
61use linestring::linestring_3d::Line3;
62use linestring::prelude::{LineString2, LineString3};
63use ordered_float::OrderedFloat;
64use rayon::iter::{IntoParallelIterator, ParallelIterator};
65use rustc_hash::{FxHashMap, FxHashSet};
66use std::collections::VecDeque;
67use std::fmt::Debug;
68use std::line;
69use thiserror::Error;
70use vector_traits::approx::{AbsDiffEq, UlpsEq, ulps_eq};
71use vector_traits::num_traits::AsPrimitive;
72use vector_traits::num_traits::{self, real::Real};
73use vector_traits::prelude::GenericVector3;
74use vector_traits::prelude::*;
75
76#[macro_use]
77extern crate bitflags;
78
79#[derive(Error, Debug)]
80pub enum CenterlineError {
81 #[error("Something is wrong with the internal logic {0}")]
82 InternalError(String),
83
84 #[error("Something is wrong with the input data {0}")]
85 CouldNotCalculateInverseMatrix(String),
86
87 #[error("Your line-strings are self-intersecting {0}")]
88 SelfIntersectingData(String),
89
90 #[error("The input data is not 2D {0}")]
91 InputNotPLane(String),
92
93 #[error("Invalid data {0}")]
94 InvalidData(String),
95
96 #[error(transparent)]
97 BvError(#[from] BV::BvError),
98
99 #[error("Error from .obj file handling {0}")]
100 ObjError(String),
101
102 #[error(transparent)]
103 IoError(#[from] std::io::Error),
104
105 #[error(transparent)]
106 LinestringError(#[from] LinestringError),
107}
108
109bitflags! {
110 pub struct ColorFlag: BV::ColorType {
112 const EXTERNAL = 0b00000001;
114 const SECONDARY = 0b00000010;
116 const INFINITE = 0b00000100;
118 const DOTLIMIT = 0b00001000;
120 }
121}
122
123type Vob32 = vob::Vob<u32>;
124
125#[doc(hidden)]
126pub trait GrowingVob {
127 fn fill(initial_size: u32) -> Self;
129 fn set_grow(&mut self, bit: usize, state: bool);
131 fn get_f(&self, bit: usize) -> bool;
133}
134
135impl<T: num_traits::PrimInt + Debug> GrowingVob for vob::Vob<T> {
136 #[inline]
137 fn fill(initial_size: u32) -> Self {
138 let mut v = Self::new_with_storage_type(0);
139 v.resize(initial_size as usize, false);
140 v
141 }
142
143 #[inline]
144 fn set_grow(&mut self, bit: usize, state: bool) {
145 if bit >= self.len() {
146 self.resize(bit, false);
147 }
148 let _ = self.set(bit, state);
149 }
150
151 #[inline]
152 fn get_f(&self, bit: usize) -> bool {
153 self.get(bit).unwrap_or(false)
154 }
155}
156
157#[derive(Debug)]
158struct Vertices {
159 id: u32, connected_vertices: Vec<u32>, shape: Option<u32>, }
163
164fn paint_every_connected_vertex(
166 vertices: &mut FxHashMap<u32, Vertices>,
167 already_painted: &mut Vob32,
168 vertex_id: u32,
169 color: u32,
170) -> Result<(), CenterlineError> {
171 let mut queue = VecDeque::<u32>::new();
172 queue.push_back(vertex_id);
173
174 while !queue.is_empty() {
175 let current_vertex = queue.pop_front().unwrap();
177 if already_painted.get_f(current_vertex as usize) {
178 continue;
179 }
180
181 if let Some(vertex_obj) = vertices.get_mut(¤t_vertex) {
182 if vertex_obj.shape.is_none() {
183 vertex_obj.shape = Some(color);
184 let _ = already_painted.set(current_vertex as usize, true);
185 } else {
186 continue;
188 }
189 for &v in vertex_obj.connected_vertices.iter() {
190 if !already_painted.get_f(v as usize) {
191 queue.push_back(v);
192 }
193 }
194 } else {
195 return Err(CenterlineError::InvalidData(format!(
196 "Vertex with id {} is not part of any geometry. Do you have disjoint vertices in your mesh? {}:{}",
197 current_vertex,
198 file!(),
199 line!()
200 )));
201 };
202 }
203 Ok(())
204}
205
206#[cfg(feature = "obj-rs")]
207#[allow(clippy::type_complexity)]
208pub fn remove_internal_edges<T: GenericVector3>(
211 obj: obj::raw::RawObj,
212) -> Result<(FxHashSet<(u32, u32)>, Vec<T>), CenterlineError>
213where
214 f32: AsPrimitive<<T as HasXY>::Scalar>,
215{
216 for p in obj.points.iter() {
217 println!("Ignored point:{p:?}");
219 }
220 let mut all_edges = FxHashSet::<(u32, u32)>::default();
221 let mut internal_edges = FxHashSet::<(u32, u32)>::default();
222
223 for i in 0..obj.lines.len() {
224 let v: Vec<u32> = match &obj.lines[i] {
228 obj::raw::object::Line::P(a) => a.iter().map(|i| *i as u32).collect(),
229 obj::raw::object::Line::PT(a) => a.iter().map(|(a, _)| *a as u32).collect(),
230 };
231 let mut i1 = v.iter();
233
234 for i in v.iter().skip(1) {
235 let i1_v = *i1.next().unwrap();
236 let i2_v = *i;
237 let key = (*std::cmp::min(&i1_v, &i2_v), *std::cmp::max(&i1_v, &i2_v));
238 if all_edges.contains(&key) {
239 let _ = internal_edges.insert(key);
240 } else {
241 let _ = all_edges.insert(key);
242 }
243 }
244 }
245 for i in 0..obj.polygons.len() {
249 let v = match &obj.polygons[i] {
251 obj::raw::object::Polygon::P(a) => {
252 let mut v = a.clone();
254 v.push(a[0]);
255 v
256 }
257 obj::raw::object::Polygon::PT(a) => {
258 let mut v = a.iter().map(|x| x.0).collect::<Vec<usize>>();
260 v.push(a[0].0);
261 v
262 }
263 obj::raw::object::Polygon::PN(a) => {
264 let mut v = a.iter().map(|x| x.0).collect::<Vec<usize>>();
266 v.push(a[0].0);
267 v
268 }
269 obj::raw::object::Polygon::PTN(a) => {
270 let mut v = a.iter().map(|x| x.0).collect::<Vec<usize>>();
272 v.push(a[0].0);
273 v
274 }
275 };
276
277 let mut i1 = v.iter();
278 for i in v.iter().skip(1) {
279 let i1_v = *i1.next().unwrap();
280 let i2_v = *i;
281 let key = (
282 *std::cmp::min(&i1_v, &i2_v) as u32,
283 *std::cmp::max(&i1_v, &i2_v) as u32,
284 );
285 if all_edges.contains(&key) {
286 let _ = internal_edges.insert(key);
287 } else {
288 let _ = all_edges.insert(key);
289 }
290 }
291 }
292 all_edges.retain(|x| !internal_edges.contains(x));
297
298 let vertices: Vec<T> = obj
300 .positions
301 .into_iter()
302 .map(|x| T::new_3d(x.0.as_(), x.1.as_(), x.2.as_()))
303 .collect();
304
305 Ok((all_edges, vertices))
306}
307
308pub fn divide_into_shapes<T: GenericVector3>(
310 edge_set: FxHashSet<(u32, u32)>,
311 points: Vec<T>,
312) -> Result<Vec<LineStringSet3<T>>, CenterlineError> {
313 let mut vertices = FxHashMap::<u32, Vertices>::default();
317 for (a, b) in edge_set.iter() {
318 debug_assert!(*a < points.len() as u32);
319 debug_assert!(*b < points.len() as u32);
320
321 vertices
322 .entry(*a)
323 .or_insert_with_key(|key| Vertices {
324 id: *key,
325 connected_vertices: Vec::<u32>::new(),
326 shape: None,
327 })
328 .connected_vertices
329 .push(*b);
330
331 vertices
332 .entry(*b)
333 .or_insert_with_key(|key| Vertices {
334 id: *key,
335 connected_vertices: Vec::<u32>::new(),
336 shape: None,
337 })
338 .connected_vertices
339 .push(*a);
340 }
341 let mut unique_shape_id_generator = 0..u32::MAX;
344
345 let mut already_painted = Vob32::fill(points.len() as u32);
346 for vertex_id in 0..vertices.len() as u32 {
347 if already_painted.get_f(vertex_id as usize) {
348 continue;
349 }
350 paint_every_connected_vertex(
352 &mut vertices,
353 &mut already_painted,
354 vertex_id,
355 unique_shape_id_generator.next().unwrap(),
356 )?;
357 }
358 let highest_shape_id_plus_one = unique_shape_id_generator.next().unwrap();
359 if highest_shape_id_plus_one == 0 {
360 return Err(CenterlineError::InternalError(format!(
361 "Could not find any shapes to separate. {}:{}",
362 file!(),
363 line!()
364 )));
365 }
366
367 let mut shape_separation = Vec::<FxHashMap<u32, Vertices>>::new();
370 for current_shape in 0..highest_shape_id_plus_one {
371 if vertices.is_empty() {
372 println!("vertices:{vertices:?}");
373 println!("current_shape:{current_shape}");
374 println!("shape_separation:{shape_separation:?}");
375
376 return Err(CenterlineError::InternalError(format!(
377 "Could not separate all shapes, ran out of vertices. {}:{}",
378 file!(),
379 line!()
380 )));
381 }
382 {
397 let mut drained = FxHashMap::<u32, Vertices>::default();
399 let mut new_vertices = FxHashMap::<u32, Vertices>::default();
400 for (x0, x1) in vertices.into_iter() {
401 if x1.shape == Some(current_shape) {
402 let _ = drained.insert(x0, x1);
403 } else {
404 let _ = new_vertices.insert(x0, x1);
405 };
406 }
407 vertices = new_vertices;
408 shape_separation.push(drained);
409 }
410 }
411 drop(vertices);
412 let shape_separation = shape_separation;
415
416 shape_separation
418 .into_par_iter()
419 .map(|rvi| -> Result<LineStringSet3<T>, CenterlineError> {
420 if rvi.is_empty() {
421 return Err(CenterlineError::InternalError(
422 format!("rvi.is_empty() Seems like the shape separation failed. {}:{}", file!(),line!()),
423 ));
424 }
425 let mut loops = 0_usize;
426
427 let mut rvs = LineStringSet3::<T>::with_capacity(rvi.len());
428 let mut als = Vec::<T>::with_capacity(rvi.len());
429
430 let started_with: u32 = rvi.iter().next().unwrap().1.id;
431 let mut prev: u32;
432 let mut current: u32 = started_with;
433 let mut next: u32 = started_with;
434 let mut first_loop = true;
435
436 loop {
437 prev = current;
438 current = next;
439 if let Some(current_vertex) = rvi.get(¤t) {
440 als.push(points[current as usize]);
441
442 next = *current_vertex.connected_vertices.iter().find(|x| **x != prev).ok_or_else(||{
444 println!("current_vertex.connected_vertices {:?}", current_vertex.connected_vertices);
445 CenterlineError::InvalidData(
446 "Could not find next vertex. All lines must form connected loops (loops connected to other loops are not supported,yet)".to_string(),
447 )},
448 )?;
449 } else {
450 break;
451 }
452 if !first_loop && current == started_with {
454 break;
455 }
456 first_loop = false;
457 loops += 1;
458 if loops > rvi.len() + 1 {
459 return Err(CenterlineError::InvalidData(
460 "It seems like one (or more) of the line strings does not form a connected loop.(loops connected to other loops are not supported,yet)"
461 .to_string(),
462 ));
463 }
464 }
465 if als.last() != als.first() {
466 println!(
467 "Linestring is not connected ! {:?} {:?}",
468 als.first(),
469 als.last()
470 );
471 println!("Linestring is not connected ! {als:?}");
472 }
473 rvs.push(als);
474 Ok(rvs)
475 })
476 .collect()
477}
478
479#[allow(clippy::type_complexity)]
480#[inline(always)]
481pub fn get_transform<T: GenericVector3>(
487 total_aabb: <T as GenericVector3>::Aabb,
488 desired_voronoi_dimension: T::Scalar,
489) -> Result<(Plane, T::Affine, <T::Vector2 as GenericVector2>::Aabb), CenterlineError>
490where
491 T::Scalar: ordered_float::FloatCore,
492{
493 get_transform_relaxed::<T>(
494 total_aabb,
495 desired_voronoi_dimension,
496 T::Scalar::default_epsilon(),
497 T::Scalar::default_max_ulps(),
498 )
499}
500
501#[allow(clippy::type_complexity)]
502pub fn get_transform_relaxed<T: GenericVector3>(
508 total_aabb: <T as GenericVector3>::Aabb,
509 desired_voronoi_dimension: T::Scalar,
510 epsilon: T::Scalar,
511 max_ulps: u32,
512) -> Result<(Plane, T::Affine, <T::Vector2 as GenericVector2>::Aabb), CenterlineError>
513where
514 T::Scalar: ordered_float::FloatCore,
515{
516 if total_aabb.is_empty() {
517 return Err(CenterlineError::InvalidData("Aabb was empty".to_string()));
518 }
519
520 let plane = if let Some(plane) = total_aabb.get_plane_relaxed(epsilon, max_ulps) {
521 plane
522 } else {
523 return Err(CenterlineError::InputNotPLane(format!("{total_aabb:?}")));
524 };
525 let center = total_aabb.center();
526 let (_min, _max, delta) = total_aabb.extents();
527 #[cfg(feature = "console_debug")]
528 {
529 println!("get_transform_relaxed desired_voronoi_dimension:{desired_voronoi_dimension:?}");
530 println!(
531 "Input data AABB: Center:({:?}, {:?}, {:?})",
532 center.x(),
533 center.y(),
534 center.z(),
535 );
536 println!(
537 " high:({:?}, {:?}, {:?})",
538 _max.x(),
539 _max.y(),
540 _max.z(),
541 );
542 println!(
543 " low:({:?}, {:?}, {:?})",
544 _min.x(),
545 _min.y(),
546 _min.z(),
547 );
548 println!(
549 " delta:({:?}, {:?}, {:?})",
550 delta.x(),
551 delta.y(),
552 delta.z(),
553 );
554 }
555 let total_transform: T::Affine = {
556 let scale: T::Scalar = desired_voronoi_dimension
557 / std::cmp::max(
558 std::cmp::max(OrderedFloat(delta.x()), OrderedFloat(delta.y())),
559 OrderedFloat(delta.z()),
560 )
561 .into_inner();
562 let plane_transform = T::Affine::from_plane_to_xy(plane);
563 let center_transform = T::Affine::from_translation(-center);
564 let scale_transform = T::Affine::from_scale(T::splat(scale));
565 scale_transform * center_transform * plane_transform
566 };
567
568 let voronoi_input_aabb = <<T as GenericVector3>::Vector2 as GenericVector2>::Aabb::from_corners(
570 total_transform.transform_point3(total_aabb.min()).to_2d(),
571 total_transform.transform_point3(total_aabb.max()).to_2d(),
572 );
573
574 #[cfg(feature = "console_debug")]
575 {
576 let (t_low0, t_high0, t_delta0) = voronoi_input_aabb.extents();
577 let t_center0 = voronoi_input_aabb.center();
578 println!(
579 "Voronoi input AABB: Center:({:?}, {:?})",
580 t_center0.x(),
581 t_center0.y(),
582 );
583 println!(
584 " high:({:?}, {:?})",
585 t_high0.x(),
586 t_high0.y(),
587 );
588 println!(
589 " low:({:?}, {:?})",
590 t_low0.x(),
591 t_low0.y(),
592 );
593 println!(
594 " delta:({:?}, {:?})",
595 t_delta0.x(),
596 t_delta0.y(),
597 );
598 }
599
600 Ok((plane, total_transform, voronoi_input_aabb))
601}
602
603pub fn consolidate_shapes<T: GenericVector2>(
606 mut raw_data: Vec<LineStringSet2<T>>,
607) -> Result<Vec<LineStringSet2<T>>, CenterlineError>
608where
609 T::Scalar: UlpsEq,
610{
611 'outer_loop: loop {
615 for i in 0..raw_data.len() {
617 for j in i + 1..raw_data.len() {
618 if raw_data[i]
620 .get_aabb()
621 .contains_aabb_inclusive(&raw_data[j].get_aabb())
622 && convex_hull::contains_convex_hull(
623 raw_data[i].get_convex_hull().as_ref().unwrap(),
624 raw_data[j].get_convex_hull().as_ref().unwrap(),
625 )
626 {
627 let mut stolen_line_j =
630 LineStringSet2::steal_from(raw_data.get_mut(j).unwrap());
631 let line_i = raw_data.get_mut(i).unwrap();
632 line_i.take_from_internal(&mut stolen_line_j)?;
633 let _ = raw_data.remove(j);
634 continue 'outer_loop;
635 } else if raw_data[j]
636 .get_aabb()
637 .contains_aabb_inclusive(&raw_data[i].get_aabb())
638 && convex_hull::contains_convex_hull(
639 raw_data[j].get_convex_hull().as_ref().unwrap(),
640 raw_data[i].get_convex_hull().as_ref().unwrap(),
641 )
642 {
643 let mut stolen_line_i =
646 LineStringSet2::steal_from(raw_data.get_mut(i).unwrap());
647 let line_j = raw_data.get_mut(j).unwrap();
648 line_j.take_from_internal(&mut stolen_line_i)?;
649 let _ = raw_data.remove(i);
650 continue 'outer_loop;
651 }
652 }
653 }
654 break 'outer_loop;
655 }
656 Ok(raw_data)
657}
658
659pub struct Centerline<I: BV::InputType, T>
665where
666 T: GenericVector3,
667{
668 pub segments: Vec<BV::Line<I>>,
670 pub diagram: BV::Diagram,
672 pub lines: Option<Vec<Line3<T>>>,
674 pub line_strings: Option<Vec<Vec<T>>>,
676
677 rejected_edges: Option<Vob32>,
679 ignored_edges: Option<Vob32>,
681
682 #[cfg(feature = "console_debug")]
683 pub debug_edges: Option<FxHashMap<usize, [T::Scalar; 4]>>,
684}
685
686impl<I: BV::InputType, T3: GenericVector3> Default for Centerline<I, T3> {
687 fn default() -> Self {
689 Self {
690 diagram: BV::Diagram::default(),
691 segments: Vec::<BV::Line<I>>::default(),
692 lines: Some(Vec::<Line3<T3>>::new()),
693 line_strings: Some(Vec::<Vec<T3>>::new()),
694 rejected_edges: None,
695 ignored_edges: None,
696 #[cfg(feature = "console_debug")]
697 debug_edges: None,
698 }
699 }
700}
701
702impl<I: BV::InputType, T3: GenericVector3> Centerline<I, T3>
703where
704 I: AsPrimitive<T3::Scalar>,
705 f64: AsPrimitive<T3::Scalar>,
706{
707 pub fn with_segments(segments: Vec<BV::Line<I>>) -> Self {
709 Self {
710 diagram: BV::Diagram::default(),
711 segments,
712 lines: Some(Vec::<Line3<T3>>::new()),
713 line_strings: Some(Vec::<Vec<T3>>::new()),
714 rejected_edges: None,
715 ignored_edges: None,
716 #[cfg(feature = "console_debug")]
717 debug_edges: None,
718 }
719 }
720
721 pub fn build_voronoi(&mut self) -> Result<(), CenterlineError> {
723 self.diagram = {
724 #[cfg(feature = "console_debug")]
725 {
726 print!("build_voronoi()-> input segments:[");
727 for s in self.segments.iter() {
728 print!("[{},{},{},{}],", s.start.x, s.start.y, s.end.x, s.end.y);
729 }
730 println!("];");
731 }
732 BV::Builder::default()
733 .with_segments(self.segments.iter())?
734 .build()?
735 };
736 self.reject_external_edges()?;
737 #[cfg(feature = "console_debug")]
738 println!(
739 "build_voronoi()-> Rejected edges:{:?} {}",
740 self.rejected_edges.as_ref(),
741 &self.rejected_edges.as_ref().unwrap().get_f(0)
742 );
743 Ok(())
744 }
745
746 #[allow(clippy::type_complexity)]
749 pub fn calculate_centerline(
750 &mut self,
751 cos_angle: T3::Scalar,
752 discrete_limit: T3::Scalar,
753 ignored_regions: Option<
754 &Vec<(
755 <<T3 as GenericVector3>::Vector2 as GenericVector2>::Aabb,
756 Vec<<T3 as GenericVector3>::Vector2>,
757 )>,
758 >,
759 ) -> Result<(), CenterlineError> {
760 self.angle_test(cos_angle)?;
761 if let Some(ignored_regions) = ignored_regions {
762 self.traverse_edges(discrete_limit, ignored_regions)?;
763 } else {
764 let ignored_regions = Vec::<(
765 <<T3 as GenericVector3>::Vector2 as GenericVector2>::Aabb,
766 Vec<T3::Vector2>,
767 )>::with_capacity(0);
768 self.traverse_edges(discrete_limit, &ignored_regions)?;
769 }
770 Ok(())
771 }
772
773 #[allow(clippy::type_complexity)]
778 pub fn calculate_centerline_mesh(
779 &mut self,
780 discrete_limit: T3::Scalar,
781 ignored_regions: Option<
782 &Vec<(
783 <<T3 as GenericVector3>::Vector2 as GenericVector2>::Aabb,
784 Vec<T3::Vector2>,
785 )>,
786 >,
787 ) -> Result<(), CenterlineError> {
788 self.ignored_edges = self.rejected_edges.clone();
789
790 if let Some(ignored_regions) = ignored_regions {
791 self.traverse_cells(discrete_limit, ignored_regions)?;
792 } else {
793 let ignored_regions = Vec::<(
794 <<T3 as GenericVector3>::Vector2 as GenericVector2>::Aabb,
795 Vec<T3::Vector2>,
796 )>::with_capacity(0);
797 self.traverse_cells(discrete_limit, &ignored_regions)?;
798 }
799 Ok(())
800 }
801
802 pub fn ignored_edges(&self) -> Option<Vob32> {
804 self.ignored_edges.to_owned()
805 }
806
807 pub fn rejected_edges(&self) -> Option<Vob32> {
809 self.rejected_edges.to_owned()
810 }
811
812 pub fn retrieve_point(&self, cell_id: BV::CellIndex) -> Result<BV::Point<I>, CenterlineError> {
813 let (index, category) = self.diagram.cell(cell_id)?.source_index_2();
814 let idx = index.usize();
815 match category {
816 BV::SourceCategory::SinglePoint => panic!("No points in the input data"),
817 BV::SourceCategory::SegmentStart => Ok(self.segments[idx].start),
818 BV::SourceCategory::Segment | BV::SourceCategory::SegmentEnd => {
819 Ok(self.segments[idx].end)
820 }
821 }
822 }
823
824 pub fn retrieve_segment(&self, cell_id: BV::CellIndex) -> Result<BV::Line<I>, CenterlineError> {
825 Ok(self.segments[self.diagram.cell(cell_id)?.source_index().usize()])
826 }
827
828 pub fn diagram(&self) -> &BV::Diagram {
830 &self.diagram
831 }
832
833 fn reject_external_edges(&mut self) -> Result<(), CenterlineError> {
835 let mut rejected_edges = Vob32::fill(self.diagram.edges().len() as u32);
836
837 for edge in self.diagram.edges().iter() {
838 let edge_id = edge.id();
839 if edge.is_secondary() {
840 let _ = rejected_edges.set(edge_id.usize(), true);
841 let twin_id = self.diagram.edge_get_twin(edge_id)?;
844 let _ = rejected_edges.set(twin_id.usize(), true);
847 }
848 if !self.diagram.edge_is_finite(edge_id)? {
849 self.mark_connected_edges(edge_id, &mut rejected_edges, true)?;
850 let _ = rejected_edges.set(edge_id.usize(), true);
851 }
852 }
853
854 self.rejected_edges = Some(rejected_edges);
855 Ok(())
856 }
857
858 fn angle_test(&mut self, cos_angle: T3::Scalar) -> Result<(), CenterlineError> {
867 let mut ignored_edges = self.rejected_edges.clone().unwrap();
868
869 for cell in self.diagram.cells().iter() {
870 let cell_id = cell.id();
871
872 if !cell.contains_segment() {
873 continue;
874 }
875 let segment = self.retrieve_segment(cell_id)?;
876 let point0 = T3::Vector2::new_2d(segment.start.x.as_(), segment.start.y.as_());
877 let point1 = T3::Vector2::new_2d(segment.end.x.as_(), segment.end.y.as_());
878
879 if let Some(incident_e) = cell.get_incident_edge() {
880 let mut e = incident_e;
882 loop {
883 e = self.diagram.edge_get_next(e)?;
884
885 if !ignored_edges.get_f(e.usize()) {
886 if let Some(Ok(vertex0)) = self
889 .diagram
890 .edge_get_vertex0(e)?
891 .map(|x| self.diagram.vertex(x))
892 {
893 let vertex0 = T3::Vector2::new_2d(vertex0.x().as_(), vertex0.y().as_());
894 if let Some(Ok(vertex1)) = self
895 .diagram
896 .edge_get_vertex1(e)?
897 .map(|x| self.diagram.vertex(x))
898 {
899 let vertex1 =
900 T3::Vector2::new_2d(vertex1.x().as_(), vertex1.y().as_());
901 let _ = self.angle_test_6(
902 cos_angle,
903 &mut ignored_edges,
904 e,
905 vertex0,
906 vertex1,
907 point0,
908 point1,
909 )? || self.angle_test_6(
910 cos_angle,
911 &mut ignored_edges,
912 e,
913 vertex0,
914 vertex1,
915 point1,
916 point0,
917 )? || self.angle_test_6(
918 cos_angle,
919 &mut ignored_edges,
920 e,
921 vertex1,
922 vertex0,
923 point0,
924 point1,
925 )? || self.angle_test_6(
926 cos_angle,
927 &mut ignored_edges,
928 e,
929 vertex1,
930 vertex0,
931 point1,
932 point0,
933 )?;
934 }
935 }
936 }
937
938 if e == incident_e {
939 break;
940 }
941 }
942 }
943 }
944 self.ignored_edges = Some(ignored_edges);
945 Ok(())
946 }
947
948 #[allow(clippy::too_many_arguments)]
950 fn angle_test_6(
951 &self,
952 cos_angle: T3::Scalar,
953 ignored_edges: &mut Vob32,
954 edge_id: BV::EdgeIndex,
955 vertex0: T3::Vector2,
956 vertex1: T3::Vector2,
957 s_point_0: T3::Vector2,
958 s_point_1: T3::Vector2,
959 ) -> Result<bool, CenterlineError> {
960 if ulps_eq!(vertex0.x(), s_point_0.x()) && ulps_eq!(vertex0.y(), s_point_0.y()) {
961 let segment_v = (s_point_1 - s_point_0).normalize();
963 let vertex_v = (vertex1 - vertex0).normalize();
964 if segment_v.dot(vertex_v).abs() < cos_angle {
965 let twin = self.diagram.edge_get_twin(edge_id)?;
966 let _ = ignored_edges.set(twin.usize(), true);
967 let _ = ignored_edges.set(edge_id.usize(), true);
968 return Ok(true);
969 }
970 }
971 Ok(false)
972 }
973
974 fn mark_connected_edges(
979 &self,
980 edge_id: BV::EdgeIndex,
981 marked_edges: &mut Vob32,
982 initial: bool,
983 ) -> Result<(), CenterlineError> {
984 if marked_edges.get_f(edge_id.usize()) {
985 return Ok(());
986 }
987
988 let mut initial = initial;
989 let mut queue = VecDeque::<BV::EdgeIndex>::new();
990 queue.push_back(edge_id);
991
992 'outer: while !queue.is_empty() {
993 let edge_id = queue.pop_front().unwrap();
995 if marked_edges.get_f(edge_id.usize()) {
996 initial = false;
997 continue 'outer;
998 }
999
1000 let v1 = self.diagram.edge_get_vertex1(edge_id)?;
1001 if self.diagram.edge_get_vertex0(edge_id)?.is_some() && v1.is_none() {
1002 let _ = marked_edges.set(edge_id.usize(), true);
1004 initial = false;
1005 continue 'outer;
1006 }
1007 let _ = marked_edges.set(edge_id.usize(), true);
1008
1009 #[allow(unused_assignments)]
1010 if initial {
1011 initial = false;
1012 queue.push_back(self.diagram.edge_get_twin(edge_id)?);
1013 } else {
1014 let _ = marked_edges.set(self.diagram.edge_get_twin(edge_id)?.usize(), true);
1015 }
1016
1017 if v1.is_none() || !self.diagram.edge(edge_id)?.is_primary() {
1018 initial = false;
1020 continue 'outer;
1021 }
1022 if let Some(v1) = v1 {
1024 let v1 = self.diagram.vertex(v1)?;
1025 if v1.is_site_point() {
1026 initial = false;
1028 continue 'outer;
1029 }
1030 let mut this_edge = v1.get_incident_edge()?;
1032 let v_incident_edge = this_edge;
1033 loop {
1034 if !marked_edges.get_f(this_edge.usize()) {
1035 queue.push_back(this_edge);
1036 }
1037 this_edge = self.diagram.edge_rot_next(this_edge).ok_or_else(|| {
1038 CenterlineError::InternalError(format!("Edge disconnected {this_edge:?}"))
1039 })?;
1040 if this_edge == v_incident_edge {
1041 break;
1042 }
1043 }
1044 }
1045 initial = false;
1046 }
1047 Ok(())
1048 }
1049
1050 #[allow(clippy::type_complexity)]
1052 fn edges_are_inside_ignored_region(
1053 &self,
1054 edges: &Vob32,
1055 ignored_regions: &[(
1056 <<T3 as GenericVector3>::Vector2 as GenericVector2>::Aabb,
1057 Vec<T3::Vector2>,
1058 )],
1059 ) -> Result<bool, CenterlineError> {
1060 let is_inside_region = |edge: BV::EdgeIndex,
1061 region: &(
1062 <<T3 as GenericVector3>::Vector2 as GenericVector2>::Aabb,
1063 Vec<T3::Vector2>,
1064 )|
1065 -> Result<bool, CenterlineError> {
1066 let v0 = self.diagram.edge_get_vertex0(edge)?.unwrap();
1067 let v0 = self.diagram.vertex(v0).unwrap();
1068 let v0 = T3::Vector2::new_2d(v0.x().as_(), v0.y().as_());
1069
1070 let v1 = self.diagram.edge_get_vertex0(edge)?.unwrap();
1071 let v1 = self.diagram.vertex(v1).unwrap();
1072 let v1 = T3::Vector2::new_2d(v1.x().as_(), v1.y().as_());
1073 Ok(region.0.contains_point_inclusive(v0)
1074 && region.0.contains_point_inclusive(v1)
1075 && convex_hull::contains_point_inclusive(®ion.1, v0)
1076 && convex_hull::contains_point_inclusive(®ion.1, v1))
1077 };
1078
1079 'outer: for region in ignored_regions.iter().enumerate() {
1080 for edge in edges.iter_set_bits(..) {
1081 if !is_inside_region(self.diagram().edge_index_unchecked(edge), region.1)? {
1082 continue 'outer;
1084 }
1085 }
1086 return Ok(true);
1088 }
1089 Ok(false)
1090 }
1091
1092 #[allow(clippy::type_complexity)]
1094 fn traverse_edges(
1095 &mut self,
1096 maxdist: T3::Scalar,
1097 ignored_regions: &[(
1098 <<T3 as GenericVector3>::Vector2 as GenericVector2>::Aabb,
1099 Vec<T3::Vector2>,
1100 )],
1101 ) -> Result<(), CenterlineError> {
1102 let mut lines = self.lines.take().ok_or_else(|| {
1104 CenterlineError::InternalError(format!(
1105 "traverse_edges(): could not take lines. {}:{}",
1106 file!(),
1107 line!()
1108 ))
1109 })?;
1110 let mut linestrings = self.line_strings.take().ok_or_else(|| {
1111 CenterlineError::InternalError(format!(
1112 "traverse_edges(): could not take linestrings. {}:{}",
1113 file!(),
1114 line!()
1115 ))
1116 })?;
1117
1118 let mut ignored_edges = self
1119 .ignored_edges
1120 .take()
1121 .unwrap_or_else(|| Vob32::fill(self.diagram.edges().len() as u32));
1122
1123 #[cfg(feature = "console_debug")]
1124 let edge_lines = FxHashMap::<usize, [T3::Scalar; 4]>::default();
1125
1126 linestrings.clear();
1127 lines.clear();
1128
1129 if !ignored_regions.is_empty() {
1130 let mut searched_edges_v = Vec::<Vob32>::new();
1132 let mut searched_edges_s = ignored_edges.clone();
1133 for it in self.diagram.edges().iter() {
1134 if searched_edges_s.get_f(it.id().usize()) {
1136 continue;
1137 }
1138 let mut edges = Vob32::fill(self.diagram.edges().len() as u32);
1139 self.mark_connected_edges(it.id(), &mut edges, true)?;
1140 let _ = searched_edges_s.or(&edges);
1141 searched_edges_v.push(edges);
1142 }
1143
1144 for edges in searched_edges_v.iter() {
1145 if self.edges_are_inside_ignored_region(edges, ignored_regions)? {
1146 let _ = ignored_edges.or(edges);
1148 continue;
1149 } else {
1150 }
1152 }
1153 }
1155
1156 let mut used_edges = ignored_edges.clone();
1157
1158 for it in self.diagram.edges().iter().enumerate() {
1159 if used_edges.get_f(it.0) {
1161 continue;
1162 }
1163 let edge_id = self.diagram().edge_index_unchecked(it.0);
1164
1165 self.traverse_edge(
1166 edge_id,
1167 false,
1168 &ignored_edges,
1169 &mut used_edges,
1170 &mut lines,
1171 &mut linestrings,
1172 maxdist,
1173 )?;
1174 }
1175
1176 for it in self.diagram.edges().iter().enumerate() {
1178 if used_edges.get_f(it.0) {
1180 continue;
1181 }
1182 let edge_id = self.diagram().edge_index_unchecked(it.0);
1183 #[cfg(feature = "console_debug")]
1184 println!("Did not use all edges, forcing the use of edge:{edge_id:?}",);
1185
1186 self.traverse_edge(
1187 edge_id,
1188 true,
1189 &ignored_edges,
1190 &mut used_edges,
1191 &mut lines,
1192 &mut linestrings,
1193 maxdist,
1194 )?;
1195 }
1196
1197 #[cfg(feature = "console_debug")]
1198 {
1199 println!("Got {} single lines", lines.len());
1200 println!("Got {} linestrings", linestrings.len());
1201 println!(
1202 " ignored_edges {:?}",
1203 ignored_edges
1204 .iter_storage()
1205 .map(|s| format!("{s:#02X} ")[2..].to_string())
1206 .collect::<String>()
1207 );
1208 println!(
1209 " used_edges {:?}",
1210 used_edges
1211 .iter_storage()
1212 .map(|s| format!("{s:#02X} ")[2..].to_string())
1213 .collect::<String>()
1214 );
1215 }
1216 self.lines = Some(lines);
1218 self.line_strings = Some(linestrings);
1219 #[cfg(feature = "console_debug")]
1220 {
1221 self.debug_edges = Some(edge_lines);
1222 }
1223 Ok(())
1224 }
1225
1226 #[allow(clippy::type_complexity)]
1228 fn traverse_cells(
1229 &mut self,
1230 max_dist: T3::Scalar,
1231 ignored_regions: &[(
1232 <<T3 as GenericVector3>::Vector2 as GenericVector2>::Aabb,
1233 Vec<T3::Vector2>,
1234 )],
1235 ) -> Result<(), CenterlineError> {
1236 let mut lines = self.lines.take().ok_or_else(|| {
1238 CenterlineError::InternalError(format!(
1239 "traverse_edges(): could not take lines. {}:{}",
1240 file!(),
1241 line!()
1242 ))
1243 })?;
1244 let mut linestrings = self.line_strings.take().ok_or_else(|| {
1245 CenterlineError::InternalError(format!(
1246 "traverse_edges(): could not take linestrings. {}:{}",
1247 file!(),
1248 line!()
1249 ))
1250 })?;
1251
1252 let mut ignored_edges = self.ignored_edges.take().unwrap_or_else(|| Vob32::fill(0));
1253
1254 #[cfg(feature = "console_debug")]
1255 let edge_lines = FxHashMap::<usize, [T3::Scalar; 4]>::default();
1256
1257 linestrings.clear();
1258 lines.clear();
1259
1260 if !ignored_regions.is_empty() {
1261 let mut searched_edges_v = Vec::<Vob32>::new();
1263 let mut searched_edges_s = ignored_edges.clone();
1264 for it in self.diagram.edges().iter().enumerate() {
1265 if searched_edges_s.get_f(it.0) {
1267 continue;
1268 }
1269 let mut edges = Vob32::fill(self.diagram.edges().len() as u32);
1270 self.mark_connected_edges(
1271 self.diagram.edge_index_unchecked(it.0),
1272 &mut edges,
1273 true,
1274 )?;
1275 let _ = searched_edges_s.or(&edges);
1276 searched_edges_v.push(edges);
1277 }
1278
1279 for edges in searched_edges_v.iter() {
1280 if self.edges_are_inside_ignored_region(edges, ignored_regions)? {
1281 let _ = ignored_edges.or(edges);
1283 continue;
1284 } else {
1285 }
1287 }
1288 }
1290
1291 let mut used_edges = ignored_edges.clone();
1292
1293 for it in self.diagram.edges().iter().enumerate() {
1294 if used_edges.get_f(it.0) {
1296 continue;
1297 }
1298 let edge_id = self.diagram.edge_index_unchecked(it.0);
1299
1300 self.traverse_edge(
1301 edge_id,
1302 false,
1303 &ignored_edges,
1304 &mut used_edges,
1305 &mut lines,
1306 &mut linestrings,
1307 max_dist,
1308 )?;
1309 }
1310
1311 for it in self.diagram.edges().iter().enumerate() {
1313 if used_edges.get_f(it.0) {
1315 continue;
1316 }
1317 let edge_id = self.diagram.edge_index_unchecked(it.0);
1318 #[cfg(feature = "console_debug")]
1319 println!("Did not use all edges, forcing the use of edge:{edge_id:?}",);
1320
1321 self.traverse_edge(
1322 edge_id,
1323 true,
1324 &ignored_edges,
1325 &mut used_edges,
1326 &mut lines,
1327 &mut linestrings,
1328 max_dist,
1329 )?;
1330 }
1331
1332 #[cfg(feature = "console_debug")]
1333 {
1334 println!("Got {} single lines", lines.len());
1335 println!("Got {} linestrings", linestrings.len());
1336 println!(
1337 " ignored_edges {}",
1338 ignored_edges
1339 .iter_storage()
1340 .map(|s| format!("{s:#02X} ")[2..].to_string())
1341 .collect::<String>()
1342 );
1343 println!(
1344 " used_edges {}",
1345 used_edges
1346 .iter_storage()
1347 .map(|s| format!("{s:#02X} ")[2..].to_string())
1348 .collect::<String>()
1349 );
1350 }
1351 self.lines = Some(lines);
1353 self.line_strings = Some(linestrings);
1354 #[cfg(feature = "console_debug")]
1355 {
1356 self.debug_edges = Some(edge_lines);
1357 }
1358 Ok(())
1359 }
1360
1361 #[inline(always)]
1363 fn mark_edge_and_twin_as_used(
1364 &self,
1365 edge_id: BV::EdgeIndex,
1366 used_edges: &mut Vob32,
1367 ) -> Result<(), CenterlineError> {
1368 let _ = used_edges.set(edge_id.usize(), true);
1369 #[cfg(feature = "console_debug")]
1370 print!("marking {edge_id:?}");
1371 {
1372 let twin = self.diagram.edge_get_twin(edge_id)?;
1373 #[cfg(feature = "console_debug")]
1374 print!(" & {twin:?}");
1375 if used_edges.get_f(twin.usize()) {
1376 eprintln!(" TWIN was already used!!!!! edge id:{twin:?}");
1377 }
1378 let _ = used_edges.set(twin.usize(), true);
1379 }
1380 Ok(())
1381 }
1382
1383 #[allow(clippy::too_many_arguments)]
1384 fn traverse_edge(
1390 &self,
1391 seed_edge: BV::EdgeIndex,
1392 force_seed_edge: bool,
1393 ignored_edges: &Vob32,
1394 used_edges: &mut Vob32,
1395 lines: &mut Vec<Line3<T3>>,
1396 linestrings: &mut Vec<Vec<T3>>,
1397 maxdist: T3::Scalar,
1398 ) -> Result<(), CenterlineError> {
1399 #[cfg(feature = "console_debug")]
1400 {
1401 println!();
1402 println!("->traverse_edge({seed_edge:?})");
1403 }
1404 #[cfg(feature = "console_debug")]
1405 let mut mockup = Vec::<Vec<BV::EdgeIndex>>::default();
1406
1407 let found_edge = force_seed_edge
1408 || self
1409 .diagram
1410 .edge_rot_next_iterator(seed_edge)
1411 .filter(|&x| !ignored_edges.get_f(x.usize()))
1412 .take(2) .count()
1414 == 1;
1415 if found_edge {
1416 let mut start_points = VecDeque::<BV::EdgeIndex>::default();
1417 let mut current_edge_set = Vec::<BV::EdgeIndex>::new();
1418 start_points.push_front(seed_edge);
1419 while !start_points.is_empty() {
1420 #[cfg(feature = "console_debug")]
1421 println!();
1422 let edge = start_points.pop_front().unwrap();
1423
1424 if ignored_edges.get_f(edge.usize()) {
1425 return Err(CenterlineError::InternalError(format!(
1427 "should never happen: edge {edge:?} already in ignore list. {}:{}",
1428 file!(),
1429 line!()
1430 )));
1431 }
1432 if used_edges.get_f(edge.usize()) {
1433 #[cfg(feature = "console_debug")]
1434 print!(" skip");
1435 continue;
1437 }
1438 #[cfg(feature = "console_debug")]
1439 println!();
1440
1441 current_edge_set.push(edge);
1442 self.mark_edge_and_twin_as_used(edge, used_edges)?;
1443
1444 let mut next_edge = self.diagram.edge(edge)?.next()?;
1445 loop {
1446 #[cfg(feature = "console_debug")]
1447 print!("Inner loop next_edge={next_edge:?} ");
1448
1449 let next_edges: Vec<BV::EdgeIndex> = self
1451 .diagram
1452 .edge_rot_next_iterator(next_edge)
1453 .filter(|&x| !ignored_edges.get_f(x.usize()))
1454 .collect();
1455
1456 #[cfg(feature = "console_debug")]
1457 {
1458 print!("candidates[");
1459
1460 for &ne in next_edges.iter() {
1461 if used_edges.get_f(ne.usize()) {
1462 print!("!");
1463 }
1464 print!("{ne:?},");
1465 }
1466 println!("]");
1467 }
1468 match next_edges.len() {
1469 1 | 2 => {
1470 let next_edges: Vec<BV::EdgeIndex> = next_edges
1471 .into_iter()
1472 .filter(|&x| !used_edges.get_f(x.usize()))
1473 .collect();
1474 if next_edges.len() == 1 {
1475 let e = next_edges.first().unwrap().to_owned();
1477 current_edge_set.push(e);
1478 self.mark_edge_and_twin_as_used(e, used_edges)?;
1479
1480 next_edge = self.diagram.edge(e)?.next()?;
1481 } else {
1482 self.convert_edges_to_lines(
1484 ¤t_edge_set,
1485 lines,
1486 linestrings,
1487 maxdist,
1488 )?;
1489 #[cfg(feature = "console_debug")]
1490 mockup.push(current_edge_set.clone());
1491 current_edge_set.clear();
1492
1493 if !next_edges.is_empty() {
1494 #[cfg(feature = "console_debug")]
1495 print!("1|2 Pushing new start points: [");
1496 for &e in next_edges.iter() {
1497 if !ignored_edges.get_f(e.usize())
1498 && !used_edges.get_f(e.usize())
1499 {
1500 #[cfg(feature = "console_debug")]
1501 print!("{e:?},");
1502 start_points.push_back(e);
1503 }
1504 }
1505 }
1506 #[cfg(feature = "console_debug")]
1507 {
1508 println!("]");
1509 println!("1|2 Starting new set");
1510 }
1511 break;
1512 }
1513 continue;
1514 }
1515 _ => {
1516 self.convert_edges_to_lines(
1518 ¤t_edge_set,
1519 lines,
1520 linestrings,
1521 maxdist,
1522 )?;
1523 if !next_edges.is_empty() {
1524 #[cfg(feature = "console_debug")]
1525 print!("0|_ Pushing new start points: [");
1526 for &e in next_edges.iter() {
1527 if !ignored_edges.get_f(e.usize())
1528 && !used_edges.get_f(e.usize())
1529 {
1530 #[cfg(feature = "console_debug")]
1531 print!("{e:?},");
1532 start_points.push_back(e);
1533 }
1534 }
1535 #[cfg(feature = "console_debug")]
1536 println!("]");
1537 }
1538 #[cfg(feature = "console_debug")]
1539 mockup.push(current_edge_set.clone());
1540 current_edge_set.clear();
1541
1542 #[cfg(feature = "console_debug")]
1543 println!("0|_ Starting new set");
1544
1545 break;
1546 }
1547 }
1548 }
1549 }
1550 } else {
1565 #[cfg(feature = "console_debug")]
1566 println!(
1567 "<-traverse_edge({seed_edge:?}) ignoring start edge, {:?}",
1568 self.diagram
1569 .edge_rot_next_iterator(seed_edge)
1570 .filter(|&x| !ignored_edges.get_f(x.usize()))
1571 .map(|x| x.usize())
1572 .collect::<Vec<usize>>()
1573 );
1574 }
1575 Ok(())
1576 }
1577
1578 fn convert_edges_to_lines(
1579 &self,
1580 edges: &[BV::EdgeIndex],
1581 lines: &mut Vec<Line3<T3>>,
1582 linestrings: &mut Vec<Vec<T3>>,
1583 maxdist: T3::Scalar,
1584 ) -> Result<(), CenterlineError> {
1585 #[cfg(feature = "console_debug")]
1586 {
1587 println!();
1588 println!(
1589 "Converting {:?} to lines",
1590 edges.iter().map(|&x| x.usize()).collect::<Vec<usize>>()
1591 );
1592 }
1593 match edges.len() {
1594 0 => panic!(),
1595 1 => {
1596 let edge_id = edges.first().unwrap();
1597 let edge = self.diagram.edge(*edge_id)?;
1598 match self.convert_edge_to_shape(edge) {
1599 Ok(Shape3d::Line(l)) => lines.push(l),
1600 Ok(Shape3d::ParabolicArc(a)) => {
1601 linestrings.push(a.discretize_3d(maxdist));
1602 }
1603 Ok(Shape3d::Linestring(_s)) => {
1604 panic!();
1605 }
1606 Err(_) => {
1607 println!("Error :{edge:?}");
1608 }
1609 }
1610 }
1611 _ => {
1612 let mut ls = Vec::<T3>::default();
1613 for edge_id in edges.iter() {
1614 let edge = self.diagram.edge(*edge_id)?;
1615 match self.convert_edge_to_shape(edge)? {
1616 Shape3d::Line(l) => {
1617 ls.push(l.start);
1619 ls.push(l.end);
1620 }
1622 Shape3d::ParabolicArc(a) => {
1623 ls.append(&mut a.discretize_3d(maxdist));
1625 }
1627 Shape3d::Linestring(_s) => {
1629 return Err(CenterlineError::InternalError(format!(
1630 "convert_edges_to_lines() got an unexpected linestring. {}:{}",
1631 file!(),
1632 line!()
1633 )));
1634 }
1635 }
1636 }
1637 linestrings.push(ls);
1638 }
1639 }
1640 Ok(())
1642 }
1643
1644 fn convert_edge_to_shape(&self, edge: &BV::Edge) -> Result<Shape3d<T3>, CenterlineError> {
1645 let edge_id = edge.id();
1646 let edge_twin_id = self.diagram.edge_get_twin(edge_id)?;
1647
1648 let vertex0 = self.diagram.vertex(edge.vertex0().ok_or_else(|| {
1650 CenterlineError::InternalError(format!(
1651 "Could not find vertex 0. {}:{}",
1652 file!(),
1653 line!()
1654 ))
1655 })?)?;
1656
1657 let vertex1 = self.diagram.edge_get_vertex1(edge_id)?.ok_or_else(|| {
1658 CenterlineError::InternalError(format!(
1659 "Could not find vertex 1. {}:{}",
1660 file!(),
1661 line!()
1662 ))
1663 })?;
1664 let vertex1 = self.diagram.vertex(vertex1)?;
1665
1666 #[cfg(feature = "console_debug")]
1667 println!(
1668 "Converting e:{:?} to line v0:{:?} v1:{:?}",
1669 edge.id(),
1670 vertex0.get_id(),
1671 vertex1.get_id(),
1672 );
1673
1674 let start_point = T3::Vector2::new_2d(vertex0.x().as_(), vertex0.y().as_());
1675 let end_point = T3::Vector2::new_2d(vertex1.x().as_(), vertex1.y().as_());
1676 let cell_id = self.diagram.edge(edge_id)?.cell().unwrap();
1677 let cell = self.diagram.cell(cell_id)?;
1678 let twin_cell_id = self.diagram.edge(edge_twin_id)?.cell().unwrap();
1679
1680 let cell_point = if cell.contains_point() {
1681 #[cfg(feature = "console_debug")]
1682 println!("cell c:{cell_id:?}");
1683 self.retrieve_point(cell_id)?
1684 } else {
1685 #[cfg(feature = "console_debug")]
1686 println!("twin cell c:{twin_cell_id:?}",);
1687 self.retrieve_point(twin_cell_id)?
1688 };
1689 let segment = if cell.contains_point() {
1690 #[cfg(feature = "console_debug")]
1691 println!("twin segment c:{twin_cell_id:?}");
1692 self.retrieve_segment(twin_cell_id)?
1693 } else {
1694 #[cfg(feature = "console_debug")]
1695 println!("segment c:{cell_id:?}",);
1696 self.retrieve_segment(cell_id)?
1697 };
1698
1699 let segment_start_point = T3::Vector2::new_2d(segment.start.x.as_(), segment.start.y.as_());
1700 let segment_end_point = T3::Vector2::new_2d(segment.end.x.as_(), segment.end.y.as_());
1701 let cell_point = T3::Vector2::new_2d(cell_point.x.as_(), cell_point.y.as_());
1702 #[cfg(feature = "console_debug")]
1703 {
1704 println!("sp:[{},{}]", start_point.x(), start_point.y());
1705 println!("ep:[{},{}]", end_point.x(), end_point.y());
1706 println!(
1707 "cp:[{},{}] sg:[{},{},{},{}]",
1708 cell_point.x(),
1709 cell_point.y(),
1710 segment_start_point.x(),
1711 segment_start_point.y(),
1712 segment_end_point.x(),
1713 segment_end_point.y()
1714 );
1715 }
1716
1717 if edge.is_curved() {
1718 let arc = linestring_2d::VoronoiParabolicArc::new(
1719 Line2 {
1720 start: segment_start_point,
1721 end: segment_end_point,
1722 },
1723 cell_point,
1724 start_point,
1725 end_point,
1726 );
1727 #[cfg(feature = "console_debug")]
1728 println!("Converted {:?} to {:?}", edge.id(), arc);
1729 Ok(Shape3d::ParabolicArc(arc))
1730 } else {
1731 let distance_to_start = {
1732 if vertex0.is_site_point() {
1733 T3::Scalar::ZERO
1734 } else if cell.contains_point() {
1735 let cell_point = self.retrieve_point(cell_id)?;
1736 let cell_point = T3::Vector2::new_2d(cell_point.x.as_(), cell_point.y.as_());
1737 -cell_point.distance(start_point)
1738 } else {
1739 let segment = self.retrieve_segment(cell_id)?;
1740 let segment_start_point =
1741 T3::Vector2::new_2d(segment.start.x.as_(), segment.start.y.as_());
1742 let segment_end_point =
1743 T3::Vector2::new_2d(segment.end.x.as_(), segment.end.y.as_());
1744 -linestring_2d::distance_to_line_squared_safe(
1745 segment_start_point,
1746 segment_end_point,
1747 start_point,
1748 )
1749 .sqrt()
1750 }
1751 };
1752 let distance_to_end = {
1753 if vertex1.is_site_point() {
1754 T3::Scalar::ZERO
1755 } else {
1756 let cell_id = self
1757 .diagram
1758 .edge(vertex1.get_incident_edge().unwrap())?
1759 .cell()
1760 .unwrap();
1761 let cell = self.diagram.cell(cell_id)?;
1762 if cell.contains_point() {
1763 let cell_point = self.retrieve_point(cell_id)?;
1764 let cell_point =
1765 T3::Vector2::new_2d(cell_point.x.as_(), cell_point.y.as_());
1766 -cell_point.distance(end_point)
1767 } else {
1768 let segment = self.retrieve_segment(cell_id)?;
1769 let segment_start_point =
1770 T3::Vector2::new_2d(segment.start.x.as_(), segment.start.y.as_());
1771 let segment_end_point =
1772 T3::Vector2::new_2d(segment.end.x.as_(), segment.end.y.as_());
1773 -linestring_2d::distance_to_line_squared_safe(
1774 segment_start_point,
1775 segment_end_point,
1776 end_point,
1777 )
1778 .sqrt()
1779 }
1780 }
1781 };
1782 let line = Line3 {
1783 start: T3::new_3d(start_point.x(), start_point.y(), distance_to_start),
1784 end: T3::new_3d(end_point.x(), end_point.y(), distance_to_end),
1785 };
1786 #[cfg(feature = "console_debug")]
1787 println!("Converted {:?} to {:?}", edge.id(), line);
1788 Ok(Shape3d::Line(line))
1789 }
1790 }
1791}
1792
1793#[derive(Clone)]
1799pub struct LineStringSet2<T: GenericVector2> {
1800 set: Vec<Vec<T>>,
1801 aabb: <T as GenericVector2>::Aabb,
1802 convex_hull: Option<Vec<T>>,
1803 pub internals: Option<Vec<(<T as GenericVector2>::Aabb, Vec<T>)>>,
1804}
1805
1806impl<T: GenericVector2> Default for LineStringSet2<T> {
1807 #[inline]
1808 fn default() -> Self {
1809 Self {
1810 set: Vec::<_>::default(),
1811 aabb: <T as GenericVector2>::Aabb::default(),
1812 convex_hull: None,
1813 internals: None,
1814 }
1815 }
1816}
1817
1818impl<T: GenericVector2> LineStringSet2<T> {
1819 pub fn steal_from(other: &mut LineStringSet2<T>) -> Self {
1821 let mut set = Vec::<Vec<T>>::new();
1823 set.append(&mut other.set);
1824 Self {
1825 set,
1826 aabb: other.aabb,
1827 convex_hull: other.convex_hull.take(),
1828 internals: other.internals.take(),
1829 }
1831 }
1832
1833 pub fn set(&self) -> &Vec<Vec<T>> {
1834 &self.set
1835 }
1836
1837 pub fn with_capacity(capacity: usize) -> Self {
1838 Self {
1839 set: Vec::<Vec<T>>::with_capacity(capacity),
1840 aabb: <T as GenericVector2>::Aabb::default(),
1841 convex_hull: None,
1842 internals: None,
1843 }
1844 }
1845
1846 pub fn get_internals(&self) -> Option<&Vec<(<T as GenericVector2>::Aabb, Vec<T>)>> {
1847 self.internals.as_ref()
1848 }
1849
1850 pub fn is_empty(&self) -> bool {
1851 self.set.is_empty()
1852 }
1853
1854 pub fn push(&mut self, ls: Vec<T>) {
1855 if !ls.is_empty() {
1856 self.set.push(ls);
1857
1858 for ls in self.set.last().unwrap().iter() {
1859 self.aabb.add_point(*ls);
1860 }
1861 }
1862 }
1863
1864 pub fn get_convex_hull(&self) -> &Option<Vec<T>> {
1866 &self.convex_hull
1867 }
1868
1869 pub fn calculate_convex_hull(&mut self) -> Result<&Vec<T>, LinestringError> {
1871 let tmp: Vec<_> = self.set.iter().flatten().cloned().collect();
1873 self.convex_hull = Some(convex_hull::graham_scan(&tmp)?);
1874 Ok(self.convex_hull.as_ref().unwrap())
1875 }
1876
1877 pub fn get_aabb(&self) -> <T as GenericVector2>::Aabb {
1879 self.aabb
1880 }
1881
1882 pub fn copy_to_3d(&self, plane: Plane) -> LineStringSet3<T::Vector3> {
1911 let mut rv = LineStringSet3::<T::Vector3>::with_capacity(self.set.len());
1912 for ls in self.set.iter() {
1913 rv.push(ls.copy_to_3d(plane));
1914 }
1915 rv
1916 }
1917
1918 pub fn take_from(&mut self, mut other: Self) {
1920 self.aabb.add_aabb(&other.aabb);
1921 self.set.append(&mut other.set);
1922 }
1923
1924 pub fn take_from_internal(&mut self, other: &mut Self) -> Result<(), LinestringError> {
1928 if other.convex_hull.is_none() {
1930 return Err(LinestringError::InvalidData(
1931 "'other' did not contain a valid 'convex_hull' field".to_string(),
1932 ));
1933 }
1934 if self.aabb.is_empty() {
1935 return Err(LinestringError::InvalidData(
1938 "'self' did not contain a valid 'aabb' field".to_string(),
1939 ));
1940 }
1941 if other.aabb.is_empty() {
1942 return Err(LinestringError::InvalidData(
1943 "'other' did not contain a valid 'aabb' field".to_string(),
1944 ));
1945 }
1946 if !self.aabb.contains_aabb_inclusive(&other.aabb) {
1947 return Err(LinestringError::InvalidData(
1950 "The 'other.aabb' is not contained within 'self.aabb'".to_string(),
1951 ));
1952 }
1953 if self.internals.is_none() {
1954 self.internals = Some(Vec::<(<T as GenericVector2>::Aabb, Vec<T>)>::new())
1955 }
1956
1957 self.set.append(&mut other.set);
1958
1959 if let Some(ref mut other_internals) = other.internals {
1960 self.internals.as_mut().unwrap().append(other_internals);
1962 }
1963
1964 self.internals
1965 .as_mut()
1966 .unwrap()
1967 .push((other.aabb, other.convex_hull.take().unwrap()));
1968 Ok(())
1969 }
1970
1971 pub fn apply<F: Fn(T) -> T>(&mut self, f: &F) {
1974 for s in self.set.iter_mut() {
1975 s.apply(f);
1976 }
1977 self.aabb.apply(f);
1978 if let Some(ref mut convex_hull) = self.convex_hull {
1979 convex_hull.apply(f);
1980 }
1981 if let Some(ref mut internals) = self.internals {
1982 for i in internals.iter_mut() {
1983 i.0.apply(f);
1984 i.1.apply(f);
1985 }
1986 }
1987 }
1988}
1989
1990#[derive(PartialEq, Clone)]
1993pub struct LineStringSet3<T: GenericVector3> {
1994 pub set: Vec<Vec<T>>,
1995 pub aabb: <T as GenericVector3>::Aabb,
1996}
1997
1998impl<T: GenericVector3> Default for LineStringSet3<T> {
1999 fn default() -> Self {
2000 Self {
2001 set: Vec::<Vec<T>>::default(),
2002 aabb: <T as GenericVector3>::Aabb::default(),
2003 }
2004 }
2005}
2006
2007impl<T: GenericVector3> LineStringSet3<T> {
2008 pub fn with_capacity(capacity: usize) -> Self {
2009 Self {
2010 set: Vec::<Vec<T>>::with_capacity(capacity),
2011 aabb: <T as GenericVector3>::Aabb::default(),
2012 }
2013 }
2014
2015 pub fn set(&self) -> &Vec<Vec<T>> {
2016 &self.set
2017 }
2018
2019 pub fn is_empty(&self) -> bool {
2020 self.set.is_empty()
2021 }
2022
2023 pub fn push(&mut self, ls: Vec<T>) {
2024 if !ls.is_empty() {
2025 self.set.push(ls);
2026
2027 for ls in self.set.last().unwrap().iter() {
2028 self.aabb.add_point(*ls);
2029 }
2030 }
2031 }
2032
2033 pub fn get_aabb(&self) -> <T as GenericVector3>::Aabb {
2034 self.aabb
2035 }
2036
2037 pub fn apply<F: Fn(T) -> T>(&mut self, f: &F) {
2038 self.set.iter_mut().for_each(|x| x.apply(f));
2039 self.aabb.apply(f);
2040 }
2041
2042 pub fn copy_to_2d(&self, plane: Plane) -> LineStringSet2<T::Vector2> {
2046 let mut rv = LineStringSet2::with_capacity(self.set.len());
2047 for ls in self.set.iter() {
2048 rv.push(ls.copy_to_2d(plane));
2049 }
2050 rv
2051 }
2052
2053 pub fn take_from(&mut self, other: &mut Self) {
2055 self.aabb.add_aabb(&other.aabb);
2056 self.set.append(&mut other.set);
2057 }
2058}
2059
2060pub enum Shape3d<T: GenericVector3> {
2062 Line(Line3<T>),
2063 Linestring(Vec<T>),
2064 ParabolicArc(linestring_2d::VoronoiParabolicArc<T::Vector2>),
2065}