190 lines
6.5 KiB
Rust
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()
|
|
}
|