outlinify/src/geometry/union.rs
2024-08-15 18:34:13 +02:00

190 lines
6.5 KiB
Rust

use std::collections::HashMap;
use clipper2::{FillRule, One, Paths, PointInPolygonResult};
use crate::geometry::helpers::do_intersect;
use super::{
elements::{linepath::LinePath, Element},
point::Point,
ClipperPaths,
};
#[derive(Debug, Clone)]
pub struct ConductorNet {
pub outline: ClipperPaths,
pub included_points: Vec<Point>,
}
pub fn union_lines(lines: &[LinePath]) -> Vec<ConductorNet> {
let mut intersection_map = HashMap::new();
println!(
"START LINE UNION of {:?}",
lines
.iter()
.map(|l| l.points.clone())
.collect::<Vec<Vec<Point>>>()
);
for (index, line) in lines.iter().enumerate() {
if !line.is_empty() {
// create list of intersecting lines
let mut intersections = Vec::new();
let (p1, q1) = (line.points[0], line.points[1]);
println!("LINE 1 {p1:?}, {q1:?}");
for (i, l) in lines.iter().enumerate() {
if !l.is_empty() {
// do not check for intersection with itself
if index != i {
// check for all lines in path
let intersect = l.points.windows(2).any(|w| {
let (p2, q2) = (w[0], w[1]);
println!("LINE 2 {p2:?}, {q2:?}");
do_intersect(p1, q1, p2, q2)
});
println!("INTERSECTING: {intersect}");
if intersect {
// let entry = intersection_map.entry(index).or_insert(Vec::new());
// entry.push(i);
intersections.push(i);
}
}
}
}
//
intersection_map.insert(index, intersections);
}
}
println!("{intersection_map:?}");
let mut final_geo = Vec::new();
// go through all line segments
for i in 0..intersection_map.len() {
if let Some(mut intersections) = intersection_map.remove(&i) {
// if no intersections are given, add line as own conductor net
// if intersections.is_empty() {
// if let Some(line) = lines.get(i) {
// final_geo.push(ConductorNet {
// outline: line.outline.clone(),
// included_points: line.points.clone(),
// })
// }
// }
println!("--- Taking line segment {i}");
// get current line path
let mut geo = lines[i].outline.clone();
let mut included_points = lines[i].points.clone();
// union with intersecting lines until done
println!("Intersection points: {intersections:?}");
while let Some(other) = intersections.pop() {
println!("Intersecting with line # {other:?}");
// union with intersecting line
let intersecting_line = &lines[other];
if let Some(union) = union(&geo, &intersecting_line.outline) {
geo = union;
}
// add points of added line to included points
for point in &intersecting_line.points {
if !included_points.contains(point) {
included_points.push(point.to_owned()); // show included points
}
}
// get intersections of united line and add them if not already in own
if let Some(new_intersections) = intersection_map.remove(&other) {
for o in new_intersections {
// add to original line if not self and not already inside
if o != i // ensure to not intersect with itself
&& !intersections.contains(&o) // do not add if already in intersection list
&& intersection_map.contains_key(&o)
// check if intersection was already performed
{
intersections.push(o);
}
}
}
}
// create conductor net
final_geo.push(ConductorNet {
outline: geo,
included_points,
})
}
}
// if final_geo.is_empty() {
// if let Some(line) = lines.get(0) {
// final_geo.push(ConductorNet {
// outline: line.outline.clone(),
// included_points: line.points.clone(),
// })
// }
// }
final_geo
}
pub fn union_with_apertures(
apertures: &Vec<Element>,
conductors: Vec<ConductorNet>,
) -> Option<ClipperPaths> {
let mut finalized_paths = Vec::new(); // handle apertures without connection
let mut current_conductors = conductors;
// go through all apertures
for ap in apertures {
// get outline path
let geo = ap.to_paths();
// create empty set of conductors
let mut new_conductors = Vec::new();
// create indicator if aperture is isolated
let mut isolated = true;
// go through all available conductor nets
for conductor in current_conductors {
// check if any point of the conductor net is not outside of the aperture
if conductor
.included_points
.iter()
.any(|c| ap.outline().is_point_inside(c.into()) != PointInPolygonResult::IsOutside)
{
// union aperture with conductor net
let geo = union(&geo, &conductor.outline)?;
let mut cond = conductor;
cond.outline = geo;
isolated = false;
new_conductors.push(cond);
} else {
new_conductors.push(conductor);
}
}
// add aperture to extra container if isolated
if isolated {
finalized_paths.push(geo);
}
// update current conductors
current_conductors = new_conductors;
}
for conductor in current_conductors {
finalized_paths.push(conductor.outline);
}
finalized_paths.into_iter().reduce(|mut all, paths| {
all.push(paths);
all
})
}
fn union(path1: &ClipperPaths, path2: &ClipperPaths) -> Option<ClipperPaths> {
path1
.to_clipper_subject()
.add_clip(path2.clone())
.union(FillRule::default())
.ok()
}