1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
// SPDX-FileCopyrightText: 2022 Thomas Kramer
//
// SPDX-License-Identifier: AGPL-3.0-or-later
//! Extract the connectivity graph of polygons.
use iron_shapes::prelude::*;
use std::fmt::Debug;
use crate::sweep_line::intersection::subdivide_segments;
use crate::sweep_line::sweep_event::SweepEvent;
use crate::PolygonSemantics;
use std::collections::{BinaryHeap, HashMap, HashSet};
use std::hash::Hash;
use std::rc::Rc;
/// Extract a connectivity of a set of polygons.
/// The polygons are supplied in the form of an interator over the polygon edges. If the supplied edges are inconsistent, e.g. don't form closed polygon shapes,
/// then the result is undefined.
///
/// Each edge must be labelled with the identifier of the polygon it belongs to.
/// Connectivity is encoded as an iterator over multi-graph edges.
/// Each consists of the IDs of the two touching polygons and a location where the both polygons touch.
/// Edges can appear many times.
///
/// # Examples
/// ```
/// use iron_shapes::prelude::*;
/// use iron_shapes::traits::Translate;
/// use iron_shapes_booleanop::connectivity_extraction::*;
/// use iron_shapes_booleanop::*;
/// use std::collections::HashSet;
///
/// let p0 = Polygon::from(vec![(0f64, 0.), (2., 0.), (2., 2.), (0., 2.)]);
/// let p1 = p0.translate((1., 1.).into());
/// let p2 = p0.translate((0., 100.).into()); // Far away, not touching anything.
///
/// let polygons = vec![p0, p1, p2];
///
/// // Convert the polygons into edges labelled with an identifier of their polygon.
/// let polygon_edges = polygons
/// .iter()
/// .enumerate()
/// .flat_map(|(id, polygon)| polygon.all_edges_iter().map(move |e| (e, id)));
///
/// let connectivity_graph = extract_connectivity_graph(
/// edge_intersection_float,
/// polygon_edges,
/// PolygonSemantics::Union,
/// );
///
/// // p0 and p1 are connected.
/// assert_eq!(connectivity_graph.get(&0), Some(&HashSet::from([1])));
/// assert_eq!(connectivity_graph.get(&1), Some(&HashSet::from([0])));
///
/// // p2 is not connected to any other polygon.
/// assert_eq!(connectivity_graph.get(&2), None);
/// ```
pub fn extract_connectivity<'a, I, T, ID>(
edge_intersection: I,
polygon_edges: impl Iterator<Item = (Edge<T>, ID)>,
polygon_semantics: PolygonSemantics,
) -> impl Iterator<Item = (ID, ID, Point<T>)>
where
I: Fn(&Edge<T>, &Edge<T>) -> EdgeIntersection<T, T, Edge<T>>,
T: CoordinateType + Debug + 'a,
ID: Clone + Hash + Eq,
{
// Prepare the event queue.
let mut event_queue: BinaryHeap<Rc<SweepEvent<_, Counter<ID>, ID>>> =
crate::init_events::fill_queue(polygon_edges);
// Compute the edge intersections, the result is a set of sorted non-intersecting edges stored
// as events.
let sorted_events = subdivide_segments(edge_intersection, &mut event_queue, |event, prev| {
update_counter(event, prev)
});
// Extract connectivity graph.
{
// Take left events only.
let left_events = sorted_events.into_iter().filter(|e| e.is_left_event());
// Check if an edge count signals the inside or the outside of a polygon.
let is_inside = move |count: i32| -> bool {
match polygon_semantics {
PolygonSemantics::Union => count != 0,
PolygonSemantics::XOR => count % 2 != 0,
}
};
// Create iterator of graph edges.
// Edges may appear multiple times. Each edge comes with a point which indicates the location of the connectivity.
let multi_graph_edges = left_events.flat_map(move |current_event| {
// Get connectivity of this event.
debug_assert!(current_event.is_left_event());
let shape_id = current_event.property.as_ref().unwrap().clone(); // Left event must have the property.
let point = current_event.p; // Location of connectivity.
// Take counter from event to own it.
// This destroys the structure of the sweep events.
let counter =
current_event.with_counter_mut(|ctr| std::mem::replace(ctr, Default::default()));
// Look at edge counts and find shapes enclosing the current event.
let shape_id_clone = shape_id.clone();
let connected_ids = counter
.counters
.into_iter()
.filter(move |(id, _count)| id != &shape_id_clone) // Look only at other polygons.
.filter(move |(_id, count)| is_inside(*count))
.map(|(id, _)| id);
connected_ids.map(move |id| (shape_id.clone(), id, point))
});
multi_graph_edges
}
}
/// Extract a connectivity graph of a set of polygons.
pub fn extract_connectivity_graph<'a, I, T, ID>(
edge_intersection: I,
polygon_edges: impl Iterator<Item = (Edge<T>, ID)>,
polygon_semantics: PolygonSemantics,
) -> HashMap<ID, HashSet<ID>>
where
I: Fn(&Edge<T>, &Edge<T>) -> EdgeIntersection<T, T, Edge<T>>,
T: CoordinateType + Debug + 'a,
ID: Clone + Hash + Eq,
{
let multi_graph_edges =
extract_connectivity(edge_intersection, polygon_edges, polygon_semantics);
let mut graph: HashMap<ID, HashSet<ID>> = Default::default();
// Insert graph edges.
for (id_a, id_b, _point) in multi_graph_edges {
graph
.entry(id_a.clone())
.or_insert(Default::default())
.insert(id_b.clone());
// Insert reverse edge.
graph.entry(id_b).or_insert(Default::default()).insert(id_a);
}
graph
}
#[derive(Clone)]
struct Counter<K> {
counters: HashMap<K, i32>,
}
impl<K> Default for Counter<K> {
fn default() -> Self {
Self {
counters: Default::default(),
}
}
}
fn update_counter<T, ID>(
event: &Rc<SweepEvent<T, Counter<ID>, ID>>,
maybe_prev: Option<&Rc<SweepEvent<T, Counter<ID>, ID>>>,
) where
T: CoordinateType,
ID: Clone + Hash + Eq,
{
debug_assert!(event.is_left_event());
// Update counter.
event.with_counter_mut(|ctr| {
let edge_id = event.property.as_ref().unwrap(); // Property must be set for all left events.
// Clone counters of previous edge.
let prev_counter = maybe_prev
.as_ref()
.map(|prev| {
debug_assert!(prev.is_left_event());
prev.with_counter(|ctr| ctr.clone())
})
.unwrap_or(Default::default());
*ctr = prev_counter;
// Increment counter.
{
let edge_weight = event.edge_weight();
let current_counter = ctr.counters.entry(edge_id.clone()).or_insert(0);
*current_counter += edge_weight;
// Remove counter if it is zero.
if *current_counter == 0 {
ctr.counters.remove(edge_id);
};
}
});
}