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, } pub fn union_lines(lines: &[LinePath]) -> Vec { let mut intersection_map = HashMap::new(); println!( "START LINE UNION of {:?}", lines .iter() .map(|l| l.points.clone()) .collect::>>() ); 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, conductors: Vec, ) -> Option { 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 { path1 .to_clipper_subject() .add_clip(path2.clone()) .union(FillRule::default()) .ok() }