various changes
This commit is contained in:
		
							parent
							
								
									63203ee717
								
							
						
					
					
						commit
						7913541282
					
				
							
								
								
									
										8
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@ -1,5 +1,7 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "cSpell.words": [
 | 
					    "cSpell.words": [
 | 
				
			||||||
 | 
					        "centered",
 | 
				
			||||||
 | 
					        "cmds",
 | 
				
			||||||
        "color",
 | 
					        "color",
 | 
				
			||||||
        "Color",
 | 
					        "Color",
 | 
				
			||||||
        "consts",
 | 
					        "consts",
 | 
				
			||||||
@ -8,11 +10,14 @@
 | 
				
			|||||||
        "egui",
 | 
					        "egui",
 | 
				
			||||||
        "emath",
 | 
					        "emath",
 | 
				
			||||||
        "epaint",
 | 
					        "epaint",
 | 
				
			||||||
 | 
					        "evalexpr",
 | 
				
			||||||
        "excellon",
 | 
					        "excellon",
 | 
				
			||||||
        "Excellon",
 | 
					        "Excellon",
 | 
				
			||||||
        "excellons",
 | 
					        "excellons",
 | 
				
			||||||
        "gerbers",
 | 
					        "gerbers",
 | 
				
			||||||
        "Heatsink",
 | 
					        "Heatsink",
 | 
				
			||||||
 | 
					        "itertools",
 | 
				
			||||||
 | 
					        "Itertools",
 | 
				
			||||||
        "linepath",
 | 
					        "linepath",
 | 
				
			||||||
        "obround",
 | 
					        "obround",
 | 
				
			||||||
        "Obround",
 | 
					        "Obround",
 | 
				
			||||||
@ -21,6 +26,7 @@
 | 
				
			|||||||
        "rect",
 | 
					        "rect",
 | 
				
			||||||
        "Rect",
 | 
					        "Rect",
 | 
				
			||||||
        "regmatch",
 | 
					        "regmatch",
 | 
				
			||||||
        "Soldermask"
 | 
					        "Soldermask",
 | 
				
			||||||
 | 
					        "winresource"
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										856
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										856
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Cargo.toml
									
									
									
									
									
								
							@ -6,16 +6,24 @@ edition = "2021"
 | 
				
			|||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
eframe = "0.28.1"
 | 
					eframe = "0.28.1"
 | 
				
			||||||
egui_plot = { version = "0.28.1", features = ["serde"] }
 | 
					egui_plot = { version = "0.28.1", features = ["serde"] }
 | 
				
			||||||
 | 
					egui_extras = { version = "0.28.1", features = ["all_loaders"] }
 | 
				
			||||||
rfd = "0.14"
 | 
					rfd = "0.14"
 | 
				
			||||||
 | 
					image = "0.25"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
clipper2 = "0.4.1"
 | 
					clipper2 = "0.4.1"
 | 
				
			||||||
gerber-types = "0.3"
 | 
					# gerber-types = "0.3"
 | 
				
			||||||
 | 
					gerber-types = { path = "./gerber-types-rs" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
svg = "0.17"
 | 
					svg = "0.17"
 | 
				
			||||||
dxf = "0.5"
 | 
					dxf = "0.5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lazy-regex = "3.1.0"
 | 
					lazy-regex = "3.1.0"
 | 
				
			||||||
 | 
					itertools = "0.13"
 | 
				
			||||||
 | 
					evalexpr = "11.3.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tracing = "0.1"
 | 
					tracing = "0.1"
 | 
				
			||||||
tracing-subscriber = "0.3"
 | 
					tracing-subscriber = "0.3"
 | 
				
			||||||
error-stack = "0.5"
 | 
					error-stack = "0.5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[build-dependencies]
 | 
				
			||||||
 | 
					winresource = "0.1"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										14
									
								
								build.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								build.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					use {
 | 
				
			||||||
 | 
					    std::{env, io},
 | 
				
			||||||
 | 
					    winresource::WindowsResource,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() -> io::Result<()> {
 | 
				
			||||||
 | 
					    if env::var_os("CARGO_CFG_WINDOWS").is_some() {
 | 
				
			||||||
 | 
					        WindowsResource::new()
 | 
				
			||||||
 | 
					            // This path can be absolute, or relative to your crate root.
 | 
				
			||||||
 | 
					            .set_icon("resources/icon.ico")
 | 
				
			||||||
 | 
					            .compile()?;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								resources/excellon_file.afdesign
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/excellon_file.afdesign
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								resources/excellon_file.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/excellon_file.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/geometry_file.afdesign
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/geometry_file.afdesign
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								resources/geometry_file.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/geometry_file.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 5.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/gerber_file.afdesign
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/gerber_file.afdesign
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								resources/gerber_file.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/gerber_file.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.0 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/icon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/icon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 147 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 187 KiB  | 
@ -1,15 +1,8 @@
 | 
				
			|||||||
use eframe::{
 | 
					 | 
				
			||||||
    egui::{Color32, Ui},
 | 
					 | 
				
			||||||
    epaint::CircleShape,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use egui_plot::PlotUi;
 | 
					use egui_plot::PlotUi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{application::Application, geometry::elements::Element};
 | 
				
			||||||
    application::Application,
 | 
					 | 
				
			||||||
    geometry::{elements::Element, DrawableRaw},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{draw_floating_area_on_canvas, draw_on_plot_canvas, CanvasColour, Drawer, PlotDrawer};
 | 
					use super::{draw_on_plot_canvas, CanvasColour, PlotDrawer};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn draw_excellons_(ui: &mut PlotUi, app: &mut Application) {
 | 
					pub fn draw_excellons_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			||||||
    for (file_name, (_, excellon)) in &app.excellons {
 | 
					    for (file_name, (_, excellon)) in &app.excellons {
 | 
				
			||||||
@ -18,7 +11,6 @@ pub fn draw_excellons_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			|||||||
        for circle in excellon.holes.iter() {
 | 
					        for circle in excellon.holes.iter() {
 | 
				
			||||||
            draw_on_plot_canvas(
 | 
					            draw_on_plot_canvas(
 | 
				
			||||||
                ui,
 | 
					                ui,
 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                PlotDrawer::Drawable((
 | 
					                PlotDrawer::Drawable((
 | 
				
			||||||
                    &Element::Circle(circle.to_owned()),
 | 
					                    &Element::Circle(circle.to_owned()),
 | 
				
			||||||
                    CanvasColour::Excellon,
 | 
					                    CanvasColour::Excellon,
 | 
				
			||||||
@ -28,29 +20,3 @@ pub fn draw_excellons_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
pub fn draw_excellons(ui: &mut Ui, app: &mut Application) {
 | 
					 | 
				
			||||||
    for (file_name, (ex_name, excellon)) in &app.excellons {
 | 
					 | 
				
			||||||
        let selected = &app.selection == file_name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (i, circle) in excellon.holes.iter().enumerate() {
 | 
					 | 
				
			||||||
            draw_floating_area_on_canvas(
 | 
					 | 
				
			||||||
                ui,
 | 
					 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                circle.canvas_pos(),
 | 
					 | 
				
			||||||
                ("ExcellonArea", ex_name, i),
 | 
					 | 
				
			||||||
                Drawer::Closure(&|ui| {
 | 
					 | 
				
			||||||
                    ui.painter().add(CircleShape::filled(
 | 
					 | 
				
			||||||
                        circle.position.invert_y().into(),
 | 
					 | 
				
			||||||
                        circle.diameter as f32 / 2.,
 | 
					 | 
				
			||||||
                        if selected {
 | 
					 | 
				
			||||||
                            Color32::BROWN.gamma_multiply(0.5)
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            Color32::BROWN
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                    ));
 | 
					 | 
				
			||||||
                }),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,4 @@
 | 
				
			|||||||
use eframe::{
 | 
					use eframe::egui::Pos2;
 | 
				
			||||||
    egui::{Color32, Pos2, Ui},
 | 
					 | 
				
			||||||
    epaint::{CircleShape, PathShape, PathStroke},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use egui_plot::{Line, PlotPoint, PlotPoints, PlotUi};
 | 
					use egui_plot::{Line, PlotPoint, PlotPoints, PlotUi};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
@ -9,7 +6,7 @@ use crate::{
 | 
				
			|||||||
    geometry::{elements::circle::Circle, DrawableRaw},
 | 
					    geometry::{elements::circle::Circle, DrawableRaw},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{draw_floating_area_on_canvas, draw_on_plot_canvas, CanvasColour, Drawer, PlotDrawer};
 | 
					use super::{draw_on_plot_canvas, CanvasColour, PlotDrawer};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn draw_geometries_(ui: &mut PlotUi, app: &mut Application) {
 | 
					pub fn draw_geometries_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			||||||
    for (file_name, geo) in &app.outlines {
 | 
					    for (file_name, geo) in &app.outlines {
 | 
				
			||||||
@ -19,7 +16,6 @@ pub fn draw_geometries_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			|||||||
        for path in geo.paths().iter() {
 | 
					        for path in geo.paths().iter() {
 | 
				
			||||||
            draw_on_plot_canvas(
 | 
					            draw_on_plot_canvas(
 | 
				
			||||||
                ui,
 | 
					                ui,
 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                PlotDrawer::Closure(&|ui| {
 | 
					                PlotDrawer::Closure(&|ui| {
 | 
				
			||||||
                    // draw outline path
 | 
					                    // draw outline path
 | 
				
			||||||
                    let mut points = path
 | 
					                    let mut points = path
 | 
				
			||||||
@ -40,7 +36,7 @@ pub fn draw_geometries_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			|||||||
                    points.push(points[1]);
 | 
					                    points.push(points[1]);
 | 
				
			||||||
                    let line = Line::new(PlotPoints::from(points))
 | 
					                    let line = Line::new(PlotPoints::from(points))
 | 
				
			||||||
                        .width(width)
 | 
					                        .width(width)
 | 
				
			||||||
                        .color(CanvasColour::Outline.to_colour32(selected));
 | 
					                        .color(CanvasColour::Outline.as_colour32(selected));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    ui.line(line)
 | 
					                    ui.line(line)
 | 
				
			||||||
                }),
 | 
					                }),
 | 
				
			||||||
@ -51,99 +47,11 @@ pub fn draw_geometries_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			|||||||
        for point in geo.points().iter() {
 | 
					        for point in geo.points().iter() {
 | 
				
			||||||
            draw_on_plot_canvas(
 | 
					            draw_on_plot_canvas(
 | 
				
			||||||
                ui,
 | 
					                ui,
 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                PlotDrawer::Closure(&|ui| {
 | 
					                PlotDrawer::Closure(&|ui| {
 | 
				
			||||||
                    let circle = Circle::new(*point, geo.stroke.into(), None);
 | 
					                    let circle = Circle::new(*point, geo.stroke.into(), None);
 | 
				
			||||||
                    circle.draw_egui_plot(ui, CanvasColour::Outline, selected);
 | 
					                    circle.draw_egui_plot(ui, CanvasColour::Outline, selected);
 | 
				
			||||||
                }),
 | 
					                }),
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            //     draw_floating_area_on_canvas(
 | 
					 | 
				
			||||||
            //         ui,
 | 
					 | 
				
			||||||
            //         app,
 | 
					 | 
				
			||||||
            //         point,
 | 
					 | 
				
			||||||
            //         ("GeometryAreaPoint", name, i),
 | 
					 | 
				
			||||||
            //         Drawer::Closure(&|ui| {
 | 
					 | 
				
			||||||
            //             // draw point shape
 | 
					 | 
				
			||||||
            //             ui.painter().add(CircleShape::filled(
 | 
					 | 
				
			||||||
            //                 point.invert_y().into(),
 | 
					 | 
				
			||||||
            //                 geo.stroke,
 | 
					 | 
				
			||||||
            //                 if selected {
 | 
					 | 
				
			||||||
            //                     Color32::DARK_BLUE.gamma_multiply(0.5)
 | 
					 | 
				
			||||||
            //                 } else {
 | 
					 | 
				
			||||||
            //                     Color32::DARK_BLUE
 | 
					 | 
				
			||||||
            //                 },
 | 
					 | 
				
			||||||
            //             ));
 | 
					 | 
				
			||||||
            //         }),
 | 
					 | 
				
			||||||
            //     );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // for circle in excellon.holes.iter() {
 | 
					 | 
				
			||||||
    //     draw_on_plot_canvas(
 | 
					 | 
				
			||||||
    //         ui,
 | 
					 | 
				
			||||||
    //         app,
 | 
					 | 
				
			||||||
    //         PlotDrawer::Drawable((
 | 
					 | 
				
			||||||
    //             &Element::Circle(circle.to_owned()),
 | 
					 | 
				
			||||||
    //             CanvasColour::Excellon,
 | 
					 | 
				
			||||||
    //             selected,
 | 
					 | 
				
			||||||
    //         )),
 | 
					 | 
				
			||||||
    //     );
 | 
					 | 
				
			||||||
    // }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub fn draw_geometries(ui: &mut Ui, app: &mut Application) {
 | 
					 | 
				
			||||||
    for (name, geo) in &app.outlines {
 | 
					 | 
				
			||||||
        let selected = &app.selection == name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // draw outline path
 | 
					 | 
				
			||||||
        for (i, path) in geo.paths().iter().enumerate() {
 | 
					 | 
				
			||||||
            draw_floating_area_on_canvas(
 | 
					 | 
				
			||||||
                ui,
 | 
					 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    let origin = path.iter().next().unwrap();
 | 
					 | 
				
			||||||
                    Pos2::new(origin.x() as f32, origin.y() as f32)
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                ("GeometryAreaOutline", name, i),
 | 
					 | 
				
			||||||
                Drawer::Closure(&|ui| {
 | 
					 | 
				
			||||||
                    // draw outline path
 | 
					 | 
				
			||||||
                    ui.painter().add(PathShape::closed_line(
 | 
					 | 
				
			||||||
                        path.iter()
 | 
					 | 
				
			||||||
                            .map(|v| Pos2::new(v.x() as f32, -v.y() as f32))
 | 
					 | 
				
			||||||
                            .collect::<Vec<Pos2>>(),
 | 
					 | 
				
			||||||
                        PathStroke::new(
 | 
					 | 
				
			||||||
                            geo.stroke,
 | 
					 | 
				
			||||||
                            if selected {
 | 
					 | 
				
			||||||
                                Color32::DARK_BLUE.gamma_multiply(0.5)
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                Color32::DARK_BLUE
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                    ));
 | 
					 | 
				
			||||||
                }),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // draw point shapes
 | 
					 | 
				
			||||||
        for (i, point) in geo.points().iter().enumerate() {
 | 
					 | 
				
			||||||
            draw_floating_area_on_canvas(
 | 
					 | 
				
			||||||
                ui,
 | 
					 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                point,
 | 
					 | 
				
			||||||
                ("GeometryAreaPoint", name, i),
 | 
					 | 
				
			||||||
                Drawer::Closure(&|ui| {
 | 
					 | 
				
			||||||
                    // draw point shape
 | 
					 | 
				
			||||||
                    ui.painter().add(CircleShape::filled(
 | 
					 | 
				
			||||||
                        point.invert_y().into(),
 | 
					 | 
				
			||||||
                        geo.stroke,
 | 
					 | 
				
			||||||
                        if selected {
 | 
					 | 
				
			||||||
                            Color32::DARK_BLUE.gamma_multiply(0.5)
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            Color32::DARK_BLUE
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                    ));
 | 
					 | 
				
			||||||
                }),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,11 @@
 | 
				
			|||||||
use eframe::{
 | 
					 | 
				
			||||||
    egui::{Color32, Pos2, Ui},
 | 
					 | 
				
			||||||
    epaint::{PathShape, PathStroke},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use egui_plot::{Line, PlotPoints, PlotUi};
 | 
					use egui_plot::{Line, PlotPoints, PlotUi};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{application::Application, geometry::DrawableRaw};
 | 
					use crate::{
 | 
				
			||||||
 | 
					    application::Application,
 | 
				
			||||||
 | 
					    geometry::{ClipperPaths, DrawableRaw},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{draw_floating_area_on_canvas, draw_on_plot_canvas, CanvasColour, Drawer, PlotDrawer};
 | 
					use super::{draw_on_plot_canvas, CanvasColour, PlotDrawer};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn draw_gerbers_(ui: &mut PlotUi, app: &mut Application) {
 | 
					pub fn draw_gerbers_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			||||||
    for (file_name, (_, geo)) in &app.gerbers {
 | 
					    for (file_name, (_, geo)) in &app.gerbers {
 | 
				
			||||||
@ -16,7 +15,6 @@ pub fn draw_gerbers_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			|||||||
        for geometry in geo.apertures.iter() {
 | 
					        for geometry in geo.apertures.iter() {
 | 
				
			||||||
            draw_on_plot_canvas(
 | 
					            draw_on_plot_canvas(
 | 
				
			||||||
                ui,
 | 
					                ui,
 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                PlotDrawer::Drawable((geometry, CanvasColour::Copper, selected)),
 | 
					                PlotDrawer::Drawable((geometry, CanvasColour::Copper, selected)),
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -25,13 +23,25 @@ pub fn draw_gerbers_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			|||||||
        for line in geo.paths.iter() {
 | 
					        for line in geo.paths.iter() {
 | 
				
			||||||
            draw_on_plot_canvas(
 | 
					            draw_on_plot_canvas(
 | 
				
			||||||
                ui,
 | 
					                ui,
 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                PlotDrawer::Closure(&|ui| line.draw_egui_plot(ui, CanvasColour::Copper, selected)),
 | 
					                PlotDrawer::Closure(&|ui| line.draw_egui_plot(ui, CanvasColour::Copper, selected)),
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // draw outline union path
 | 
					        // draw conductor outlines
 | 
				
			||||||
        for path in geo.outline_union.iter() {
 | 
					        for net in &geo.united_nets.conductors {
 | 
				
			||||||
 | 
					            draw_paths_outline(ui, &net.outline, selected);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // draw isolated aperture outlines
 | 
				
			||||||
 | 
					        for element in &geo.united_nets.isolated_apertures {
 | 
				
			||||||
 | 
					            draw_paths_outline(ui, &element.to_paths(), selected);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn draw_paths_outline(ui: &mut PlotUi, paths: &ClipperPaths, selected: bool) {
 | 
				
			||||||
 | 
					    for path in paths.iter() {
 | 
				
			||||||
 | 
					        if !path.is_empty() {
 | 
				
			||||||
            let mut points = path
 | 
					            let mut points = path
 | 
				
			||||||
                .iter()
 | 
					                .iter()
 | 
				
			||||||
                .map(|p| [p.x(), p.y()])
 | 
					                .map(|p| [p.x(), p.y()])
 | 
				
			||||||
@ -39,63 +49,9 @@ pub fn draw_gerbers_(ui: &mut PlotUi, app: &mut Application) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            points.push(points[0]);
 | 
					            points.push(points[0]);
 | 
				
			||||||
            let line = Line::new(PlotPoints::from(points))
 | 
					            let line = Line::new(PlotPoints::from(points))
 | 
				
			||||||
                .color(CanvasColour::CopperOutline.to_colour32(selected));
 | 
					                .color(CanvasColour::CopperOutline.as_colour32(selected));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ui.line(line)
 | 
					            ui.line(line)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
pub fn draw_gerbers(ui: &mut Ui, app: &mut Application) {
 | 
					 | 
				
			||||||
    for (file_name, (name, geo)) in &app.gerbers {
 | 
					 | 
				
			||||||
        let selected = &app.selection == file_name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (i, geometry) in geo.apertures.iter().enumerate() {
 | 
					 | 
				
			||||||
            draw_floating_area_on_canvas(
 | 
					 | 
				
			||||||
                ui,
 | 
					 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                geometry.canvas_pos(),
 | 
					 | 
				
			||||||
                ("GerberArea", name, i),
 | 
					 | 
				
			||||||
                Drawer::Drawable((geometry, selected)),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (i, line) in geo.paths.iter().enumerate() {
 | 
					 | 
				
			||||||
            draw_floating_area_on_canvas(
 | 
					 | 
				
			||||||
                ui,
 | 
					 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                line.canvas_pos(),
 | 
					 | 
				
			||||||
                ("LinePath", name, i),
 | 
					 | 
				
			||||||
                Drawer::Closure(&|ui| line.draw_egui(ui, selected)),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // draw union path
 | 
					 | 
				
			||||||
        for (i, path) in geo.outline_union.iter().enumerate() {
 | 
					 | 
				
			||||||
            draw_floating_area_on_canvas(
 | 
					 | 
				
			||||||
                ui,
 | 
					 | 
				
			||||||
                app,
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    let origin = path.iter().next().unwrap();
 | 
					 | 
				
			||||||
                    Pos2::new(origin.x() as f32, origin.y() as f32)
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                ("UnionOutline", name, i),
 | 
					 | 
				
			||||||
                Drawer::Closure(&|ui| {
 | 
					 | 
				
			||||||
                    ui.painter().add(PathShape::closed_line(
 | 
					 | 
				
			||||||
                        path.iter()
 | 
					 | 
				
			||||||
                            .map(|v| Pos2::new(v.x() as f32, -v.y() as f32))
 | 
					 | 
				
			||||||
                            .collect::<Vec<Pos2>>(),
 | 
					 | 
				
			||||||
                        PathStroke::new(
 | 
					 | 
				
			||||||
                            0.1_f32,
 | 
					 | 
				
			||||||
                            if selected {
 | 
					 | 
				
			||||||
                                Color32::LIGHT_RED
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                Color32::BLACK
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                    ));
 | 
					 | 
				
			||||||
                }),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,63 +0,0 @@
 | 
				
			|||||||
use eframe::egui::{self, Color32, Pos2, TextWrapMode, Ui, Vec2};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use crate::application::Application;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub fn draw_live_position(ui: &mut Ui, app: &mut Application) {
 | 
					 | 
				
			||||||
    if let Some(cursor) = cursor_canvas_position(app, ui) {
 | 
					 | 
				
			||||||
        let _id = egui::Area::new(app.canvas.0.with("cursor_position_box"))
 | 
					 | 
				
			||||||
            .fixed_pos(app.canvas.1.min)
 | 
					 | 
				
			||||||
            // .order(egui::Order::Middle)
 | 
					 | 
				
			||||||
            .default_size(Vec2::new(80., 80.))
 | 
					 | 
				
			||||||
            .show(ui.ctx(), |ui| {
 | 
					 | 
				
			||||||
                // let painter = ui.painter();
 | 
					 | 
				
			||||||
                // painter.add(RectShape::filled(Rect::from_min_size(rect.min, Vec2::new(80., 80.)), Rounding::ZERO, Color32::LIGHT_BLUE));
 | 
					 | 
				
			||||||
                egui::Frame::default()
 | 
					 | 
				
			||||||
                    .rounding(egui::Rounding::same(4.0))
 | 
					 | 
				
			||||||
                    .inner_margin(egui::Margin::same(8.0))
 | 
					 | 
				
			||||||
                    .stroke(ui.ctx().style().visuals.window_stroke)
 | 
					 | 
				
			||||||
                    .fill(Color32::from_rgba_premultiplied(0xAD, 0xD8, 0xE6, 200))
 | 
					 | 
				
			||||||
                    .show(ui, |ui| {
 | 
					 | 
				
			||||||
                        ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
 | 
					 | 
				
			||||||
                        let cursor_position = app.transform.inverse() * cursor; // + rect.min.to_vec2() - app.canvas_size.min;
 | 
					 | 
				
			||||||
                        let cursor_position2 = app.test_transform.inverse() * cursor; // + rect.min.to_vec2() - app.canvas_size.min;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        ui.label(format!(
 | 
					 | 
				
			||||||
                            "x: {} {}",
 | 
					 | 
				
			||||||
                            (-app.transform.translation.x + cursor.x) / app.transform.scaling,
 | 
					 | 
				
			||||||
                            app.variables.units
 | 
					 | 
				
			||||||
                        ));
 | 
					 | 
				
			||||||
                        ui.label(format!(
 | 
					 | 
				
			||||||
                            "y: {} {}",
 | 
					 | 
				
			||||||
                            // cursor.y / app.transform.scaling + app.test_transform.translation.y,
 | 
					 | 
				
			||||||
                            (-app.transform.translation.y + cursor.y) / app.transform.scaling,
 | 
					 | 
				
			||||||
                            app.variables.units
 | 
					 | 
				
			||||||
                        ));
 | 
					 | 
				
			||||||
                        ui.label(format!(
 | 
					 | 
				
			||||||
                            "cursor: {:?} {}",
 | 
					 | 
				
			||||||
                            cursor_position, app.variables.units
 | 
					 | 
				
			||||||
                        ));
 | 
					 | 
				
			||||||
                        ui.label(format!(
 | 
					 | 
				
			||||||
                            "cursor2: {:?} {}",
 | 
					 | 
				
			||||||
                            cursor_position2, app.variables.units
 | 
					 | 
				
			||||||
                        ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        ui.label(format!("{:?} - {:?}", app.transform, app.test_transform))
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn cursor_canvas_position(app: &Application, ui: &mut Ui) -> Option<Pos2> {
 | 
					 | 
				
			||||||
    let pointer_pos = ui.ctx().input(|i| i.pointer.hover_pos());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if let Some(cursor) = pointer_pos {
 | 
					 | 
				
			||||||
        let pos = cursor - app.canvas.1.min;
 | 
					 | 
				
			||||||
        match (pos.x, pos.y) {
 | 
					 | 
				
			||||||
            (x, y) if x > 0. && y > 0. => Some(pos.to_pos2()),
 | 
					 | 
				
			||||||
            (_, _) => None,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        None
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // pointer_pos.map(|pos| (pos - app.canvas_size.min).to_pos2())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,19 +1,12 @@
 | 
				
			|||||||
pub mod excellons;
 | 
					pub mod excellons;
 | 
				
			||||||
pub mod geometries;
 | 
					pub mod geometries;
 | 
				
			||||||
pub mod gerbers;
 | 
					pub mod gerbers;
 | 
				
			||||||
mod live_position;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::hash::Hash;
 | 
					use eframe::egui::{Color32, Ui};
 | 
				
			||||||
 | 
					 | 
				
			||||||
use eframe::{
 | 
					 | 
				
			||||||
    egui::{self, Color32, Pos2, Rounding, Stroke, Ui},
 | 
					 | 
				
			||||||
    epaint::RectShape,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use egui_plot::PlotUi;
 | 
					use egui_plot::PlotUi;
 | 
				
			||||||
use excellons::{draw_excellons, draw_excellons_};
 | 
					use excellons::draw_excellons_;
 | 
				
			||||||
use geometries::{draw_geometries, draw_geometries_};
 | 
					use geometries::draw_geometries_;
 | 
				
			||||||
use gerbers::{draw_gerbers, draw_gerbers_};
 | 
					use gerbers::draw_gerbers_;
 | 
				
			||||||
use live_position::draw_live_position;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::geometry::elements::Element;
 | 
					use crate::geometry::elements::Element;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -28,6 +21,7 @@ const EXCELLON_COLOR_SELECTED: Color32 = Color32::from_rgba_premultiplied(65, 42
 | 
				
			|||||||
const OUTLINE_COLOR: Color32 = Color32::DARK_BLUE;
 | 
					const OUTLINE_COLOR: Color32 = Color32::DARK_BLUE;
 | 
				
			||||||
const OUTLINE_COLOR_SELECTED: Color32 = Color32::from_rgba_premultiplied(0, 0, 139, 128);
 | 
					const OUTLINE_COLOR_SELECTED: Color32 = Color32::from_rgba_premultiplied(0, 0, 139, 128);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
pub enum CanvasColour {
 | 
					pub enum CanvasColour {
 | 
				
			||||||
    Copper,
 | 
					    Copper,
 | 
				
			||||||
    CopperOutline,
 | 
					    CopperOutline,
 | 
				
			||||||
@ -36,7 +30,7 @@ pub enum CanvasColour {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl CanvasColour {
 | 
					impl CanvasColour {
 | 
				
			||||||
    pub fn to_colour32(&self, selected: bool) -> Color32 {
 | 
					    pub fn as_colour32(&self, selected: bool) -> Color32 {
 | 
				
			||||||
        match (self, selected) {
 | 
					        match (self, selected) {
 | 
				
			||||||
            (CanvasColour::Copper, true) => COPPER_COLOR_SELECTED,
 | 
					            (CanvasColour::Copper, true) => COPPER_COLOR_SELECTED,
 | 
				
			||||||
            (CanvasColour::Copper, false) => COPPER_COLOR,
 | 
					            (CanvasColour::Copper, false) => COPPER_COLOR,
 | 
				
			||||||
@ -59,48 +53,6 @@ pub fn draw_canvas(ui: &mut Ui, app: &mut Application) {
 | 
				
			|||||||
            draw_excellons_(plot_ui, app);
 | 
					            draw_excellons_(plot_ui, app);
 | 
				
			||||||
            draw_geometries_(plot_ui, app);
 | 
					            draw_geometries_(plot_ui, app);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // draw_live_position(ui, app);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // ui.painter().add(RectShape::stroke(
 | 
					 | 
				
			||||||
    //     app.canvas.1,
 | 
					 | 
				
			||||||
    //     Rounding::same(0.),
 | 
					 | 
				
			||||||
    //     Stroke::new(0.2, Color32::BLACK),
 | 
					 | 
				
			||||||
    // ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // draw_gerbers(ui, app);
 | 
					 | 
				
			||||||
    // draw_excellons(ui, app);
 | 
					 | 
				
			||||||
    // draw_geometries(ui, app);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub enum Drawer<'a> {
 | 
					 | 
				
			||||||
    Drawable((&'a Element, bool)),
 | 
					 | 
				
			||||||
    Closure(&'a dyn Fn(&mut Ui)),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn draw_floating_area_on_canvas(
 | 
					 | 
				
			||||||
    ui: &mut Ui,
 | 
					 | 
				
			||||||
    app: &Application,
 | 
					 | 
				
			||||||
    pos: impl Into<Pos2>,
 | 
					 | 
				
			||||||
    name: impl Hash,
 | 
					 | 
				
			||||||
    drawer: Drawer,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    let window_layer = ui.layer_id();
 | 
					 | 
				
			||||||
    let id = egui::Area::new(app.canvas.0.with(name))
 | 
					 | 
				
			||||||
        .current_pos(pos)
 | 
					 | 
				
			||||||
        // .order(egui::Order::Middle)
 | 
					 | 
				
			||||||
        .show(ui.ctx(), |ui| {
 | 
					 | 
				
			||||||
            ui.set_clip_rect(app.test_transform.inverse() * app.canvas.1);
 | 
					 | 
				
			||||||
            match drawer {
 | 
					 | 
				
			||||||
                Drawer::Drawable((t, selected)) => t.draw_egui(ui, selected),
 | 
					 | 
				
			||||||
                Drawer::Closure(fun) => fun(ui),
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .response
 | 
					 | 
				
			||||||
        .layer_id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ui.ctx().set_transform_layer(id, app.test_transform);
 | 
					 | 
				
			||||||
    ui.ctx().set_sublayer(window_layer, id);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub enum PlotDrawer<'a> {
 | 
					pub enum PlotDrawer<'a> {
 | 
				
			||||||
@ -108,7 +60,7 @@ pub enum PlotDrawer<'a> {
 | 
				
			|||||||
    Closure(&'a dyn Fn(&mut PlotUi)),
 | 
					    Closure(&'a dyn Fn(&mut PlotUi)),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn draw_on_plot_canvas(ui: &mut PlotUi, app: &Application, drawer: PlotDrawer) {
 | 
					fn draw_on_plot_canvas(ui: &mut PlotUi, drawer: PlotDrawer) {
 | 
				
			||||||
    match drawer {
 | 
					    match drawer {
 | 
				
			||||||
        PlotDrawer::Drawable((t, colour, selected)) => t.draw_egui_plot(ui, colour, selected),
 | 
					        PlotDrawer::Drawable((t, colour, selected)) => t.draw_egui_plot(ui, colour, selected),
 | 
				
			||||||
        PlotDrawer::Closure(fun) => fun(ui),
 | 
					        PlotDrawer::Closure(fun) => fun(ui),
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,4 @@
 | 
				
			|||||||
use eframe::{
 | 
					use eframe::egui;
 | 
				
			||||||
    egui::{self, Vec2},
 | 
					 | 
				
			||||||
    emath::TSTransform,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    canvas::draw_canvas,
 | 
					    canvas::draw_canvas,
 | 
				
			||||||
@ -9,230 +6,17 @@ use super::{
 | 
				
			|||||||
    Application,
 | 
					    Application,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl eframe::App for Application {
 | 
					impl<'a> eframe::App for Application<'a> {
 | 
				
			||||||
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
 | 
					    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
 | 
				
			||||||
        // let window_size = ctx.screen_rect().size();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        draw_header(ctx, self);
 | 
					        draw_header(ctx, self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        draw_sidebar(ctx, self);
 | 
					        draw_sidebar(ctx, self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        egui::CentralPanel::default().show(ctx, |ui| {
 | 
					        egui::CentralPanel::default().show(ctx, |ui| {
 | 
				
			||||||
            // ui.horizontal(|ui| {
 | 
					 | 
				
			||||||
            //     let name_label = ui.label("Your name: ");
 | 
					 | 
				
			||||||
            //     ui.text_edit_singleline(&mut self.name)
 | 
					 | 
				
			||||||
            //         .labelled_by(name_label.id);
 | 
					 | 
				
			||||||
            // });
 | 
					 | 
				
			||||||
            // ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age"));
 | 
					 | 
				
			||||||
            // if ui.button("Increment").clicked() {
 | 
					 | 
				
			||||||
            //     self.age += 1;
 | 
					 | 
				
			||||||
            // }
 | 
					 | 
				
			||||||
            // ui.label(format!("Hello '{}', age {}", self.name, self.age));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // // ui.image(egui::include_image!(
 | 
					 | 
				
			||||||
            // //     "../../../crates/egui/assets/ferris.png"
 | 
					 | 
				
			||||||
            // // ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let pointer_pos = ui.ctx().input(|i| i.pointer.hover_pos());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            draw_action_panel(ui, self);
 | 
					            draw_action_panel(ui, self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // ui.label(
 | 
					 | 
				
			||||||
            //     "Pan, zoom in, and zoom out with scrolling (see the plot demo for more instructions). \
 | 
					 | 
				
			||||||
            //            Double click on the background to reset.",
 | 
					 | 
				
			||||||
            // );
 | 
					 | 
				
			||||||
            // ui.vertical_centered(|ui| {
 | 
					 | 
				
			||||||
            //     ui.add(crate::egui_github_link_file!());
 | 
					 | 
				
			||||||
            // });
 | 
					 | 
				
			||||||
            // if let Some(pos) = pointer_pos {
 | 
					 | 
				
			||||||
            //     ui.label(format!("Translation: {:?}", self.transform.translation));
 | 
					 | 
				
			||||||
            //     ui.label(format!("ScLING: {}", self.transform.scaling));
 | 
					 | 
				
			||||||
            //     ui.label(format!("Canvas start: {}", self.canvas.1.min));
 | 
					 | 
				
			||||||
            // }
 | 
					 | 
				
			||||||
            ui.separator();
 | 
					            ui.separator();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // ui.allocate_ui(ui.available_size(), |ui| {
 | 
					 | 
				
			||||||
            //     egui::TopBottomPanel::bottom("BottomCanvas")
 | 
					 | 
				
			||||||
            //         .show_separator_line(false)
 | 
					 | 
				
			||||||
            //         .exact_height(15.)
 | 
					 | 
				
			||||||
            //         .show_inside(ui, |ui| {});
 | 
					 | 
				
			||||||
            //     egui::SidePanel::left("LeftCanvas")
 | 
					 | 
				
			||||||
            //         .show_separator_line(false)
 | 
					 | 
				
			||||||
            //         .exact_width(15.)
 | 
					 | 
				
			||||||
            //         .show_inside(ui, |ui| {});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //     egui::CentralPanel::default()
 | 
					 | 
				
			||||||
            //         .frame(egui::Frame::none().inner_margin(0.).outer_margin(0.))
 | 
					 | 
				
			||||||
            //         .show_inside(ui, |ui| {
 | 
					 | 
				
			||||||
            //             let sin: egui_plot::PlotPoints = (0..1000)
 | 
					 | 
				
			||||||
            //                 .map(|i| {
 | 
					 | 
				
			||||||
            //                     let x = i as f64 * 0.01;
 | 
					 | 
				
			||||||
            //                     [x, x.sin()]
 | 
					 | 
				
			||||||
            //                 })
 | 
					 | 
				
			||||||
            //                 .collect();
 | 
					 | 
				
			||||||
            //             let line = egui_plot::Line::new(sin);
 | 
					 | 
				
			||||||
            //             egui_plot::Plot::new("my_plot")
 | 
					 | 
				
			||||||
            //                 .view_aspect(2.0)
 | 
					 | 
				
			||||||
            //                 .data_aspect(1.0)
 | 
					 | 
				
			||||||
            //                 .show(ui, |plot_ui| {
 | 
					 | 
				
			||||||
            //                     super::canvas::gerbers::draw_gerbers_(plot_ui, self);
 | 
					 | 
				
			||||||
            //                     super::canvas::excellons::draw_excellons_(plot_ui, self);
 | 
					 | 
				
			||||||
            //                     super::canvas::geometries::draw_geometries_(plot_ui, self)
 | 
					 | 
				
			||||||
            //                     //         plot_ui.line(line);
 | 
					 | 
				
			||||||
            //                     //         for (_, (name, geo)) in &self.gerbers {
 | 
					 | 
				
			||||||
            //                     //             let path = geo.outline_union.iter().map(|path| {
 | 
					 | 
				
			||||||
            //                     //                 let mut points = path
 | 
					 | 
				
			||||||
            //                     //                     .iter()
 | 
					 | 
				
			||||||
            //                     //                     .map(|p| [p.x(), p.y()])
 | 
					 | 
				
			||||||
            //                     //                     .collect::<Vec<[f64; 2]>>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //                     //                 points.push(points[0]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //                     //                 (
 | 
					 | 
				
			||||||
            //                     //                     egui_plot::Line::new(egui_plot::PlotPoints::from(
 | 
					 | 
				
			||||||
            //                     //                         points.clone(),
 | 
					 | 
				
			||||||
            //                     //                     ))
 | 
					 | 
				
			||||||
            //                     //                     .color(egui::Color32::DARK_BLUE),
 | 
					 | 
				
			||||||
            //                     //                     egui_plot::Polygon::new(egui_plot::PlotPoints::from(
 | 
					 | 
				
			||||||
            //                     //                         points,
 | 
					 | 
				
			||||||
            //                     //                     ))
 | 
					 | 
				
			||||||
            //                     //                     .fill_color(egui::Color32::LIGHT_GREEN),
 | 
					 | 
				
			||||||
            //                     //                 )
 | 
					 | 
				
			||||||
            //                     //             });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //                     //             for line in path {
 | 
					 | 
				
			||||||
            //                     //                 plot_ui.line(line.0);
 | 
					 | 
				
			||||||
            //                     //                 plot_ui.polygon(line.1);
 | 
					 | 
				
			||||||
            //                     //             }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //                     //             let circle_points: egui_plot::PlotPoints = (0..=400)
 | 
					 | 
				
			||||||
            //                     //                 .map(|i| {
 | 
					 | 
				
			||||||
            //                     //                     let t = egui::remap(
 | 
					 | 
				
			||||||
            //                     //                         i as f64,
 | 
					 | 
				
			||||||
            //                     //                         0.0..=(400 as f64),
 | 
					 | 
				
			||||||
            //                     //                         0.0..=std::f64::consts::TAU,
 | 
					 | 
				
			||||||
            //                     //                     );
 | 
					 | 
				
			||||||
            //                     //                     let r = 10.;
 | 
					 | 
				
			||||||
            //                     //                     [r * t.cos() + 20. as f64, r * t.sin() + 20. as f64]
 | 
					 | 
				
			||||||
            //                     //                 })
 | 
					 | 
				
			||||||
            //                     //                 .collect();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //                     //             let poly = egui_plot::Polygon::new(
 | 
					 | 
				
			||||||
            //                     //                 egui_plot::PlotPoints::from(circle_points),
 | 
					 | 
				
			||||||
            //                     //             )
 | 
					 | 
				
			||||||
            //                     //             .fill_color(egui::Color32::LIGHT_GREEN);
 | 
					 | 
				
			||||||
            //                     //             plot_ui.polygon(poly);
 | 
					 | 
				
			||||||
            //                     //         }
 | 
					 | 
				
			||||||
            //                 });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //             // let (id, rect) = ui.allocate_space(ui.available_size());
 | 
					 | 
				
			||||||
            //             // let response = ui.interact(rect, id, egui::Sense::click_and_drag());
 | 
					 | 
				
			||||||
            //             // self.canvas = (id, rect);
 | 
					 | 
				
			||||||
            //             // // Allow dragging the background as well.
 | 
					 | 
				
			||||||
            //             // if response.dragged() {
 | 
					 | 
				
			||||||
            //             //     self.transform.translation += response.drag_delta();
 | 
					 | 
				
			||||||
            //             // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //             // // Plot-like reset
 | 
					 | 
				
			||||||
            //             // if response.double_clicked() {
 | 
					 | 
				
			||||||
            //             //     self.transform = TSTransform::default();
 | 
					 | 
				
			||||||
            //             //     self.transform = TSTransform::from_translation(Vec2::new(
 | 
					 | 
				
			||||||
            //             //         rect.width() / 2.,
 | 
					 | 
				
			||||||
            //             //         rect.height() / 2.,
 | 
					 | 
				
			||||||
            //             //     ));
 | 
					 | 
				
			||||||
            //             // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //             // let transform =
 | 
					 | 
				
			||||||
            //             //     TSTransform::from_translation(ui.min_rect().left_top().to_vec2())
 | 
					 | 
				
			||||||
            //             //         * self.transform;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //             // if let Some(pointer) = ui.ctx().input(|i| i.pointer.hover_pos()) {
 | 
					 | 
				
			||||||
            //             //     // Note: doesn't catch zooming / panning if a button in this PanZoom container is hovered.
 | 
					 | 
				
			||||||
            //             //     if response.hovered() {
 | 
					 | 
				
			||||||
            //             //         let pointer_in_layer = transform.inverse() * pointer;
 | 
					 | 
				
			||||||
            //             //         let zoom_delta = ui.ctx().input(|i| i.zoom_delta());
 | 
					 | 
				
			||||||
            //             //         let pan_delta = ui.ctx().input(|i| i.smooth_scroll_delta);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //             //         // Zoom in on pointer:
 | 
					 | 
				
			||||||
            //             //         self.transform = self.transform
 | 
					 | 
				
			||||||
            //             //             * TSTransform::from_translation(pointer_in_layer.to_vec2())
 | 
					 | 
				
			||||||
            //             //             * TSTransform::from_scaling(zoom_delta)
 | 
					 | 
				
			||||||
            //             //             * TSTransform::from_translation(-pointer_in_layer.to_vec2());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //             //         // Pan:
 | 
					 | 
				
			||||||
            //             //         self.transform =
 | 
					 | 
				
			||||||
            //             //             TSTransform::from_translation(pan_delta) * self.transform;
 | 
					 | 
				
			||||||
            //             //     }
 | 
					 | 
				
			||||||
            //             // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //             // self.test_transform = transform;
 | 
					 | 
				
			||||||
            //         })
 | 
					 | 
				
			||||||
            // });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // let (id, rect) = ui.allocate_space(Vec2::new(ui.available_width(), 15.));
 | 
					 | 
				
			||||||
            // ui.painter().add(RectShape::stroke(
 | 
					 | 
				
			||||||
            //     rect,
 | 
					 | 
				
			||||||
            //     Rounding::same(0.),
 | 
					 | 
				
			||||||
            //     egui::Stroke::new(0.2, Color32::BLACK),
 | 
					 | 
				
			||||||
            // ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // let (id, rect) = ui.allocate_space(Vec2::new(10., ui.available_height()));
 | 
					 | 
				
			||||||
            // ui.painter().add(RectShape::stroke(
 | 
					 | 
				
			||||||
            //     rect,
 | 
					 | 
				
			||||||
            //     Rounding::same(0.),
 | 
					 | 
				
			||||||
            //     egui::Stroke::new(0.2, Color32::BLACK),
 | 
					 | 
				
			||||||
            // ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // let (id, rect) = ui.allocate_space(ui.available_size());
 | 
					 | 
				
			||||||
            // let response = ui.interact(rect, id, egui::Sense::click_and_drag());
 | 
					 | 
				
			||||||
            // self.canvas = (id, rect);
 | 
					 | 
				
			||||||
            // // Allow dragging the background as well.
 | 
					 | 
				
			||||||
            // if response.dragged() {
 | 
					 | 
				
			||||||
            //     self.transform.translation += response.drag_delta();
 | 
					 | 
				
			||||||
            // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // // Plot-like reset
 | 
					 | 
				
			||||||
            // if response.double_clicked() {
 | 
					 | 
				
			||||||
            //     self.transform = TSTransform::default();
 | 
					 | 
				
			||||||
            //     self.transform =
 | 
					 | 
				
			||||||
            //         TSTransform::from_translation(Vec2::new(rect.width() / 2., rect.height() / 2.));
 | 
					 | 
				
			||||||
            // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // let transform =
 | 
					 | 
				
			||||||
            //     TSTransform::from_translation(ui.min_rect().left_top().to_vec2()) * self.transform;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // if let Some(pointer) = ui.ctx().input(|i| i.pointer.hover_pos()) {
 | 
					 | 
				
			||||||
            //     // Note: doesn't catch zooming / panning if a button in this PanZoom container is hovered.
 | 
					 | 
				
			||||||
            //     if response.hovered() {
 | 
					 | 
				
			||||||
            //         let pointer_in_layer = transform.inverse() * pointer;
 | 
					 | 
				
			||||||
            //         let zoom_delta = ui.ctx().input(|i| i.zoom_delta());
 | 
					 | 
				
			||||||
            //         let pan_delta = ui.ctx().input(|i| i.smooth_scroll_delta);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //         // Zoom in on pointer:
 | 
					 | 
				
			||||||
            //         self.transform = self.transform
 | 
					 | 
				
			||||||
            //             * TSTransform::from_translation(pointer_in_layer.to_vec2())
 | 
					 | 
				
			||||||
            //             * TSTransform::from_scaling(zoom_delta)
 | 
					 | 
				
			||||||
            //             * TSTransform::from_translation(-pointer_in_layer.to_vec2());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //         // Pan:
 | 
					 | 
				
			||||||
            //         self.transform = TSTransform::from_translation(pan_delta) * self.transform;
 | 
					 | 
				
			||||||
            //     }
 | 
					 | 
				
			||||||
            // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // self.test_transform = transform;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // let p = ui.painter_at(rect);
 | 
					 | 
				
			||||||
            // p.add(RectShape::filled(
 | 
					 | 
				
			||||||
            //     Rect::from_center_size(rect.center(), Vec2::new(rect.width(), 1.)),
 | 
					 | 
				
			||||||
            //     Rounding::ZERO,
 | 
					 | 
				
			||||||
            //     Color32::LIGHT_RED,
 | 
					 | 
				
			||||||
            // ));
 | 
					 | 
				
			||||||
            // p.add(RectShape::filled(
 | 
					 | 
				
			||||||
            //     Rect::from_center_size(rect.center(), Vec2::new(1., rect.height())),
 | 
					 | 
				
			||||||
            //     Rounding::ZERO,
 | 
					 | 
				
			||||||
            //     Color32::LIGHT_RED,
 | 
					 | 
				
			||||||
            // ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            draw_canvas(ui, self);
 | 
					            draw_canvas(ui, self);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								src/application/errors.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/application/errors.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use error_stack::{Context, Report};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct FileError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl FileError {
 | 
				
			||||||
 | 
					    pub fn new(text: &str) -> Report<Self> {
 | 
				
			||||||
 | 
					        Report::new(Self).attach_printable(text.to_string())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for FileError {
 | 
				
			||||||
 | 
					    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        fmt.write_str("Error opening file")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Context for FileError {}
 | 
				
			||||||
@ -1,30 +1,24 @@
 | 
				
			|||||||
mod canvas;
 | 
					mod canvas;
 | 
				
			||||||
mod egui;
 | 
					mod egui;
 | 
				
			||||||
 | 
					mod errors;
 | 
				
			||||||
pub mod panels;
 | 
					pub mod panels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use eframe::{
 | 
					 | 
				
			||||||
    egui::{Id, Pos2, Rect, Vec2},
 | 
					 | 
				
			||||||
    emath::TSTransform,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    excellon::drills::Drills,
 | 
					    excellon::drills::Drills,
 | 
				
			||||||
    geometry::{Geometry, Unit},
 | 
					    geometry::{Geometry, Unit},
 | 
				
			||||||
    outline_geometry::OutlineGeometry,
 | 
					    outline_geometry::OutlineGeometry,
 | 
				
			||||||
 | 
					    resources::ResourceLoader,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use canvas::CanvasColour;
 | 
					pub use canvas::CanvasColour;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Application {
 | 
					pub struct Application<'a> {
 | 
				
			||||||
 | 
					    resources: ResourceLoader<'a>,
 | 
				
			||||||
    gerbers: HashMap<String, (String, Geometry)>,
 | 
					    gerbers: HashMap<String, (String, Geometry)>,
 | 
				
			||||||
    outlines: HashMap<String, OutlineGeometry>,
 | 
					    outlines: HashMap<String, OutlineGeometry>,
 | 
				
			||||||
    excellons: HashMap<String, (String, Drills)>,
 | 
					    excellons: HashMap<String, (String, Drills)>,
 | 
				
			||||||
    // geometry: Geometry,
 | 
					 | 
				
			||||||
    transform: TSTransform,
 | 
					 | 
				
			||||||
    test_transform: TSTransform,
 | 
					 | 
				
			||||||
    canvas: (Id, Rect),
 | 
					 | 
				
			||||||
    selection: String,
 | 
					    selection: String,
 | 
				
			||||||
    variables: Variables,
 | 
					    variables: Variables,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -35,19 +29,13 @@ pub struct Variables {
 | 
				
			|||||||
    units: Unit,
 | 
					    units: Unit,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Application {
 | 
					impl<'a> Application<'a> {
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
 | 
					            resources: ResourceLoader::new(),
 | 
				
			||||||
            gerbers: HashMap::new(),
 | 
					            gerbers: HashMap::new(),
 | 
				
			||||||
            outlines: HashMap::new(),
 | 
					            outlines: HashMap::new(),
 | 
				
			||||||
            excellons: HashMap::new(),
 | 
					            excellons: HashMap::new(),
 | 
				
			||||||
            // geometry,
 | 
					 | 
				
			||||||
            transform: TSTransform::default(),
 | 
					 | 
				
			||||||
            test_transform: TSTransform::default(),
 | 
					 | 
				
			||||||
            canvas: (
 | 
					 | 
				
			||||||
                Id::new("0"),
 | 
					 | 
				
			||||||
                Rect::from_center_size(Pos2::new(0., 0.), Vec2::default()),
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            selection: "".into(),
 | 
					            selection: "".into(),
 | 
				
			||||||
            variables: Variables::default(),
 | 
					            variables: Variables::default(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,51 +1,51 @@
 | 
				
			|||||||
 | 
					use std::collections::hash_map::Entry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use eframe::egui::Ui;
 | 
					use eframe::egui::Ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{application::Application, geometry::ClipperPath, outline_geometry::OutlineGeometry};
 | 
				
			||||||
    application::Application,
 | 
					 | 
				
			||||||
    geometry::{ClipperPath, ClipperPaths, DrawableRaw},
 | 
					 | 
				
			||||||
    outline_geometry::OutlineGeometry,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::HOLE_MARK_DIAMETER;
 | 
					use super::HOLE_MARK_DIAMETER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn show_excellon_actions(ui: &mut Ui, app: &mut Application) {
 | 
					pub fn show_excellon_actions(ui: &mut Ui, app: &mut Application) {
 | 
				
			||||||
    if let Some((name, drill)) = app.excellons.get(&app.selection) {
 | 
					    if app.excellons.contains_key(&app.selection) {
 | 
				
			||||||
        if ui.button("Generate cut out").clicked() {
 | 
					        match app.excellons.entry(app.selection.to_string()) {
 | 
				
			||||||
            let path = drill
 | 
					            Entry::Occupied(c) => {
 | 
				
			||||||
                .holes
 | 
					                let mut deletion_request = false;
 | 
				
			||||||
                .iter()
 | 
					                let (name, drill) = c.get();
 | 
				
			||||||
                .map(|hole| hole.outline.clone())
 | 
					                if ui.button("Delete").clicked() {
 | 
				
			||||||
                .collect::<Vec<ClipperPath>>()
 | 
					                    deletion_request = true;
 | 
				
			||||||
                .into();
 | 
					                }
 | 
				
			||||||
            app.outlines.insert(
 | 
					                ui.horizontal(|ui| {
 | 
				
			||||||
                format!("{name}-CutOut"),
 | 
					                    if ui.button("Generate cut out").clicked() {
 | 
				
			||||||
                OutlineGeometry::new_no_inflate(
 | 
					                        let path = drill
 | 
				
			||||||
                    &path,
 | 
					 | 
				
			||||||
                    0.1,
 | 
					 | 
				
			||||||
                    app.variables.units,
 | 
					 | 
				
			||||||
                    name,
 | 
					 | 
				
			||||||
                    path.bounds(),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if ui.button("Generate hole mark").clicked() {
 | 
					 | 
				
			||||||
            app.outlines.insert(
 | 
					 | 
				
			||||||
                format!("{name}-HoleMark"),
 | 
					 | 
				
			||||||
                OutlineGeometry::point_marker(
 | 
					 | 
				
			||||||
                    drill.holes.iter().map(|c| c.canvas_pos()).collect(),
 | 
					 | 
				
			||||||
                    HOLE_MARK_DIAMETER,
 | 
					 | 
				
			||||||
                    app.variables.units,
 | 
					 | 
				
			||||||
                    name,
 | 
					 | 
				
			||||||
                    ClipperPaths::from(
 | 
					 | 
				
			||||||
                        drill
 | 
					 | 
				
			||||||
                            .holes
 | 
					                            .holes
 | 
				
			||||||
                            .iter()
 | 
					                            .iter()
 | 
				
			||||||
                            .map(|hole| hole.outline.clone())
 | 
					                            .map(|hole| hole.outline.clone())
 | 
				
			||||||
                            .collect::<Vec<ClipperPath>>(),
 | 
					                            .collect::<Vec<ClipperPath>>()
 | 
				
			||||||
                    )
 | 
					                            .into();
 | 
				
			||||||
                    .bounds(),
 | 
					                        app.outlines.insert(
 | 
				
			||||||
                ),
 | 
					                            format!("{name}-CutOut"),
 | 
				
			||||||
            );
 | 
					                            OutlineGeometry::new_no_inflate(&path, 0.1, app.variables.units, name),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if ui.button("Generate hole mark").clicked() {
 | 
				
			||||||
 | 
					                        app.outlines.insert(
 | 
				
			||||||
 | 
					                            format!("{name}-HoleMark"),
 | 
				
			||||||
 | 
					                            OutlineGeometry::drill_marker(
 | 
				
			||||||
 | 
					                                drill,
 | 
				
			||||||
 | 
					                                HOLE_MARK_DIAMETER,
 | 
				
			||||||
 | 
					                                app.variables.units,
 | 
				
			||||||
 | 
					                                name,
 | 
				
			||||||
 | 
					                            ),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if deletion_request {
 | 
				
			||||||
 | 
					                    c.remove();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Entry::Vacant(_) => unreachable!(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,34 +1,59 @@
 | 
				
			|||||||
 | 
					use std::collections::hash_map::Entry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use eframe::egui::{ComboBox, Ui};
 | 
					use eframe::egui::{ComboBox, Ui};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{application::Application, export::svg::SVGConverter};
 | 
					use crate::{application::Application, export::svg::SVGConverter};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn show_geometry_actions(ui: &mut Ui, app: &mut Application) {
 | 
					pub fn show_geometry_actions(ui: &mut Ui, app: &mut Application) {
 | 
				
			||||||
    if let Some(outline) = app.outlines.get_mut(&app.selection) {
 | 
					    if app.outlines.contains_key(&app.selection) {
 | 
				
			||||||
        ui.label("BoundingBox");
 | 
					        match app.outlines.entry(app.selection.to_string()) {
 | 
				
			||||||
        ComboBox::from_label("Select one!")
 | 
					            Entry::Occupied(mut c) => {
 | 
				
			||||||
            .selected_text(format!("{:?}", outline.bounds_from))
 | 
					                let mut deletion_request = false;
 | 
				
			||||||
            .show_ui(ui, |ui| {
 | 
					                let isolated_tracks = c.get_mut();
 | 
				
			||||||
                for (key, (name, _)) in app.gerbers.iter() {
 | 
					
 | 
				
			||||||
                    if ui
 | 
					                if ui.button("Delete").clicked() {
 | 
				
			||||||
                        .selectable_value(&mut outline.bounds_from, name.into(), name)
 | 
					                    deletion_request = true;
 | 
				
			||||||
                        .clicked()
 | 
					                }
 | 
				
			||||||
                    {
 | 
					
 | 
				
			||||||
                        if let Some((_, box_geo)) = app.gerbers.get(key) {
 | 
					                ui.horizontal(|ui| {
 | 
				
			||||||
                            outline.bounding_box = box_geo.outline_union.bounds();
 | 
					                    ui.label("BoundingBox");
 | 
				
			||||||
 | 
					                    let id = ui.make_persistent_id("BoundingBoxSelection");
 | 
				
			||||||
 | 
					                    ComboBox::new(id, "")
 | 
				
			||||||
 | 
					                        .selected_text(format!("{:?}", isolated_tracks.bounds_from))
 | 
				
			||||||
 | 
					                        .show_ui(ui, |ui| {
 | 
				
			||||||
 | 
					                            for (key, (name, _)) in app.gerbers.iter() {
 | 
				
			||||||
 | 
					                                if ui
 | 
				
			||||||
 | 
					                                    .selectable_value(
 | 
				
			||||||
 | 
					                                        &mut isolated_tracks.bounds_from,
 | 
				
			||||||
 | 
					                                        name.into(),
 | 
				
			||||||
 | 
					                                        name,
 | 
				
			||||||
 | 
					                                    )
 | 
				
			||||||
 | 
					                                    .clicked()
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    if let Some((_, box_geo)) = app.gerbers.get(key) {
 | 
				
			||||||
 | 
					                                        isolated_tracks.bounding_box = box_geo.united_nets.bounds();
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if ui.button("Save as SVG").clicked() {
 | 
				
			||||||
 | 
					                        if let Some(path) = rfd::FileDialog::new()
 | 
				
			||||||
 | 
					                            .set_title("Save as SVG")
 | 
				
			||||||
 | 
					                            .set_file_name(app.selection.to_string())
 | 
				
			||||||
 | 
					                            .add_filter("SVG", &["svg", "SVG"])
 | 
				
			||||||
 | 
					                            .save_file()
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            SVGConverter::export(isolated_tracks, &path);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                });
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ui.button("Save as SVG").clicked() {
 | 
					                if deletion_request {
 | 
				
			||||||
            if let Some(path) = rfd::FileDialog::new()
 | 
					                    c.remove();
 | 
				
			||||||
                .set_title("Save as SVG")
 | 
					                }
 | 
				
			||||||
                .set_file_name(app.selection.to_string())
 | 
					 | 
				
			||||||
                .add_filter("SVG", &["svg", "SVG"])
 | 
					 | 
				
			||||||
                .save_file()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                SVGConverter::export(outline, &path);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            Entry::Vacant(_) => unreachable!(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,28 +1,45 @@
 | 
				
			|||||||
 | 
					use std::collections::hash_map::Entry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use eframe::egui::{DragValue, Ui};
 | 
					use eframe::egui::{DragValue, Ui};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{application::Application, outline_geometry::OutlineGeometry};
 | 
					use crate::{application::Application, outline_geometry::OutlineGeometry};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn show_gerber_actions(ui: &mut Ui, app: &mut Application) {
 | 
					pub fn show_gerber_actions(ui: &mut Ui, app: &mut Application) {
 | 
				
			||||||
    if let Some((name, geo)) = app.gerbers.get(&app.selection) {
 | 
					    if app.gerbers.contains_key(&app.selection) {
 | 
				
			||||||
        ui.horizontal(|ui| {
 | 
					        match app.gerbers.entry(app.selection.to_string()) {
 | 
				
			||||||
            ui.add(
 | 
					            Entry::Occupied(c) => {
 | 
				
			||||||
                DragValue::new(&mut app.variables.laser_line_width)
 | 
					                let mut deletion_request = false;
 | 
				
			||||||
                    .range(0.1..=10.)
 | 
					                let (name, gerber) = c.get();
 | 
				
			||||||
                    .suffix(geo.units),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ui.button("Generate Isolation").clicked() {
 | 
					                if ui.button("Delete").clicked() {
 | 
				
			||||||
                app.outlines.insert(
 | 
					                    deletion_request = true;
 | 
				
			||||||
                    format!("{name}-Iso"),
 | 
					                }
 | 
				
			||||||
                    OutlineGeometry::new(
 | 
					                ui.horizontal(|ui| {
 | 
				
			||||||
                        &geo.outline_union,
 | 
					                    ui.add(
 | 
				
			||||||
                        app.variables.laser_line_width,
 | 
					                        DragValue::new(&mut app.variables.laser_line_width)
 | 
				
			||||||
                        geo.units,
 | 
					                            .range(0.1..=10.)
 | 
				
			||||||
                        name,
 | 
					                            .speed(0.1)
 | 
				
			||||||
                        geo.outline_union.bounds(),
 | 
					                            .suffix(gerber.units),
 | 
				
			||||||
                    ),
 | 
					                    );
 | 
				
			||||||
                );
 | 
					
 | 
				
			||||||
 | 
					                    if ui.button("Generate Isolation").clicked() {
 | 
				
			||||||
 | 
					                        app.outlines.insert(
 | 
				
			||||||
 | 
					                            format!("{name}-Iso"),
 | 
				
			||||||
 | 
					                            OutlineGeometry::new(
 | 
				
			||||||
 | 
					                                &gerber.united_nets,
 | 
				
			||||||
 | 
					                                app.variables.laser_line_width,
 | 
				
			||||||
 | 
					                                gerber.units,
 | 
				
			||||||
 | 
					                                name,
 | 
				
			||||||
 | 
					                            ),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if deletion_request {
 | 
				
			||||||
 | 
					                    c.remove();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					            Entry::Vacant(_) => unreachable!(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,62 +1,192 @@
 | 
				
			|||||||
use std::{fs::File, io::BufReader};
 | 
					use std::{
 | 
				
			||||||
 | 
					    fs::File,
 | 
				
			||||||
 | 
					    io::{BufReader, Read},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use eframe::egui;
 | 
					use eframe::egui;
 | 
				
			||||||
 | 
					use error_stack::{Report, ResultExt};
 | 
				
			||||||
 | 
					use tracing::error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    application::Application,
 | 
					    application::{errors::FileError, Application},
 | 
				
			||||||
    excellon::{drills::Drills, parse_excellon},
 | 
					    excellon::{doc::ExcellonDoc, drills::Drills, parse_excellon},
 | 
				
			||||||
    geometry::Geometry,
 | 
					    geometry::Geometry,
 | 
				
			||||||
    gerber::parse_gerber,
 | 
					    gerber::{doc::GerberDoc, parse_gerber},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const GERBER_EXTENSIONS: &[&str] = &["GBR ", "gbr", "GB", "geb"];
 | 
				
			||||||
 | 
					const EXCELLON_EXTENSIONS: &[&str] = &["DRL ", "drl"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn draw_header(ctx: &egui::Context, app: &mut Application) {
 | 
					pub fn draw_header(ctx: &egui::Context, app: &mut Application) {
 | 
				
			||||||
    egui::TopBottomPanel::top("top_panel")
 | 
					    egui::TopBottomPanel::top("top_panel")
 | 
				
			||||||
        .exact_height(40.)
 | 
					        .exact_height(40.)
 | 
				
			||||||
        .show(ctx, |ui| {
 | 
					        .show(ctx, |ui| {
 | 
				
			||||||
            ui.horizontal(|ui| {
 | 
					            ui.horizontal_centered(|ui| {
 | 
				
			||||||
                if ui.button("Open Gerber").clicked() {
 | 
					                if let Some(opened_files) = ui
 | 
				
			||||||
                    if let Some(paths) = rfd::FileDialog::new()
 | 
					                    .menu_button("📝 Open File", open_file_menu)
 | 
				
			||||||
                        .add_filter("Gerber", &["GBR ", "gbr", "GB", "geb"])
 | 
					                    .inner
 | 
				
			||||||
                        .pick_files()
 | 
					                    .flatten()
 | 
				
			||||||
                    {
 | 
					                {
 | 
				
			||||||
                        // self.picked_path = Some(path.display().to_string());
 | 
					                    match opened_files {
 | 
				
			||||||
                        for path in paths {
 | 
					                        Ok(files) => {
 | 
				
			||||||
                            // TODO remove all unwraps
 | 
					                            for (path, name, content) in files {
 | 
				
			||||||
                            if let Ok(file) = File::open(&path) {
 | 
					                                match content {
 | 
				
			||||||
                                let gerber = parse_gerber(BufReader::new(file));
 | 
					                                    AppFileContent::Gerber(gerber) => {
 | 
				
			||||||
                                let name = path.file_name().unwrap().to_str().unwrap().to_string();
 | 
					                                        app.gerbers.insert(
 | 
				
			||||||
                                app.gerbers.insert(
 | 
					                                            path,
 | 
				
			||||||
                                    path.to_str().unwrap().into(),
 | 
					                                            (
 | 
				
			||||||
                                    (name, Geometry::from(gerber).to_unit(app.variables.units)),
 | 
					                                                name,
 | 
				
			||||||
                                );
 | 
					                                                Geometry::from(gerber)
 | 
				
			||||||
                            } else {
 | 
					                                                    .into_unit(app.variables.units),
 | 
				
			||||||
                                // TODO show error
 | 
					                                            ),
 | 
				
			||||||
                            };
 | 
					                                        );
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                    AppFileContent::Excellon(excellon) => {
 | 
				
			||||||
 | 
					                                        let drills: Drills = excellon.into();
 | 
				
			||||||
 | 
					                                        app.excellons.insert(
 | 
				
			||||||
 | 
					                                            path,
 | 
				
			||||||
 | 
					                                            (name, drills.into_unit(app.variables.units)),
 | 
				
			||||||
 | 
					                                        );
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        Err(e) => {
 | 
				
			||||||
 | 
					                            // TODO Show error
 | 
				
			||||||
 | 
					                            error!("{e:?}");
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                };
 | 
				
			||||||
 | 
					 | 
				
			||||||
                if ui.button("Open Excellon").clicked() {
 | 
					 | 
				
			||||||
                    if let Some(paths) = rfd::FileDialog::new()
 | 
					 | 
				
			||||||
                        .add_filter("Excellon", &["DRL ", "drl"])
 | 
					 | 
				
			||||||
                        .pick_files()
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        for path in paths {
 | 
					 | 
				
			||||||
                            // TODO remove all unwraps
 | 
					 | 
				
			||||||
                            if let Ok(file) = File::open(&path) {
 | 
					 | 
				
			||||||
                                let excellon = parse_excellon(BufReader::new(file)).unwrap();
 | 
					 | 
				
			||||||
                                let drills: Drills = excellon.into();
 | 
					 | 
				
			||||||
                                let name = path.file_name().unwrap().to_str().unwrap().to_string();
 | 
					 | 
				
			||||||
                                app.excellons.insert(
 | 
					 | 
				
			||||||
                                    path.to_str().unwrap().into(),
 | 
					 | 
				
			||||||
                                    (name, drills.to_unit(app.variables.units)),
 | 
					 | 
				
			||||||
                                );
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                // TODO show error
 | 
					 | 
				
			||||||
                            };
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type FileOpenResponse = Result<Vec<(String, String, AppFileContent)>, Report<FileError>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn open_file_menu(ui: &mut egui::Ui) -> Option<FileOpenResponse> {
 | 
				
			||||||
 | 
					    let mut response = None;
 | 
				
			||||||
 | 
					    ui.set_max_width(200.0); // To make sure we wrap long text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ui.button("Gerber").clicked() {
 | 
				
			||||||
 | 
					        response = Some(open_file(AppFileType::Gerber));
 | 
				
			||||||
 | 
					        ui.close_menu();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ui.button("Excellon").clicked() {
 | 
				
			||||||
 | 
					        ui.close_menu();
 | 
				
			||||||
 | 
					        response = Some(open_file(AppFileType::Excellon));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ui.menu_button("SubMenu", |ui| {
 | 
				
			||||||
 | 
					    //     ui.menu_button("SubMenu", |ui| {
 | 
				
			||||||
 | 
					    //         if ui.button("Open…").clicked() {
 | 
				
			||||||
 | 
					    //             ui.close_menu();
 | 
				
			||||||
 | 
					    //         }
 | 
				
			||||||
 | 
					    //         let _ = ui.button("Item");
 | 
				
			||||||
 | 
					    //     });
 | 
				
			||||||
 | 
					    //     ui.menu_button("SubMenu", |ui| {
 | 
				
			||||||
 | 
					    //         if ui.button("Open…").clicked() {
 | 
				
			||||||
 | 
					    //             ui.close_menu();
 | 
				
			||||||
 | 
					    //         }
 | 
				
			||||||
 | 
					    //         let _ = ui.button("Item");
 | 
				
			||||||
 | 
					    //     });
 | 
				
			||||||
 | 
					    //     let _ = ui.button("Item");
 | 
				
			||||||
 | 
					    //     if ui.button("Open…").clicked() {
 | 
				
			||||||
 | 
					    //         ui.close_menu();
 | 
				
			||||||
 | 
					    //     }
 | 
				
			||||||
 | 
					    // });
 | 
				
			||||||
 | 
					    // ui.menu_button("SubMenu", |ui| {
 | 
				
			||||||
 | 
					    //     let _ = ui.button("Item1");
 | 
				
			||||||
 | 
					    //     let _ = ui.button("Item2");
 | 
				
			||||||
 | 
					    //     let _ = ui.button("Item3");
 | 
				
			||||||
 | 
					    //     let _ = ui.button("Item4");
 | 
				
			||||||
 | 
					    //     if ui.button("Open…").clicked() {
 | 
				
			||||||
 | 
					    //         ui.close_menu();
 | 
				
			||||||
 | 
					    //     }
 | 
				
			||||||
 | 
					    // });
 | 
				
			||||||
 | 
					    // let _ = ui.button("Very long text for this item that should be wrapped");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    response
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
 | 
					enum AppFileType {
 | 
				
			||||||
 | 
					    Gerber,
 | 
				
			||||||
 | 
					    Excellon,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::fmt::Display for AppFileType {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        write!(
 | 
				
			||||||
 | 
					            f,
 | 
				
			||||||
 | 
					            "{}",
 | 
				
			||||||
 | 
					            match self {
 | 
				
			||||||
 | 
					                AppFileType::Gerber => "Gerber",
 | 
				
			||||||
 | 
					                AppFileType::Excellon => "Excellon",
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AppFileType {
 | 
				
			||||||
 | 
					    pub fn extensions(&self) -> &[&str] {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            AppFileType::Gerber => GERBER_EXTENSIONS,
 | 
				
			||||||
 | 
					            AppFileType::Excellon => EXCELLON_EXTENSIONS,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub enum AppFileContent {
 | 
				
			||||||
 | 
					    Gerber(GerberDoc),
 | 
				
			||||||
 | 
					    Excellon(ExcellonDoc),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AppFileContent {
 | 
				
			||||||
 | 
					    fn from_file<T: Read>(
 | 
				
			||||||
 | 
					        file_type: AppFileType,
 | 
				
			||||||
 | 
					        file: BufReader<T>,
 | 
				
			||||||
 | 
					    ) -> Result<Self, Report<FileError>> {
 | 
				
			||||||
 | 
					        Ok(match file_type {
 | 
				
			||||||
 | 
					            AppFileType::Gerber => Self::Gerber(parse_gerber(file)),
 | 
				
			||||||
 | 
					            AppFileType::Excellon => Self::Excellon(
 | 
				
			||||||
 | 
					                parse_excellon(file)
 | 
				
			||||||
 | 
					                    .change_context(FileError)
 | 
				
			||||||
 | 
					                    .attach_printable("Could not parse Excellon file")?,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn open_file(
 | 
				
			||||||
 | 
					    file_type: AppFileType,
 | 
				
			||||||
 | 
					) -> Result<Vec<(String, String, AppFileContent)>, Report<FileError>> {
 | 
				
			||||||
 | 
					    let mut result = Vec::new();
 | 
				
			||||||
 | 
					    if let Some(paths) = rfd::FileDialog::new()
 | 
				
			||||||
 | 
					        .add_filter(file_type.to_string(), file_type.extensions())
 | 
				
			||||||
 | 
					        .pick_files()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        for path in paths {
 | 
				
			||||||
 | 
					            // get file content
 | 
				
			||||||
 | 
					            let file = File::open(&path).change_context(FileError)?;
 | 
				
			||||||
 | 
					            // parse file format
 | 
				
			||||||
 | 
					            let content = AppFileContent::from_file(file_type, BufReader::new(file))?;
 | 
				
			||||||
 | 
					            // get file path
 | 
				
			||||||
 | 
					            let path_str = path
 | 
				
			||||||
 | 
					                .to_str()
 | 
				
			||||||
 | 
					                .ok_or(FileError::new("Could not get file path string"))?;
 | 
				
			||||||
 | 
					            // get file name
 | 
				
			||||||
 | 
					            let name_str = path
 | 
				
			||||||
 | 
					                .file_name()
 | 
				
			||||||
 | 
					                .ok_or(FileError::new(
 | 
				
			||||||
 | 
					                    "Could not determine file name from selection",
 | 
				
			||||||
 | 
					                ))?
 | 
				
			||||||
 | 
					                .to_str()
 | 
				
			||||||
 | 
					                .ok_or(FileError::new("Could not convert OS String"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result.push((path_str.into(), name_str.into(), content));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(result)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
use eframe::egui::{self, CollapsingHeader};
 | 
					use eframe::egui::{self, RichText};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{application::Application, APP_NAME};
 | 
					use crate::{application::Application, APP_NAME};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -6,30 +6,74 @@ pub fn draw_sidebar(ctx: &egui::Context, app: &mut Application) {
 | 
				
			|||||||
    egui::SidePanel::left("left_panel")
 | 
					    egui::SidePanel::left("left_panel")
 | 
				
			||||||
        .exact_width(230.)
 | 
					        .exact_width(230.)
 | 
				
			||||||
        .show(ctx, |ui| {
 | 
					        .show(ctx, |ui| {
 | 
				
			||||||
 | 
					            ui.add_space(6.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ui.heading(APP_NAME);
 | 
					            ui.heading(APP_NAME);
 | 
				
			||||||
            CollapsingHeader::new("Gerber")
 | 
					
 | 
				
			||||||
                .default_open(true)
 | 
					            ui.add_space(12.0);
 | 
				
			||||||
                .show(ui, |ui| {
 | 
					
 | 
				
			||||||
 | 
					            let id = ui.make_persistent_id("GerberContainer");
 | 
				
			||||||
 | 
					            egui::collapsing_header::CollapsingState::load_with_default_open(ui.ctx(), id, true)
 | 
				
			||||||
 | 
					                .show_header(ui, |ui| {
 | 
				
			||||||
 | 
					                    ui.horizontal_centered(|ui| {
 | 
				
			||||||
 | 
					                        ui.image(app.resources.icons().gerber_file.clone());
 | 
				
			||||||
 | 
					                        ui.label(
 | 
				
			||||||
 | 
					                            RichText::new("Gerber")
 | 
				
			||||||
 | 
					                                .extra_letter_spacing(1.2)
 | 
				
			||||||
 | 
					                                .size(17.)
 | 
				
			||||||
 | 
					                                .strong(),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .body(|ui| {
 | 
				
			||||||
                    for (key, (name, _)) in app.gerbers.iter() {
 | 
					                    for (key, (name, _)) in app.gerbers.iter() {
 | 
				
			||||||
                        ui.selectable_value(&mut app.selection, key.to_string(), name);
 | 
					                        ui.selectable_value(
 | 
				
			||||||
 | 
					                            &mut app.selection,
 | 
				
			||||||
 | 
					                            key.to_string(),
 | 
				
			||||||
 | 
					                            RichText::new(name).size(11.),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            CollapsingHeader::new("Excellon")
 | 
					            let id = ui.make_persistent_id("ExcellonContainer");
 | 
				
			||||||
                .default_open(true)
 | 
					            egui::collapsing_header::CollapsingState::load_with_default_open(ui.ctx(), id, true)
 | 
				
			||||||
                .show(ui, |ui| {
 | 
					                .show_header(ui, |ui| {
 | 
				
			||||||
 | 
					                    ui.horizontal_centered(|ui| {
 | 
				
			||||||
 | 
					                        ui.image(app.resources.icons().excellon_file.clone());
 | 
				
			||||||
 | 
					                        ui.label(
 | 
				
			||||||
 | 
					                            RichText::new("Excellon")
 | 
				
			||||||
 | 
					                                .extra_letter_spacing(1.2)
 | 
				
			||||||
 | 
					                                .size(17.)
 | 
				
			||||||
 | 
					                                .strong(),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .body(|ui| {
 | 
				
			||||||
                    for (key, (name, _)) in app.excellons.iter() {
 | 
					                    for (key, (name, _)) in app.excellons.iter() {
 | 
				
			||||||
                        ui.selectable_value(&mut app.selection, key.to_string(), name);
 | 
					                        ui.selectable_value(&mut app.selection, key.to_string(), name);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            CollapsingHeader::new("Geometry")
 | 
					            let id = ui.make_persistent_id("GeometryContainer");
 | 
				
			||||||
                .default_open(true)
 | 
					            egui::collapsing_header::CollapsingState::load_with_default_open(ui.ctx(), id, true)
 | 
				
			||||||
                .show(ui, |ui| {
 | 
					                .show_header(ui, |ui| {
 | 
				
			||||||
 | 
					                    ui.horizontal_centered(|ui| {
 | 
				
			||||||
 | 
					                        ui.image(app.resources.icons().geometry_file.clone());
 | 
				
			||||||
 | 
					                        ui.label(
 | 
				
			||||||
 | 
					                            RichText::new("Geometry")
 | 
				
			||||||
 | 
					                                .extra_letter_spacing(1.2)
 | 
				
			||||||
 | 
					                                .size(17.)
 | 
				
			||||||
 | 
					                                .strong(),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .body(|ui| {
 | 
				
			||||||
                    for (key, _) in app.outlines.iter() {
 | 
					                    for (key, _) in app.outlines.iter() {
 | 
				
			||||||
                        ui.selectable_value(&mut app.selection, key.to_string(), key);
 | 
					                        ui.selectable_value(&mut app.selection, key.to_string(), key);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // allocate rest of the container
 | 
				
			||||||
            let (id, rect) = ui.allocate_space(ui.available_size());
 | 
					            let (id, rect) = ui.allocate_space(ui.available_size());
 | 
				
			||||||
            let response = ui.interact(rect, id, egui::Sense::click_and_drag());
 | 
					            let response = ui.interact(rect, id, egui::Sense::click_and_drag());
 | 
				
			||||||
            if response.clicked() {
 | 
					            if response.clicked() {
 | 
				
			||||||
 | 
				
			|||||||
@ -41,13 +41,13 @@ impl From<ExcellonDoc> for Drills {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Drills {
 | 
					impl Drills {
 | 
				
			||||||
    pub fn to_unit(&self, unit: Unit) -> Self {
 | 
					    pub fn into_unit(self, unit: Unit) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            units: unit,
 | 
					            units: unit,
 | 
				
			||||||
            holes: self
 | 
					            holes: self
 | 
				
			||||||
                .holes
 | 
					                .holes
 | 
				
			||||||
                .iter()
 | 
					                .into_iter()
 | 
				
			||||||
                .map(|hole| hole.to_unit(self.units, unit))
 | 
					                .map(|hole| hole.into_unit(self.units, unit))
 | 
				
			||||||
                .collect(),
 | 
					                .collect(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
mod doc;
 | 
					pub mod doc;
 | 
				
			||||||
pub mod drills;
 | 
					pub mod drills;
 | 
				
			||||||
mod errors;
 | 
					mod errors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -271,8 +271,6 @@ mod tests {
 | 
				
			|||||||
        let file = File::open("./FirstPCB-PTH.drl").unwrap();
 | 
					        let file = File::open("./FirstPCB-PTH.drl").unwrap();
 | 
				
			||||||
        let _reader = BufReader::new(file);
 | 
					        let _reader = BufReader::new(file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let excellon = parse_excellon(BufReader::new(excellon.as_bytes()));
 | 
					        let _excellon = parse_excellon(BufReader::new(excellon.as_bytes()));
 | 
				
			||||||
        // let excellon = parse_excellon(reader);
 | 
					 | 
				
			||||||
        println!("{:#?}", excellon);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,9 +8,9 @@ use crate::geometry::Geometry;
 | 
				
			|||||||
pub struct DXFConverter;
 | 
					pub struct DXFConverter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl DXFConverter {
 | 
					impl DXFConverter {
 | 
				
			||||||
    pub fn export(geometry: &Geometry, file: &str) {
 | 
					    pub fn export(_geometry: &Geometry, _file: &str) {
 | 
				
			||||||
        let mut drawing = Drawing::new();
 | 
					        let mut drawing = Drawing::new();
 | 
				
			||||||
        let added_entity_ref = drawing.add_entity(Entity::new(EntityType::Line(Line::default())));
 | 
					        let _added_entity_ref = drawing.add_entity(Entity::new(EntityType::Line(Line::default())));
 | 
				
			||||||
        // `added_entity_ref` is a reference to the newly added entity
 | 
					        // `added_entity_ref` is a reference to the newly added entity
 | 
				
			||||||
        drawing.save_file("./file.dxf").unwrap();
 | 
					        drawing.save_file("./file.dxf").unwrap();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -43,8 +43,8 @@ impl SVGConverter {
 | 
				
			|||||||
        let points = geo.points().into_iter().map(|p| {
 | 
					        let points = geo.points().into_iter().map(|p| {
 | 
				
			||||||
            Circle::new()
 | 
					            Circle::new()
 | 
				
			||||||
                .set("r", HOLE_MARK_DIAMETER)
 | 
					                .set("r", HOLE_MARK_DIAMETER)
 | 
				
			||||||
                .set("cx", p.x)
 | 
					                .set("cx", p.x())
 | 
				
			||||||
                .set("cy", p.invert_y().y)
 | 
					                .set("cy", p.invert_y().y())
 | 
				
			||||||
                .set("fill", "black")
 | 
					                .set("fill", "black")
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,21 @@
 | 
				
			|||||||
use std::f64::consts::{PI, TAU};
 | 
					use std::f64::consts::{PI, TAU};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use eframe::{
 | 
					use eframe::egui::{remap, Stroke};
 | 
				
			||||||
    egui::{remap, Stroke, Ui},
 | 
					 | 
				
			||||||
    epaint::CircleShape,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use egui_plot::{PlotPoints, PlotUi, Polygon};
 | 
					use egui_plot::{PlotPoints, PlotUi, Polygon};
 | 
				
			||||||
 | 
					use gerber_types::CirclePrimitive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    application::CanvasColour,
 | 
					    application::CanvasColour,
 | 
				
			||||||
    geometry::{ClipperPath, ClipperPaths},
 | 
					    geometry::{ClipperPath, ClipperPaths},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::super::{
 | 
					use super::{
 | 
				
			||||||
    helpers::{create_circular_path, CircleSegment},
 | 
					    super::{
 | 
				
			||||||
    point::{convert_to_unit, Point},
 | 
					        helpers::{create_circular_path, CircleSegment},
 | 
				
			||||||
    DrawableRaw, Unit,
 | 
					        point::{convert_to_unit, Point},
 | 
				
			||||||
 | 
					        DrawableRaw, Unit,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    macro_decimal_to_f64,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
@ -29,7 +30,7 @@ impl Circle {
 | 
				
			|||||||
    pub fn new(position: impl Into<Point>, diameter: f64, hole_diameter: Option<f64>) -> Self {
 | 
					    pub fn new(position: impl Into<Point>, diameter: f64, hole_diameter: Option<f64>) -> Self {
 | 
				
			||||||
        let position = position.into();
 | 
					        let position = position.into();
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            position: position,
 | 
					            position,
 | 
				
			||||||
            diameter,
 | 
					            diameter,
 | 
				
			||||||
            hole_diameter,
 | 
					            hole_diameter,
 | 
				
			||||||
            outline: create_circular_path(&position, diameter, CircleSegment::Full).into(),
 | 
					            outline: create_circular_path(&position, diameter, CircleSegment::Full).into(),
 | 
				
			||||||
@ -39,8 +40,19 @@ impl Circle {
 | 
				
			|||||||
        Self::new(position, aperture.diameter, aperture.hole_diameter)
 | 
					        Self::new(position, aperture.diameter, aperture.hole_diameter)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn to_unit(&self, origin: Unit, to: Unit) -> Self {
 | 
					    pub fn from_macro(ap_macro: &CirclePrimitive, variables: &[f64], target: Point) -> Self {
 | 
				
			||||||
        let position = self.position.to_unit(origin, to);
 | 
					        Self::new(
 | 
				
			||||||
 | 
					            Point::new(
 | 
				
			||||||
 | 
					                macro_decimal_to_f64(&ap_macro.center.0, variables) + target.x(),
 | 
				
			||||||
 | 
					                macro_decimal_to_f64(&ap_macro.center.1, variables) + target.y(),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            macro_decimal_to_f64(&ap_macro.diameter, variables),
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn into_unit(self, origin: Unit, to: Unit) -> Self {
 | 
				
			||||||
 | 
					        let position = self.position.into_unit(origin, to);
 | 
				
			||||||
        let diameter = convert_to_unit(self.diameter, origin, to);
 | 
					        let diameter = convert_to_unit(self.diameter, origin, to);
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            position,
 | 
					            position,
 | 
				
			||||||
@ -51,17 +63,6 @@ impl Circle {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn draw_circle(&self, ui: &mut PlotUi, colour: CanvasColour, selected: bool) {
 | 
					    fn draw_circle(&self, ui: &mut PlotUi, colour: CanvasColour, selected: bool) {
 | 
				
			||||||
        // let n = 512;
 | 
					 | 
				
			||||||
        // let circle_points: PlotPoints = (0..=n)
 | 
					 | 
				
			||||||
        //     .map(|i| {
 | 
					 | 
				
			||||||
        //         let t = remap(i as f64, 0.0..=(n as f64), 0.0..=TAU);
 | 
					 | 
				
			||||||
        //         let r = self.diameter / 2.;
 | 
					 | 
				
			||||||
        //         [
 | 
					 | 
				
			||||||
        //             r * t.cos() + self.position.x as f64,
 | 
					 | 
				
			||||||
        //             r * t.sin() + self.position.y as f64,
 | 
					 | 
				
			||||||
        //         ]
 | 
					 | 
				
			||||||
        //     })
 | 
					 | 
				
			||||||
        //     .collect();
 | 
					 | 
				
			||||||
        let circle_points = PlotPoints::from(Self::circle_segment_points(
 | 
					        let circle_points = PlotPoints::from(Self::circle_segment_points(
 | 
				
			||||||
            self.position,
 | 
					            self.position,
 | 
				
			||||||
            self.diameter,
 | 
					            self.diameter,
 | 
				
			||||||
@ -71,7 +72,7 @@ impl Circle {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        ui.polygon(
 | 
					        ui.polygon(
 | 
				
			||||||
            Polygon::new(circle_points)
 | 
					            Polygon::new(circle_points)
 | 
				
			||||||
                .fill_color(colour.to_colour32(selected))
 | 
					                .fill_color(colour.as_colour32(selected))
 | 
				
			||||||
                .stroke(Stroke::NONE),
 | 
					                .stroke(Stroke::NONE),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -84,16 +85,15 @@ impl Circle {
 | 
				
			|||||||
    ) -> Vec<[f64; 2]> {
 | 
					    ) -> Vec<[f64; 2]> {
 | 
				
			||||||
        let segment_width = segment_width.clamp(0.0, 1.0);
 | 
					        let segment_width = segment_width.clamp(0.0, 1.0);
 | 
				
			||||||
        let n = (512. * segment_width) as i32;
 | 
					        let n = (512. * segment_width) as i32;
 | 
				
			||||||
        let circle_points = (0..=n)
 | 
					
 | 
				
			||||||
 | 
					        (0..=n)
 | 
				
			||||||
            .map(|i| {
 | 
					            .map(|i| {
 | 
				
			||||||
                let t = remap(i as f64, 0.0..=(n as f64), 0.0..=TAU * segment_width)
 | 
					                let t = remap(i as f64, 0.0..=(n as f64), 0.0..=TAU * segment_width)
 | 
				
			||||||
                    + rotation * (PI / 180.);
 | 
					                    + rotation * (PI / 180.);
 | 
				
			||||||
                let r = diameter / 2.;
 | 
					                let r = diameter / 2.;
 | 
				
			||||||
                [r * t.cos() + position.x, r * t.sin() + position.y]
 | 
					                [r * t.cos() + position.x(), r * t.sin() + position.y()]
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .collect();
 | 
					            .collect()
 | 
				
			||||||
 | 
					 | 
				
			||||||
        circle_points
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -101,31 +101,7 @@ impl DrawableRaw for Circle {
 | 
				
			|||||||
    fn canvas_pos(&self) -> Point {
 | 
					    fn canvas_pos(&self) -> Point {
 | 
				
			||||||
        self.position
 | 
					        self.position
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn draw_egui(&self, ui: &mut Ui, selected: bool) {
 | 
					 | 
				
			||||||
        ui.painter().add(CircleShape::filled(
 | 
					 | 
				
			||||||
            self.position.invert_y().into(),
 | 
					 | 
				
			||||||
            self.diameter as f32 / 2.,
 | 
					 | 
				
			||||||
            CanvasColour::Copper.to_colour32(selected),
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn draw_egui_plot(&self, ui: &mut egui_plot::PlotUi, colour: CanvasColour, selected: bool) {
 | 
					    fn draw_egui_plot(&self, ui: &mut egui_plot::PlotUi, colour: CanvasColour, selected: bool) {
 | 
				
			||||||
        // let circle_points: Vec<[f64; 2]> =
 | 
					 | 
				
			||||||
        //     create_circular_path(&self.position, self.diameter, CircleSegment::Full)
 | 
					 | 
				
			||||||
        //         .iter()
 | 
					 | 
				
			||||||
        //         .map(|(x, y)| [*x, *y])
 | 
					 | 
				
			||||||
        //         .collect();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // let polygon = Polygon::new(PlotPoints::from(circle_points))
 | 
					 | 
				
			||||||
        //     .fill_color(if selected {
 | 
					 | 
				
			||||||
        //         COPPER_COLOR_SELECTED
 | 
					 | 
				
			||||||
        //     } else {
 | 
					 | 
				
			||||||
        //         COPPER_COLOR
 | 
					 | 
				
			||||||
        //     })
 | 
					 | 
				
			||||||
        //     .stroke(Stroke::new(0., Color32::TRANSPARENT));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // ui.polygon(polygon);
 | 
					 | 
				
			||||||
        self.draw_circle(ui, colour, selected);
 | 
					        self.draw_circle(ui, colour, selected);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										117
									
								
								src/geometry/elements/composite.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/geometry/elements/composite.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use clipper2::PointInPolygonResult;
 | 
				
			||||||
 | 
					use itertools::Itertools;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    application::CanvasColour,
 | 
				
			||||||
 | 
					    geometry::{point::Point, union::union_function, ClipperPath, ClipperPaths, DrawableRaw, Unit},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::Element;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					pub struct Composite {
 | 
				
			||||||
 | 
					    elements: Vec<Element>,
 | 
				
			||||||
 | 
					    outline: ClipperPaths,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Composite {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            elements: Vec::new(),
 | 
				
			||||||
 | 
					            outline: ClipperPaths::new(vec![]),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add(&mut self, element: Element) {
 | 
				
			||||||
 | 
					        self.elements.push(element);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn finalize(&mut self) {
 | 
				
			||||||
 | 
					        let mut intersection_map: HashMap<usize, Vec<usize>> = HashMap::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for part in self.elements.iter().enumerate().combinations(2) {
 | 
				
			||||||
 | 
					            let (index1, element1) = part[0];
 | 
				
			||||||
 | 
					            let (index2, element2) = part[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // check if element1 and element2 intersect
 | 
				
			||||||
 | 
					            if element1
 | 
				
			||||||
 | 
					                .outline()
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .any(|p| element2.outline().is_point_inside(*p) != PointInPolygonResult::IsOutside)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // add intersection result for both indices
 | 
				
			||||||
 | 
					                let entry1 = intersection_map.entry(index1).or_default();
 | 
				
			||||||
 | 
					                entry1.push(index2);
 | 
				
			||||||
 | 
					                let entry2 = intersection_map.entry(index2).or_default();
 | 
				
			||||||
 | 
					                entry2.push(index1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // go through all aperture components
 | 
				
			||||||
 | 
					        for i in 0..intersection_map.len() {
 | 
				
			||||||
 | 
					            // remove component from map
 | 
				
			||||||
 | 
					            if let Some(mut intersections) = intersection_map.remove(&i) {
 | 
				
			||||||
 | 
					                // take outline of element as base
 | 
				
			||||||
 | 
					                let mut geo = self.elements[i].to_paths();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // union with intersecting aperture components until done
 | 
				
			||||||
 | 
					                while let Some(other) = intersections.pop() {
 | 
				
			||||||
 | 
					                    // union with intersecting aperture component
 | 
				
			||||||
 | 
					                    let intersecting_element = &self.elements[other];
 | 
				
			||||||
 | 
					                    if let Some(union) = union_function(&geo, &intersecting_element.to_paths()) {
 | 
				
			||||||
 | 
					                        geo = union;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // get intersections of united aperture component 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);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                self.outline.push(geo);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn into_unit(self, origin: Unit, to: Unit) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            elements: self
 | 
				
			||||||
 | 
					                .elements
 | 
				
			||||||
 | 
					                .into_iter()
 | 
				
			||||||
 | 
					                .map(|el| el.into_unit(origin, to))
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
 | 
					            outline: self.outline,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl DrawableRaw for Composite {
 | 
				
			||||||
 | 
					    fn canvas_pos(&self) -> Point {
 | 
				
			||||||
 | 
					        // self.position
 | 
				
			||||||
 | 
					        todo!()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn draw_egui_plot(&self, ui: &mut egui_plot::PlotUi, colour: CanvasColour, selected: bool) {
 | 
				
			||||||
 | 
					        for element in &self.elements {
 | 
				
			||||||
 | 
					            element.draw_egui_plot(ui, colour, selected);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn to_paths(&self) -> ClipperPaths {
 | 
				
			||||||
 | 
					        self.outline.clone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn outline(&self) -> ClipperPath {
 | 
				
			||||||
 | 
					        self.outline.first().unwrap().clone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,14 +1,12 @@
 | 
				
			|||||||
use std::f64::consts::PI;
 | 
					use std::f64::consts::PI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use eframe::{
 | 
					use clipper2::PointInPolygonResult;
 | 
				
			||||||
    egui::{Pos2, Stroke, Ui},
 | 
					use eframe::egui::Stroke;
 | 
				
			||||||
    epaint::{CircleShape, PathShape, PathStroke},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use egui_plot::{PlotPoints, Polygon};
 | 
					use egui_plot::{PlotPoints, Polygon};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    application::CanvasColour,
 | 
					    application::CanvasColour,
 | 
				
			||||||
    geometry::{helpers::semi_circle, ClipperPath, ClipperPaths},
 | 
					    geometry::{helpers::semi_circle, ClipperPath, ClipperPaths, ClipperPoint},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::super::{
 | 
					use super::super::{
 | 
				
			||||||
@ -47,11 +45,16 @@ impl LinePath {
 | 
				
			|||||||
    pub fn finalize(&mut self, stroke_width: f64) {
 | 
					    pub fn finalize(&mut self, stroke_width: f64) {
 | 
				
			||||||
        self.diameter = stroke_width;
 | 
					        self.diameter = stroke_width;
 | 
				
			||||||
        self.outline = self.create_outline();
 | 
					        self.outline = self.create_outline();
 | 
				
			||||||
 | 
					        println!("Line with diameter: {stroke_width}");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn to_unit(&self, origin: Unit, to: Unit) -> Self {
 | 
					    pub fn into_unit(self, origin: Unit, to: Unit) -> Self {
 | 
				
			||||||
        let mut converted = Self {
 | 
					        let mut converted = Self {
 | 
				
			||||||
            points: self.points.iter().map(|p| p.to_unit(origin, to)).collect(),
 | 
					            points: self
 | 
				
			||||||
 | 
					                .points
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .map(|p| p.into_unit(origin, to))
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
            diameter: convert_to_unit(self.diameter, origin, to),
 | 
					            diameter: convert_to_unit(self.diameter, origin, to),
 | 
				
			||||||
            outline: ClipperPaths::new(vec![]),
 | 
					            outline: ClipperPaths::new(vec![]),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@ -60,6 +63,14 @@ impl LinePath {
 | 
				
			|||||||
        converted
 | 
					        converted
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn outline_includes_points(&self, points: &[Point]) -> bool {
 | 
				
			||||||
 | 
					        self.outline.iter().any(|path| {
 | 
				
			||||||
 | 
					            points.iter().any(|point| {
 | 
				
			||||||
 | 
					                path.is_point_inside(ClipperPoint::from(point)) != PointInPolygonResult::IsOutside
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn create_outline(&self) -> ClipperPaths {
 | 
					    fn create_outline(&self) -> ClipperPaths {
 | 
				
			||||||
        let mut paths: Vec<ClipperPath> = Vec::new();
 | 
					        let mut paths: Vec<ClipperPath> = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -91,33 +102,12 @@ impl DrawableRaw for &LinePath {
 | 
				
			|||||||
            points.push(points[0]);
 | 
					            points.push(points[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let poly = Polygon::new(PlotPoints::from(points))
 | 
					            let poly = Polygon::new(PlotPoints::from(points))
 | 
				
			||||||
                .fill_color(colour.to_colour32(selected))
 | 
					                .fill_color(colour.as_colour32(selected))
 | 
				
			||||||
                .stroke(Stroke::NONE);
 | 
					                .stroke(Stroke::NONE);
 | 
				
			||||||
            ui.polygon(poly);
 | 
					            ui.polygon(poly);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn draw_egui(&self, ui: &mut Ui, selected: bool) {
 | 
					 | 
				
			||||||
        ui.painter().add(PathShape::line(
 | 
					 | 
				
			||||||
            self.points
 | 
					 | 
				
			||||||
                .iter()
 | 
					 | 
				
			||||||
                .map(|v| (*v).invert_y().into())
 | 
					 | 
				
			||||||
                .collect::<Vec<Pos2>>(),
 | 
					 | 
				
			||||||
            PathStroke::new(
 | 
					 | 
				
			||||||
                self.diameter as f32,
 | 
					 | 
				
			||||||
                CanvasColour::Copper.to_colour32(selected),
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for point in &self.points {
 | 
					 | 
				
			||||||
            ui.painter().add(CircleShape::filled(
 | 
					 | 
				
			||||||
                point.invert_y().into(),
 | 
					 | 
				
			||||||
                self.diameter as f32 / 2.,
 | 
					 | 
				
			||||||
                CanvasColour::Copper.to_colour32(selected),
 | 
					 | 
				
			||||||
            ));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn to_paths(&self) -> ClipperPaths {
 | 
					    fn to_paths(&self) -> ClipperPaths {
 | 
				
			||||||
        self.outline.clone()
 | 
					        self.outline.clone()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -132,7 +122,7 @@ fn create_outline_between_points(point1: Point, point2: Point, width: f64) -> Ve
 | 
				
			|||||||
    let normalized = line_vec.normalize();
 | 
					    let normalized = line_vec.normalize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let angle =
 | 
					    let angle =
 | 
				
			||||||
        (normalized.x).acos() * (180. / PI) * if normalized.y < 0. { -1. } else { 1. } + 90.;
 | 
					        (normalized.x()).acos() * (180. / PI) * if normalized.y() < 0. { -1. } else { 1. } + 90.;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut outline: Vec<(f64, f64)> = Vec::new();
 | 
					    let mut outline: Vec<(f64, f64)> = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,79 +1,126 @@
 | 
				
			|||||||
use circle::Circle;
 | 
					use circle::Circle;
 | 
				
			||||||
use eframe::egui::Ui;
 | 
					use composite::Composite;
 | 
				
			||||||
use egui_plot::PlotUi;
 | 
					use egui_plot::PlotUi;
 | 
				
			||||||
 | 
					use evalexpr::eval_number;
 | 
				
			||||||
 | 
					use gerber_types::MacroDecimal;
 | 
				
			||||||
 | 
					use lazy_regex::regex;
 | 
				
			||||||
use linepath::LinePath;
 | 
					use linepath::LinePath;
 | 
				
			||||||
use obround::Obround;
 | 
					use obround::Obround;
 | 
				
			||||||
 | 
					use polygon::Polygon;
 | 
				
			||||||
use rectangle::Rectangle;
 | 
					use rectangle::Rectangle;
 | 
				
			||||||
 | 
					use tracing::error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::application::CanvasColour;
 | 
					use crate::application::CanvasColour;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{point::Point, ClipperPath, ClipperPaths, DrawableRaw, Unit};
 | 
					use super::{point::Point, ClipperPath, ClipperPaths, DrawableRaw, Unit};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod circle;
 | 
					pub mod circle;
 | 
				
			||||||
 | 
					pub mod composite;
 | 
				
			||||||
pub mod linepath;
 | 
					pub mod linepath;
 | 
				
			||||||
pub mod obround;
 | 
					pub mod obround;
 | 
				
			||||||
 | 
					pub mod polygon;
 | 
				
			||||||
pub mod rectangle;
 | 
					pub mod rectangle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub enum Element {
 | 
					pub enum Element {
 | 
				
			||||||
    Circle(Circle),
 | 
					    Circle(Circle),
 | 
				
			||||||
 | 
					    Composite(Composite),
 | 
				
			||||||
    Rectangle(Rectangle),
 | 
					    Rectangle(Rectangle),
 | 
				
			||||||
    Line(LinePath),
 | 
					    _Line(LinePath),
 | 
				
			||||||
    Obround(Obround),
 | 
					    Obround(Obround),
 | 
				
			||||||
 | 
					    Polygon(Polygon),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Element {
 | 
					impl Element {
 | 
				
			||||||
    pub fn draw_egui_plot(&self, ui: &mut PlotUi, colour: CanvasColour, selected: bool) {
 | 
					    pub fn draw_egui_plot(&self, ui: &mut PlotUi, colour: CanvasColour, selected: bool) {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Element::Circle(c) => c.draw_egui_plot(ui, colour, selected),
 | 
					            Element::Circle(c) => c.draw_egui_plot(ui, colour, selected),
 | 
				
			||||||
 | 
					            Element::Composite(co) => co.draw_egui_plot(ui, colour, selected),
 | 
				
			||||||
            Element::Rectangle(r) => r.draw_egui_plot(ui, colour, selected),
 | 
					            Element::Rectangle(r) => r.draw_egui_plot(ui, colour, selected),
 | 
				
			||||||
            Element::Line(l) => l.draw_egui_plot(ui, colour, selected),
 | 
					            Element::_Line(l) => l.draw_egui_plot(ui, colour, selected),
 | 
				
			||||||
            Element::Obround(o) => o.draw_egui_plot(ui, colour, selected),
 | 
					            Element::Obround(o) => o.draw_egui_plot(ui, colour, selected),
 | 
				
			||||||
        }
 | 
					            Element::Polygon(p) => p.draw_egui_plot(ui, colour, selected),
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn draw_egui(&self, ui: &mut Ui, selected: bool) {
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            Element::Circle(c) => c.draw_egui(ui, selected),
 | 
					 | 
				
			||||||
            Element::Rectangle(r) => r.draw_egui(ui, selected),
 | 
					 | 
				
			||||||
            Element::Line(l) => l.draw_egui(ui, selected),
 | 
					 | 
				
			||||||
            Element::Obround(o) => o.draw_egui(ui, selected),
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn canvas_pos(&self) -> Point {
 | 
					    pub fn canvas_pos(&self) -> Point {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Element::Circle(c) => c.canvas_pos(),
 | 
					            Element::Circle(c) => c.canvas_pos(),
 | 
				
			||||||
 | 
					            Element::Composite(c) => c.canvas_pos(),
 | 
				
			||||||
            Element::Rectangle(r) => r.canvas_pos(),
 | 
					            Element::Rectangle(r) => r.canvas_pos(),
 | 
				
			||||||
            Element::Line(l) => l.canvas_pos(),
 | 
					            Element::_Line(l) => l.canvas_pos(),
 | 
				
			||||||
            Element::Obround(o) => o.canvas_pos(),
 | 
					            Element::Obround(o) => o.canvas_pos(),
 | 
				
			||||||
 | 
					            Element::Polygon(p) => p.canvas_pos(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn to_paths(&self) -> ClipperPaths {
 | 
					    pub fn to_paths(&self) -> ClipperPaths {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Element::Circle(c) => c.to_paths(),
 | 
					            Element::Circle(c) => c.to_paths(),
 | 
				
			||||||
 | 
					            Element::Composite(c) => c.to_paths(),
 | 
				
			||||||
            Element::Rectangle(r) => r.to_paths(),
 | 
					            Element::Rectangle(r) => r.to_paths(),
 | 
				
			||||||
            Element::Line(l) => l.to_paths(),
 | 
					            Element::_Line(l) => l.to_paths(),
 | 
				
			||||||
            Element::Obround(o) => o.to_paths(),
 | 
					            Element::Obround(o) => o.to_paths(),
 | 
				
			||||||
 | 
					            Element::Polygon(p) => p.to_paths(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn outline(&self) -> ClipperPath {
 | 
					    pub fn outline(&self) -> ClipperPath {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Element::Circle(c) => c.outline(),
 | 
					            Element::Circle(c) => c.outline(),
 | 
				
			||||||
 | 
					            Element::Composite(c) => c.outline(),
 | 
				
			||||||
            Element::Rectangle(r) => r.outline(),
 | 
					            Element::Rectangle(r) => r.outline(),
 | 
				
			||||||
            Element::Line(l) => l.outline(),
 | 
					            Element::_Line(l) => l.outline(),
 | 
				
			||||||
            Element::Obround(o) => o.outline(),
 | 
					            Element::Obround(o) => o.outline(),
 | 
				
			||||||
 | 
					            Element::Polygon(p) => p.outline(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn to_unit(&self, origin: Unit, to: Unit) -> Self {
 | 
					    pub fn into_unit(self, origin: Unit, to: Unit) -> Self {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Element::Circle(c) => Element::Circle(c.to_unit(origin, to)),
 | 
					            Element::Circle(c) => Element::Circle(c.into_unit(origin, to)),
 | 
				
			||||||
            Element::Rectangle(r) => Element::Rectangle(r.to_unit(origin, to)),
 | 
					            Element::Composite(c) => Element::Composite(c.into_unit(origin, to)),
 | 
				
			||||||
            Element::Line(l) => Element::Line(l.to_unit(origin, to)),
 | 
					            Element::Rectangle(r) => Element::Rectangle(r.into_unit(origin, to)),
 | 
				
			||||||
            Element::Obround(o) => Element::Obround(o.to_unit(origin, to)),
 | 
					            Element::_Line(l) => Element::_Line(l.into_unit(origin, to)),
 | 
				
			||||||
 | 
					            Element::Obround(o) => Element::Obround(o.into_unit(origin, to)),
 | 
				
			||||||
 | 
					            Element::Polygon(p) => Element::Polygon(p.into_unit(origin, to)),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn macro_decimal_to_f64(macro_decimal: &MacroDecimal, variables: &[f64]) -> f64 {
 | 
				
			||||||
 | 
					    let re_units = regex!(r#"(\$\d+)"#);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    match macro_decimal {
 | 
				
			||||||
 | 
					        MacroDecimal::Value(v) => *v,
 | 
				
			||||||
 | 
					        MacroDecimal::Variable(v) => *variables.get(*v as usize).unwrap_or(&0.),
 | 
				
			||||||
 | 
					        MacroDecimal::Expression(ex) => {
 | 
				
			||||||
 | 
					            // search for variables (eg. $1)
 | 
				
			||||||
 | 
					            if let Some(captures) = re_units.captures(ex) {
 | 
				
			||||||
 | 
					                // copy expression
 | 
				
			||||||
 | 
					                let mut expr = ex.clone();
 | 
				
			||||||
 | 
					                // go through all found variable names
 | 
				
			||||||
 | 
					                for c in captures.iter().skip(1).flatten() {
 | 
				
			||||||
 | 
					                    // get variable name
 | 
				
			||||||
 | 
					                    let matched = c.as_str();
 | 
				
			||||||
 | 
					                    // create index out of variable
 | 
				
			||||||
 | 
					                    let index = matched.replace('$', "").parse::<usize>().unwrap();
 | 
				
			||||||
 | 
					                    // replace variable name with value
 | 
				
			||||||
 | 
					                    expr = expr.replace(
 | 
				
			||||||
 | 
					                        matched,
 | 
				
			||||||
 | 
					                        &variables.get(index - 1).unwrap_or(&0.).to_string(),
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                match eval_number(&expr) {
 | 
				
			||||||
 | 
					                    Ok(res) => res,
 | 
				
			||||||
 | 
					                    Err(e) => {
 | 
				
			||||||
 | 
					                        error!("{e:?}");
 | 
				
			||||||
 | 
					                        0.
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                0.
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,4 @@
 | 
				
			|||||||
use eframe::{
 | 
					use eframe::egui::Stroke;
 | 
				
			||||||
    egui::{Rect, Rounding, Stroke, Ui, Vec2},
 | 
					 | 
				
			||||||
    epaint::RectShape,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use egui_plot::{PlotPoints, Polygon};
 | 
					use egui_plot::{PlotPoints, Polygon};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
@ -9,24 +6,21 @@ use crate::{
 | 
				
			|||||||
    geometry::{ClipperPath, ClipperPaths},
 | 
					    geometry::{ClipperPath, ClipperPaths},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::rectangle::Rectangle;
 | 
					use super::super::{
 | 
				
			||||||
use super::{
 | 
					    helpers::{create_circular_path, semi_circle, CircleSegment},
 | 
				
			||||||
    super::{
 | 
					    point::{convert_to_unit, Point},
 | 
				
			||||||
        helpers::{create_circular_path, semi_circle, CircleSegment},
 | 
					    DrawableRaw, Unit,
 | 
				
			||||||
        point::{convert_to_unit, Point},
 | 
					 | 
				
			||||||
        DrawableRaw, Unit,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    circle::Circle,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					use super::rectangle::Rectangle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub struct Obround {
 | 
					pub struct Obround {
 | 
				
			||||||
    position: Point,
 | 
					    position: Point,
 | 
				
			||||||
    x: f64,
 | 
					    x: f64,
 | 
				
			||||||
    y: f64,
 | 
					    y: f64,
 | 
				
			||||||
    rounding: f64,
 | 
					    _rounding: f64,
 | 
				
			||||||
    outline: ClipperPath,
 | 
					    outline: ClipperPath,
 | 
				
			||||||
    rectangle: Rectangle,
 | 
					    _rectangle: Rectangle,
 | 
				
			||||||
    hole_diameter: Option<f64>,
 | 
					    hole_diameter: Option<f64>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -40,12 +34,27 @@ impl Obround {
 | 
				
			|||||||
            // check if obround is round to x or y
 | 
					            // check if obround is round to x or y
 | 
				
			||||||
            if x < y {
 | 
					            if x < y {
 | 
				
			||||||
                // round on y axis
 | 
					                // round on y axis
 | 
				
			||||||
                path.append(&mut semi_circle(position - Point::new(0., y / 4.), x, 180.));
 | 
					                path.append(&mut semi_circle(
 | 
				
			||||||
                path.append(&mut semi_circle(position + Point::new(0., y / 4.), x, 0.));
 | 
					                    position - Point::new(0., y / 2. - x / 2.),
 | 
				
			||||||
 | 
					                    x,
 | 
				
			||||||
 | 
					                    180.,
 | 
				
			||||||
 | 
					                ));
 | 
				
			||||||
 | 
					                path.append(&mut semi_circle(
 | 
				
			||||||
 | 
					                    position + Point::new(0., y / 2. - x / 2.),
 | 
				
			||||||
 | 
					                    x,
 | 
				
			||||||
 | 
					                    0.,
 | 
				
			||||||
 | 
					                ));
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                // TODO round on x axis -> check for correctness!!!!!!!
 | 
					                path.append(&mut semi_circle(
 | 
				
			||||||
                path.append(&mut semi_circle(position - Point::new(0., x / 4.), y, 270.));
 | 
					                    position + Point::new(x / 2. - y / 2., 0.),
 | 
				
			||||||
                path.append(&mut semi_circle(position + Point::new(0., x / 4.), y, 90.));
 | 
					                    y,
 | 
				
			||||||
 | 
					                    270.,
 | 
				
			||||||
 | 
					                ));
 | 
				
			||||||
 | 
					                path.append(&mut semi_circle(
 | 
				
			||||||
 | 
					                    position - Point::new(x / 2. - y / 2., 0.),
 | 
				
			||||||
 | 
					                    y,
 | 
				
			||||||
 | 
					                    90.,
 | 
				
			||||||
 | 
					                ));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            path
 | 
					            path
 | 
				
			||||||
@ -53,12 +62,12 @@ impl Obround {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            position,
 | 
					            position,
 | 
				
			||||||
            x: x,
 | 
					            x,
 | 
				
			||||||
            y: y,
 | 
					            y,
 | 
				
			||||||
            rounding: diameter,
 | 
					            _rounding: diameter,
 | 
				
			||||||
            outline: outline.into(),
 | 
					            outline: outline.into(),
 | 
				
			||||||
            rectangle: Rectangle::new(position, x, y, hole_diameter),
 | 
					            _rectangle: Rectangle::new(position, x, y, hole_diameter),
 | 
				
			||||||
            hole_diameter: hole_diameter,
 | 
					            hole_diameter,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,9 +75,9 @@ impl Obround {
 | 
				
			|||||||
        Self::new(position, aperture.x, aperture.y, aperture.hole_diameter)
 | 
					        Self::new(position, aperture.x, aperture.y, aperture.hole_diameter)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn to_unit(&self, origin: Unit, to: Unit) -> Self {
 | 
					    pub fn into_unit(self, origin: Unit, to: Unit) -> Self {
 | 
				
			||||||
        Self::new(
 | 
					        Self::new(
 | 
				
			||||||
            self.position.to_unit(origin, to),
 | 
					            self.position.into_unit(origin, to),
 | 
				
			||||||
            convert_to_unit(self.x, origin, to),
 | 
					            convert_to_unit(self.x, origin, to),
 | 
				
			||||||
            convert_to_unit(self.y, origin, to),
 | 
					            convert_to_unit(self.y, origin, to),
 | 
				
			||||||
            self.hole_diameter.map(|d| convert_to_unit(d, origin, to)),
 | 
					            self.hole_diameter.map(|d| convert_to_unit(d, origin, to)),
 | 
				
			||||||
@ -86,22 +95,11 @@ impl DrawableRaw for Obround {
 | 
				
			|||||||
        points.push(points[0]);
 | 
					        points.push(points[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let poly = Polygon::new(PlotPoints::from(points))
 | 
					        let poly = Polygon::new(PlotPoints::from(points))
 | 
				
			||||||
            .fill_color(colour.to_colour32(selected))
 | 
					            .fill_color(colour.as_colour32(selected))
 | 
				
			||||||
            .stroke(Stroke::NONE);
 | 
					            .stroke(Stroke::NONE);
 | 
				
			||||||
        ui.polygon(poly);
 | 
					        ui.polygon(poly);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn draw_egui(&self, ui: &mut Ui, selected: bool) {
 | 
					 | 
				
			||||||
        ui.painter().add(RectShape::filled(
 | 
					 | 
				
			||||||
            Rect::from_center_size(
 | 
					 | 
				
			||||||
                self.position.invert_y().into(),
 | 
					 | 
				
			||||||
                Vec2::new(self.rectangle.width as f32, self.rectangle.height as f32),
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            Rounding::same(self.rounding as f32 / 2.),
 | 
					 | 
				
			||||||
            CanvasColour::Copper.to_colour32(selected),
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn to_paths(&self) -> ClipperPaths {
 | 
					    fn to_paths(&self) -> ClipperPaths {
 | 
				
			||||||
        ClipperPaths::new(vec![self.outline.clone()])
 | 
					        ClipperPaths::new(vec![self.outline.clone()])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										72
									
								
								src/geometry/elements/polygon.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/geometry/elements/polygon.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					use eframe::egui::Stroke;
 | 
				
			||||||
 | 
					use gerber_types::OutlinePrimitive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    application::CanvasColour,
 | 
				
			||||||
 | 
					    geometry::{
 | 
				
			||||||
 | 
					        elements::macro_decimal_to_f64, point::Point, ClipperPath, ClipperPaths, DrawableRaw, Unit,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					pub struct Polygon {
 | 
				
			||||||
 | 
					    points: Vec<Point>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Polygon {
 | 
				
			||||||
 | 
					    pub fn new(points: &[Point]) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            points: points.to_vec(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn into_unit(self, origin: Unit, to: Unit) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            points: self
 | 
				
			||||||
 | 
					                .points
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .map(|p| p.into_unit(origin, to))
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn from_macro(ap_macro: &OutlinePrimitive, variables: &[f64], target: Point) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            points: ap_macro
 | 
				
			||||||
 | 
					                .points
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .map(|(x, y)| {
 | 
				
			||||||
 | 
					                    Point::new(
 | 
				
			||||||
 | 
					                        macro_decimal_to_f64(x, variables),
 | 
				
			||||||
 | 
					                        macro_decimal_to_f64(y, variables),
 | 
				
			||||||
 | 
					                    ) + target
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl DrawableRaw for Polygon {
 | 
				
			||||||
 | 
					    fn canvas_pos(&self) -> Point {
 | 
				
			||||||
 | 
					        // self.position
 | 
				
			||||||
 | 
					        todo!()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn draw_egui_plot(&self, ui: &mut egui_plot::PlotUi, colour: CanvasColour, selected: bool) {
 | 
				
			||||||
 | 
					        let points: Vec<[f64; 2]> = self.points.iter().map(|p| [p.x(), p.y()]).collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ui.polygon(
 | 
				
			||||||
 | 
					            egui_plot::Polygon::new(points)
 | 
				
			||||||
 | 
					                .fill_color(colour.as_colour32(selected))
 | 
				
			||||||
 | 
					                .stroke(Stroke::NONE),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn to_paths(&self) -> ClipperPaths {
 | 
				
			||||||
 | 
					        ClipperPaths::new(vec![self.outline()])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn outline(&self) -> ClipperPath {
 | 
				
			||||||
 | 
					        ClipperPath::new(self.points.iter().map(|p| p.into()).collect())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,20 +1,21 @@
 | 
				
			|||||||
use eframe::{
 | 
					use eframe::egui::Stroke;
 | 
				
			||||||
    egui::{Rect, Rounding, Stroke, Ui, Vec2},
 | 
					 | 
				
			||||||
    epaint::RectShape,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use egui_plot::{PlotPoints, Polygon};
 | 
					use egui_plot::{PlotPoints, Polygon};
 | 
				
			||||||
 | 
					use gerber_types::{CenterLinePrimitive, VectorLinePrimitive};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    application::CanvasColour,
 | 
					    application::CanvasColour,
 | 
				
			||||||
    geometry::{ClipperPath, ClipperPaths},
 | 
					    geometry::{ClipperPath, ClipperPaths},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::super::{
 | 
					use super::{
 | 
				
			||||||
    point::{convert_to_unit, Point},
 | 
					    super::{
 | 
				
			||||||
    DrawableRaw, Unit,
 | 
					        point::{convert_to_unit, Point},
 | 
				
			||||||
 | 
					        DrawableRaw, Unit,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    macro_decimal_to_f64,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub struct Rectangle {
 | 
					pub struct Rectangle {
 | 
				
			||||||
    position: Point,
 | 
					    position: Point,
 | 
				
			||||||
    pub width: f64,
 | 
					    pub width: f64,
 | 
				
			||||||
@ -34,11 +35,26 @@ impl Rectangle {
 | 
				
			|||||||
            height: aperture.y,
 | 
					            height: aperture.y,
 | 
				
			||||||
            hole_diameter: aperture.hole_diameter,
 | 
					            hole_diameter: aperture.hole_diameter,
 | 
				
			||||||
            outline: vec![
 | 
					            outline: vec![
 | 
				
			||||||
                (position.x - aperture.x / 2., position.y - aperture.y / 2.),
 | 
					                (
 | 
				
			||||||
                (position.x - aperture.x / 2., position.y + aperture.y / 2.),
 | 
					                    position.x() - aperture.x / 2.,
 | 
				
			||||||
                (position.x + aperture.x / 2., position.y + aperture.y / 2.),
 | 
					                    position.y() - aperture.y / 2.,
 | 
				
			||||||
                (position.x + aperture.x / 2., position.y - aperture.y / 2.),
 | 
					                ),
 | 
				
			||||||
                (position.x - aperture.x / 2., position.y - aperture.y / 2.),
 | 
					                (
 | 
				
			||||||
 | 
					                    position.x() - aperture.x / 2.,
 | 
				
			||||||
 | 
					                    position.y() + aperture.y / 2.,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    position.x() + aperture.x / 2.,
 | 
				
			||||||
 | 
					                    position.y() + aperture.y / 2.,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    position.x() + aperture.x / 2.,
 | 
				
			||||||
 | 
					                    position.y() - aperture.y / 2.,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    position.x() - aperture.x / 2.,
 | 
				
			||||||
 | 
					                    position.y() - aperture.y / 2.,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
            .into(),
 | 
					            .into(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -51,18 +67,66 @@ impl Rectangle {
 | 
				
			|||||||
            height,
 | 
					            height,
 | 
				
			||||||
            hole_diameter,
 | 
					            hole_diameter,
 | 
				
			||||||
            outline: vec![
 | 
					            outline: vec![
 | 
				
			||||||
                (position.x - width / 2., position.y - height / 2.),
 | 
					                (position.x() - width / 2., position.y() - height / 2.),
 | 
				
			||||||
                (position.x - width / 2., position.y + height / 2.),
 | 
					                (position.x() - width / 2., position.y() + height / 2.),
 | 
				
			||||||
                (position.x + width / 2., position.y + height / 2.),
 | 
					                (position.x() + width / 2., position.y() + height / 2.),
 | 
				
			||||||
                (position.x + width / 2., position.y - height / 2.),
 | 
					                (position.x() + width / 2., position.y() - height / 2.),
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
            .into(),
 | 
					            .into(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn to_unit(&self, origin: Unit, to: Unit) -> Self {
 | 
					    pub fn from_macro_vector_line(
 | 
				
			||||||
 | 
					        ap_macro: &VectorLinePrimitive,
 | 
				
			||||||
 | 
					        variables: &[f64],
 | 
				
			||||||
 | 
					        target: Point,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        // TODO angle
 | 
				
			||||||
 | 
					        let x_start = macro_decimal_to_f64(&ap_macro.start.0, variables);
 | 
				
			||||||
 | 
					        let x_end = macro_decimal_to_f64(&ap_macro.end.0, variables);
 | 
				
			||||||
 | 
					        let y_start = macro_decimal_to_f64(&ap_macro.start.1, variables);
 | 
				
			||||||
 | 
					        let y_end = macro_decimal_to_f64(&ap_macro.end.1, variables);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let height = if x_end - x_start == 0. {
 | 
				
			||||||
 | 
					            macro_decimal_to_f64(&ap_macro.width, variables)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            x_end - x_start
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let width = if y_end - y_start == 0. {
 | 
				
			||||||
 | 
					            macro_decimal_to_f64(&ap_macro.width, variables)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            y_end - y_start
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self::new(
 | 
					        Self::new(
 | 
				
			||||||
            self.position.to_unit(origin, to),
 | 
					            Point::new(x_start, y_start)
 | 
				
			||||||
 | 
					                + (Point::new(x_end, y_end) - Point::new(x_start, y_start)).div(2.)
 | 
				
			||||||
 | 
					                + target,
 | 
				
			||||||
 | 
					            height,
 | 
				
			||||||
 | 
					            width,
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn from_macro_center_line(
 | 
				
			||||||
 | 
					        ap_macro: &CenterLinePrimitive,
 | 
				
			||||||
 | 
					        variables: &[f64],
 | 
				
			||||||
 | 
					        target: Point,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        // TODO angle
 | 
				
			||||||
 | 
					        Self::new(
 | 
				
			||||||
 | 
					            Point::new(
 | 
				
			||||||
 | 
					                macro_decimal_to_f64(&ap_macro.center.0, variables),
 | 
				
			||||||
 | 
					                macro_decimal_to_f64(&ap_macro.center.1, variables),
 | 
				
			||||||
 | 
					            ) + target,
 | 
				
			||||||
 | 
					            macro_decimal_to_f64(&ap_macro.dimensions.0, variables),
 | 
				
			||||||
 | 
					            macro_decimal_to_f64(&ap_macro.dimensions.1, variables),
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn into_unit(self, origin: Unit, to: Unit) -> Self {
 | 
				
			||||||
 | 
					        Self::new(
 | 
				
			||||||
 | 
					            self.position.into_unit(origin, to),
 | 
				
			||||||
            convert_to_unit(self.width, origin, to),
 | 
					            convert_to_unit(self.width, origin, to),
 | 
				
			||||||
            convert_to_unit(self.height, origin, to),
 | 
					            convert_to_unit(self.height, origin, to),
 | 
				
			||||||
            self.hole_diameter.map(|d| convert_to_unit(d, origin, to)),
 | 
					            self.hole_diameter.map(|d| convert_to_unit(d, origin, to)),
 | 
				
			||||||
@ -73,8 +137,8 @@ impl Rectangle {
 | 
				
			|||||||
impl DrawableRaw for Rectangle {
 | 
					impl DrawableRaw for Rectangle {
 | 
				
			||||||
    fn canvas_pos(&self) -> Point {
 | 
					    fn canvas_pos(&self) -> Point {
 | 
				
			||||||
        self.position
 | 
					        self.position
 | 
				
			||||||
            .shift_x(self.position.x / 2.)
 | 
					            .shift_x(self.position.x() / 2.)
 | 
				
			||||||
            .shift_y(self.position.y / 2.)
 | 
					            .shift_y(self.position.y() / 2.)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn draw_egui_plot(&self, ui: &mut egui_plot::PlotUi, colour: CanvasColour, selected: bool) {
 | 
					    fn draw_egui_plot(&self, ui: &mut egui_plot::PlotUi, colour: CanvasColour, selected: bool) {
 | 
				
			||||||
@ -82,22 +146,11 @@ impl DrawableRaw for Rectangle {
 | 
				
			|||||||
        points.push(points[0]);
 | 
					        points.push(points[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let poly = Polygon::new(PlotPoints::from(points))
 | 
					        let poly = Polygon::new(PlotPoints::from(points))
 | 
				
			||||||
            .fill_color(colour.to_colour32(selected))
 | 
					            .fill_color(colour.as_colour32(selected))
 | 
				
			||||||
            .stroke(Stroke::NONE);
 | 
					            .stroke(Stroke::NONE);
 | 
				
			||||||
        ui.polygon(poly);
 | 
					        ui.polygon(poly);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn draw_egui(&self, ui: &mut Ui, selected: bool) {
 | 
					 | 
				
			||||||
        ui.painter().add(RectShape::filled(
 | 
					 | 
				
			||||||
            Rect::from_center_size(
 | 
					 | 
				
			||||||
                self.position.invert_y().into(),
 | 
					 | 
				
			||||||
                Vec2::new(self.width as f32, self.height as f32),
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            Rounding::ZERO,
 | 
					 | 
				
			||||||
            CanvasColour::Copper.to_colour32(selected),
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn to_paths(&self) -> ClipperPaths {
 | 
					    fn to_paths(&self) -> ClipperPaths {
 | 
				
			||||||
        ClipperPaths::new(vec![self.outline.clone()])
 | 
					        ClipperPaths::new(vec![self.outline.clone()])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,10 @@
 | 
				
			|||||||
use clipper2::Paths;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use gerber_types::{
 | 
					use gerber_types::{
 | 
				
			||||||
    Aperture, Command, Coordinates, DCode, FunctionCode, GCode, InterpolationMode, MCode,
 | 
					    Aperture, ApertureMacro, Command, Coordinates, DCode, FunctionCode, GCode, InterpolationMode,
 | 
				
			||||||
    Operation, Unit,
 | 
					    MCode, MacroContent, Operation, Unit,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use tracing::{debug, error, info};
 | 
					use tracing::{error, info};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    geometry::{
 | 
					    geometry::{
 | 
				
			||||||
@ -16,6 +17,7 @@ use crate::{
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
 | 
					    elements::{composite::Composite, polygon::Polygon},
 | 
				
			||||||
    union::{union_lines, union_with_apertures},
 | 
					    union::{union_lines, union_with_apertures},
 | 
				
			||||||
    Geometry,
 | 
					    Geometry,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -32,7 +34,6 @@ impl From<GerberDoc> for Geometry {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // create geometries by applying all gerber commands
 | 
					        // create geometries by applying all gerber commands
 | 
				
			||||||
        for command in gerber.commands {
 | 
					        for command in gerber.commands {
 | 
				
			||||||
            println!("{command:?}");
 | 
					 | 
				
			||||||
            match command {
 | 
					            match command {
 | 
				
			||||||
                Command::FunctionCode(f) => {
 | 
					                Command::FunctionCode(f) => {
 | 
				
			||||||
                    match f {
 | 
					                    match f {
 | 
				
			||||||
@ -83,6 +84,7 @@ impl From<GerberDoc> for Geometry {
 | 
				
			|||||||
                                    Operation::Flash(f) => {
 | 
					                                    Operation::Flash(f) => {
 | 
				
			||||||
                                        Self::add_geometry(
 | 
					                                        Self::add_geometry(
 | 
				
			||||||
                                            &mut added_apertures,
 | 
					                                            &mut added_apertures,
 | 
				
			||||||
 | 
					                                            &gerber.aperture_macros,
 | 
				
			||||||
                                            ¤t_position,
 | 
					                                            ¤t_position,
 | 
				
			||||||
                                            &f,
 | 
					                                            &f,
 | 
				
			||||||
                                            &selected_aperture,
 | 
					                                            &selected_aperture,
 | 
				
			||||||
@ -93,6 +95,19 @@ impl From<GerberDoc> for Geometry {
 | 
				
			|||||||
                                },
 | 
					                                },
 | 
				
			||||||
                                // select an aperture
 | 
					                                // select an aperture
 | 
				
			||||||
                                DCode::SelectAperture(ap) => {
 | 
					                                DCode::SelectAperture(ap) => {
 | 
				
			||||||
 | 
					                                    // check if a aperture is selected and if it's circular
 | 
				
			||||||
 | 
					                                    if let Some(Aperture::Circle(c)) = selected_aperture.as_ref() {
 | 
				
			||||||
 | 
					                                        // check if a path is currently active
 | 
				
			||||||
 | 
					                                        if !active_path.is_empty() {
 | 
				
			||||||
 | 
					                                            // finish active path if there is an active one before selecting new aperture
 | 
				
			||||||
 | 
					                                            active_path.finalize(c.diameter);
 | 
				
			||||||
 | 
					                                            path_container.push(active_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                            // create new active path
 | 
				
			||||||
 | 
					                                            active_path = LinePath::new();
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                    selected_aperture = Some(
 | 
					                                    selected_aperture = Some(
 | 
				
			||||||
                                        gerber
 | 
					                                        gerber
 | 
				
			||||||
                                            .apertures
 | 
					                                            .apertures
 | 
				
			||||||
@ -127,53 +142,13 @@ impl From<GerberDoc> for Geometry {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // create empty path set
 | 
					 | 
				
			||||||
        let mut result = clipper2::Paths::new(vec![]);
 | 
					 | 
				
			||||||
        // if path_container.len() > 1 {
 | 
					 | 
				
			||||||
        //     let mut clipper = path_container[1]
 | 
					 | 
				
			||||||
        //         .outline
 | 
					 | 
				
			||||||
        //         // .to_paths()
 | 
					 | 
				
			||||||
        //         .to_clipper_subject()
 | 
					 | 
				
			||||||
        //         .add_clip(path_container[2].outline.clone());
 | 
					 | 
				
			||||||
        //     // .add_clip(path_container[3].outline.clone())
 | 
					 | 
				
			||||||
        //     // .add_clip(path_container[4].outline.clone());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //     // for clip in added_apertures.iter().skip(2) {
 | 
					 | 
				
			||||||
        //     //     clipper = clipper.add_clip(clip.to_paths());
 | 
					 | 
				
			||||||
        //     // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //     // for line in path_container.iter().skip(2) {
 | 
					 | 
				
			||||||
        //     //     clipper = clipper.add_clip(line.to_paths())
 | 
					 | 
				
			||||||
        //     // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //     result = clipper.union(clipper2::FillRule::default()).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //     result = result
 | 
					 | 
				
			||||||
        //         .to_clipper_subject()
 | 
					 | 
				
			||||||
        //         .add_clip(path_container[3].outline.clone())
 | 
					 | 
				
			||||||
        //         .add_clip(path_container[4].outline.clone())
 | 
					 | 
				
			||||||
        //         .union(clipper2::FillRule::default())
 | 
					 | 
				
			||||||
        //         .unwrap();
 | 
					 | 
				
			||||||
        // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // let mut geo = Paths::new(vec![]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // union all drawn lines into nets of conductors
 | 
					        // union all drawn lines into nets of conductors
 | 
				
			||||||
        let conductor_net = union_lines(&path_container);
 | 
					        let conductor_net = union_lines(&path_container);
 | 
				
			||||||
 | 
					        // union conductors with apertures
 | 
				
			||||||
 | 
					        let united_nets = union_with_apertures(&added_apertures, conductor_net).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // for outline in &conductor_net {
 | 
					 | 
				
			||||||
        //     println!("{:?}", outline.included_points);
 | 
					 | 
				
			||||||
        //     geo.push(outline.outline.clone());
 | 
					 | 
				
			||||||
        // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // println!("Number of conductor net paths: {}", geo.len());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if let Some(geo) = union_with_apertures(&added_apertures, conductor_net) {
 | 
					 | 
				
			||||||
            println!("Number of finalized net paths: {}", geo.len());
 | 
					 | 
				
			||||||
            result = geo;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            outline_union: result,
 | 
					            united_nets,
 | 
				
			||||||
            apertures: added_apertures,
 | 
					            apertures: added_apertures,
 | 
				
			||||||
            paths: path_container,
 | 
					            paths: path_container,
 | 
				
			||||||
            units: gerber.units.unwrap_or(Unit::Millimeters).into(),
 | 
					            units: gerber.units.unwrap_or(Unit::Millimeters).into(),
 | 
				
			||||||
@ -182,7 +157,7 @@ impl From<GerberDoc> for Geometry {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Geometry {
 | 
					impl Geometry {
 | 
				
			||||||
    fn move_position(coord: &Coordinates, position: &mut Point) -> () {
 | 
					    fn move_position(coord: &Coordinates, position: &mut Point) {
 | 
				
			||||||
        if let Ok(pos) = Point::try_from(coord) {
 | 
					        if let Ok(pos) = Point::try_from(coord) {
 | 
				
			||||||
            *position = pos;
 | 
					            *position = pos;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@ -190,6 +165,7 @@ impl Geometry {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    fn add_geometry(
 | 
					    fn add_geometry(
 | 
				
			||||||
        geometries: &mut Vec<Element>,
 | 
					        geometries: &mut Vec<Element>,
 | 
				
			||||||
 | 
					        aperture_macros: &HashMap<String, ApertureMacro>,
 | 
				
			||||||
        position: &Point,
 | 
					        position: &Point,
 | 
				
			||||||
        coordinates: &Coordinates,
 | 
					        coordinates: &Coordinates,
 | 
				
			||||||
        aperture: &Option<Aperture>,
 | 
					        aperture: &Option<Aperture>,
 | 
				
			||||||
@ -216,7 +192,53 @@ impl Geometry {
 | 
				
			|||||||
                error!("Unsupported Polygon aperture:\r\n{:#?}", p);
 | 
					                error!("Unsupported Polygon aperture:\r\n{:#?}", p);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Aperture::Other(o) => {
 | 
					            Aperture::Other(o) => {
 | 
				
			||||||
                error!("Unsupported Other aperture:\r\n{:#?}", o);
 | 
					                // split at '/' -> name/arguments
 | 
				
			||||||
 | 
					                if let Some((name, args)) = o.split_once("/") {
 | 
				
			||||||
 | 
					                    // parse variables from args
 | 
				
			||||||
 | 
					                    let variables: Vec<f64> = args
 | 
				
			||||||
 | 
					                        .split(",")
 | 
				
			||||||
 | 
					                        .map(|s| s.parse::<f64>().unwrap_or(0.))
 | 
				
			||||||
 | 
					                        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if let Some(ap_macro) = aperture_macros.get(name) {
 | 
				
			||||||
 | 
					                        let mut composite = Composite::new();
 | 
				
			||||||
 | 
					                        // add elements from macro
 | 
				
			||||||
 | 
					                        for aperture in &ap_macro.content {
 | 
				
			||||||
 | 
					                            match aperture {
 | 
				
			||||||
 | 
					                                MacroContent::Circle(c) => composite.add(Element::Circle(
 | 
				
			||||||
 | 
					                                    Circle::from_macro(c, &variables, target),
 | 
				
			||||||
 | 
					                                )),
 | 
				
			||||||
 | 
					                                MacroContent::VectorLine(vl) => composite.add(Element::Rectangle(
 | 
				
			||||||
 | 
					                                    Rectangle::from_macro_vector_line(vl, &variables, target),
 | 
				
			||||||
 | 
					                                )),
 | 
				
			||||||
 | 
					                                MacroContent::CenterLine(cl) => composite.add(Element::Rectangle(
 | 
				
			||||||
 | 
					                                    Rectangle::from_macro_center_line(cl, &variables, target),
 | 
				
			||||||
 | 
					                                )),
 | 
				
			||||||
 | 
					                                MacroContent::Outline(ol) => composite.add(Element::Polygon(
 | 
				
			||||||
 | 
					                                    Polygon::from_macro(ol, &variables, target),
 | 
				
			||||||
 | 
					                                )),
 | 
				
			||||||
 | 
					                                MacroContent::Polygon(p) => {
 | 
				
			||||||
 | 
					                                    error!("Polygon Not implemented yet")
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                MacroContent::Moire(m) => {
 | 
				
			||||||
 | 
					                                    error!("Moire Not implemented yet")
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                MacroContent::Thermal(t) => {
 | 
				
			||||||
 | 
					                                    error!("Thermal Not implemented yet")
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                MacroContent::VariableDefinition(_) => {}
 | 
				
			||||||
 | 
					                                MacroContent::Comment(_) => {}
 | 
				
			||||||
 | 
					                            };
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        composite.finalize();
 | 
				
			||||||
 | 
					                        geometries.push(Element::Composite(composite));
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        error!("Unsupported Other aperture:\r\n{:#?}", o);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    error!("Unsupported Other aperture:\r\n{:#?}", o);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -10,13 +10,14 @@ pub fn semi_circle(center: Point, diameter: f64, tilt: f64) -> Vec<(f64, f64)> {
 | 
				
			|||||||
        .map(|i| {
 | 
					        .map(|i| {
 | 
				
			||||||
            let angle = (i as f64 / (CIRCLE_SEGMENTS / 2) as f64) * PI + tilt * (PI / 180.);
 | 
					            let angle = (i as f64 / (CIRCLE_SEGMENTS / 2) as f64) * PI + tilt * (PI / 180.);
 | 
				
			||||||
            (
 | 
					            (
 | 
				
			||||||
                angle.cos() * diameter / 2. + center.x,
 | 
					                angle.cos() * diameter / 2. + center.x(),
 | 
				
			||||||
                angle.sin() * diameter / 2. + center.y,
 | 
					                angle.sin() * diameter / 2. + center.y(),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .collect()
 | 
					        .collect()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[allow(unused)]
 | 
				
			||||||
pub enum CircleSegment {
 | 
					pub enum CircleSegment {
 | 
				
			||||||
    North,
 | 
					    North,
 | 
				
			||||||
    East,
 | 
					    East,
 | 
				
			||||||
@ -49,106 +50,106 @@ pub fn create_circular_path(
 | 
				
			|||||||
        .map(|&i| {
 | 
					        .map(|&i| {
 | 
				
			||||||
            let angle = (i as f64 / CIRCLE_SEGMENTS as f64) * 2.0 * PI;
 | 
					            let angle = (i as f64 / CIRCLE_SEGMENTS as f64) * 2.0 * PI;
 | 
				
			||||||
            (
 | 
					            (
 | 
				
			||||||
                angle.sin() * diameter / 2. + position.x,
 | 
					                angle.sin() * diameter / 2. + position.x(),
 | 
				
			||||||
                angle.cos() * diameter / 2. + position.y,
 | 
					                angle.cos() * diameter / 2. + position.y(),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .collect()
 | 
					        .collect()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, PartialEq)]
 | 
					// #[derive(Debug, PartialEq)]
 | 
				
			||||||
pub enum Orientation {
 | 
					// pub enum Orientation {
 | 
				
			||||||
    Clockwise,
 | 
					//     Clockwise,
 | 
				
			||||||
    CounterClockwise,
 | 
					//     CounterClockwise,
 | 
				
			||||||
    CoLinear,
 | 
					//     CoLinear,
 | 
				
			||||||
}
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// To find orientation of ordered triplet (p, q, r).
 | 
					// // To find orientation of ordered triplet (p, q, r).
 | 
				
			||||||
// https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
 | 
					// // https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
 | 
				
			||||||
pub fn orientation(p: impl Into<Point>, q: impl Into<Point>, r: impl Into<Point>) -> Orientation {
 | 
					// pub fn orientation(p: impl Into<Point>, q: impl Into<Point>, r: impl Into<Point>) -> Orientation {
 | 
				
			||||||
    let (p, q, r) = (p.into(), q.into(), r.into());
 | 
					//     let (p, q, r) = (p.into(), q.into(), r.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // See https://www.geeksforgeeks.org/orientation-3-ordered-points/
 | 
					//     // See https://www.geeksforgeeks.org/orientation-3-ordered-points/
 | 
				
			||||||
    // for details of below formula.
 | 
					//     // for details of below formula.
 | 
				
			||||||
    let val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
 | 
					//     let val = (q.y() - p.y()) * (r.x() - q.x()) - (q.x() - p.x()) * (r.y() - q.y());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    match val {
 | 
					//     match val {
 | 
				
			||||||
        0. => Orientation::CoLinear,
 | 
					//         0. => Orientation::CoLinear,
 | 
				
			||||||
        x if x > 0. => Orientation::Clockwise,
 | 
					//         x if x > 0. => Orientation::Clockwise,
 | 
				
			||||||
        _ => Orientation::CounterClockwise,
 | 
					//         _ => Orientation::CounterClockwise,
 | 
				
			||||||
    }
 | 
					//     }
 | 
				
			||||||
}
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Given three collinear points p, q, r, the function checks if
 | 
					// // Given three collinear points p, q, r, the function checks if
 | 
				
			||||||
// point q lies on line segment 'pr'
 | 
					// // point q lies on line segment 'pr'
 | 
				
			||||||
pub fn on_segment(p: impl Into<Point>, q: impl Into<Point>, r: impl Into<Point>) -> bool {
 | 
					// pub fn on_segment(p: impl Into<Point>, q: impl Into<Point>, r: impl Into<Point>) -> bool {
 | 
				
			||||||
    let (p, q, r) = (p.into(), q.into(), r.into());
 | 
					//     let (p, q, r) = (p.into(), q.into(), r.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    q.x <= greater_val(p.x, r.x)
 | 
					//     q.x() <= greater_val(p.x(), r.x())
 | 
				
			||||||
        && q.x >= lower_val(p.x, r.x)
 | 
					//         && q.x() >= lower_val(p.x(), r.x())
 | 
				
			||||||
        && q.y <= greater_val(p.y, r.y)
 | 
					//         && q.y() <= greater_val(p.y(), r.y())
 | 
				
			||||||
        && q.y >= lower_val(p.y, r.y)
 | 
					//         && q.y() >= lower_val(p.y(), r.y())
 | 
				
			||||||
}
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn greater_val(a: f64, b: f64) -> f64 {
 | 
					// fn greater_val(a: f64, b: f64) -> f64 {
 | 
				
			||||||
    if a > b {
 | 
					//     if a > b {
 | 
				
			||||||
        a
 | 
					//         a
 | 
				
			||||||
    } else {
 | 
					//     } else {
 | 
				
			||||||
        b
 | 
					//         b
 | 
				
			||||||
    }
 | 
					//     }
 | 
				
			||||||
}
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn lower_val(a: f64, b: f64) -> f64 {
 | 
					// fn lower_val(a: f64, b: f64) -> f64 {
 | 
				
			||||||
    if a < b {
 | 
					//     if a < b {
 | 
				
			||||||
        a
 | 
					//         a
 | 
				
			||||||
    } else {
 | 
					//     } else {
 | 
				
			||||||
        b
 | 
					//         b
 | 
				
			||||||
    }
 | 
					//     }
 | 
				
			||||||
}
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The main function that returns true if line segment 'p1q1'
 | 
					// // The main function that returns true if line segment 'p1q1'
 | 
				
			||||||
// and 'p2q2' intersect.
 | 
					// // and 'p2q2' intersect.
 | 
				
			||||||
// https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
 | 
					// // https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
 | 
				
			||||||
pub fn do_intersect(
 | 
					// pub fn do_intersect(
 | 
				
			||||||
    p1: impl Into<Point>,
 | 
					//     p1: impl Into<Point>,
 | 
				
			||||||
    q1: impl Into<Point>,
 | 
					//     q1: impl Into<Point>,
 | 
				
			||||||
    p2: impl Into<Point>,
 | 
					//     p2: impl Into<Point>,
 | 
				
			||||||
    q2: impl Into<Point>,
 | 
					//     q2: impl Into<Point>,
 | 
				
			||||||
) -> bool {
 | 
					// ) -> bool {
 | 
				
			||||||
    let (p1, q1, p2, q2) = (p1.into(), q1.into(), p2.into(), q2.into());
 | 
					//     let (p1, q1, p2, q2) = (p1.into(), q1.into(), p2.into(), q2.into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Find the four orientations needed for general and
 | 
					//     // Find the four orientations needed for general and
 | 
				
			||||||
    // special cases
 | 
					//     // special cases
 | 
				
			||||||
    let o1 = orientation(p1, q1, p2);
 | 
					//     let o1 = orientation(p1, q1, p2);
 | 
				
			||||||
    let o2 = orientation(p1, q1, q2);
 | 
					//     let o2 = orientation(p1, q1, q2);
 | 
				
			||||||
    let o3 = orientation(p2, q2, p1);
 | 
					//     let o3 = orientation(p2, q2, p1);
 | 
				
			||||||
    let o4 = orientation(p2, q2, q1);
 | 
					//     let o4 = orientation(p2, q2, q1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // General case
 | 
					//     // General case
 | 
				
			||||||
    if o1 != o2 && o3 != o4 {
 | 
					//     if o1 != o2 && o3 != o4 {
 | 
				
			||||||
        return true;
 | 
					//         return true;
 | 
				
			||||||
    }
 | 
					//     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Special Cases
 | 
					//     // Special Cases
 | 
				
			||||||
    // p1, q1 and p2 are collinear and p2 lies on segment p1q1
 | 
					//     // p1, q1 and p2 are collinear and p2 lies on segment p1q1
 | 
				
			||||||
    if o1 == Orientation::CoLinear && on_segment(p1, p2, q1) {
 | 
					//     if o1 == Orientation::CoLinear && on_segment(p1, p2, q1) {
 | 
				
			||||||
        return true;
 | 
					//         return true;
 | 
				
			||||||
    }
 | 
					//     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // p1, q1 and q2 are collinear and q2 lies on segment p1q1
 | 
					//     // p1, q1 and q2 are collinear and q2 lies on segment p1q1
 | 
				
			||||||
    if o2 == Orientation::CoLinear && on_segment(p1, q2, q1) {
 | 
					//     if o2 == Orientation::CoLinear && on_segment(p1, q2, q1) {
 | 
				
			||||||
        return true;
 | 
					//         return true;
 | 
				
			||||||
    };
 | 
					//     };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // p2, q2 and p1 are collinear and p1 lies on segment p2q2
 | 
					//     // p2, q2 and p1 are collinear and p1 lies on segment p2q2
 | 
				
			||||||
    if o3 == Orientation::CoLinear && on_segment(p2, p1, q2) {
 | 
					//     if o3 == Orientation::CoLinear && on_segment(p2, p1, q2) {
 | 
				
			||||||
        return true;
 | 
					//         return true;
 | 
				
			||||||
    };
 | 
					//     };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // p2, q2 and q1 are collinear and q1 lies on segment p2q2
 | 
					//     // p2, q2 and q1 are collinear and q1 lies on segment p2q2
 | 
				
			||||||
    if o4 == Orientation::CoLinear && on_segment(p2, q1, q2) {
 | 
					//     if o4 == Orientation::CoLinear && on_segment(p2, q1, q2) {
 | 
				
			||||||
        return true;
 | 
					//         return true;
 | 
				
			||||||
    };
 | 
					//     };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    false // Doesn't fall in any of the above cases
 | 
					//     false // Doesn't fall in any of the above cases
 | 
				
			||||||
}
 | 
					// }
 | 
				
			||||||
 | 
				
			|||||||
@ -2,47 +2,39 @@ pub mod elements;
 | 
				
			|||||||
pub mod gerber;
 | 
					pub mod gerber;
 | 
				
			||||||
mod helpers;
 | 
					mod helpers;
 | 
				
			||||||
pub mod point;
 | 
					pub mod point;
 | 
				
			||||||
mod union;
 | 
					pub mod union;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::fmt::{Debug, Display};
 | 
					use std::fmt::{Debug, Display};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use clipper2::{Bounds, Path, Paths, PointScaler};
 | 
					use clipper2::{Bounds, Path, Paths, PointScaler};
 | 
				
			||||||
use eframe::egui::Ui;
 | 
					 | 
				
			||||||
use egui_plot::PlotUi;
 | 
					use egui_plot::PlotUi;
 | 
				
			||||||
use elements::{linepath::LinePath, Element};
 | 
					use elements::{linepath::LinePath, Element};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use point::Point;
 | 
					use point::Point;
 | 
				
			||||||
 | 
					use union::UnitedNets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::application::CanvasColour;
 | 
					use crate::application::CanvasColour;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Geometry {
 | 
					pub struct Geometry {
 | 
				
			||||||
    pub outline_union: ClipperPaths,
 | 
					    pub united_nets: UnitedNets,
 | 
				
			||||||
    pub apertures: Vec<Element>,
 | 
					    pub apertures: Vec<Element>,
 | 
				
			||||||
    pub paths: Vec<LinePath>,
 | 
					    pub paths: Vec<LinePath>,
 | 
				
			||||||
    pub units: Unit,
 | 
					    pub units: Unit,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Geometry {
 | 
					impl Geometry {
 | 
				
			||||||
    pub fn to_unit(self, to: Unit) -> Self {
 | 
					    pub fn into_unit(self, to: Unit) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            outline_union: self
 | 
					            united_nets: self.united_nets.into_unit(self.units, to),
 | 
				
			||||||
                .outline_union
 | 
					 | 
				
			||||||
                .iter()
 | 
					 | 
				
			||||||
                .map(|p| {
 | 
					 | 
				
			||||||
                    p.iter()
 | 
					 | 
				
			||||||
                        .map(|p| (&Point::from(p).to_unit(self.units, to)).into())
 | 
					 | 
				
			||||||
                        .collect()
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .collect(),
 | 
					 | 
				
			||||||
            apertures: self
 | 
					            apertures: self
 | 
				
			||||||
                .apertures
 | 
					                .apertures
 | 
				
			||||||
                .iter()
 | 
					                .into_iter()
 | 
				
			||||||
                .map(|a| a.to_unit(self.units, to))
 | 
					                .map(|a| a.into_unit(self.units, to))
 | 
				
			||||||
                .collect(),
 | 
					                .collect(),
 | 
				
			||||||
            paths: self
 | 
					            paths: self
 | 
				
			||||||
                .paths
 | 
					                .paths
 | 
				
			||||||
                .iter()
 | 
					                .into_iter()
 | 
				
			||||||
                .map(|l| l.to_unit(self.units, to))
 | 
					                .map(|l| l.into_unit(self.units, to))
 | 
				
			||||||
                .collect(),
 | 
					                .collect(),
 | 
				
			||||||
            units: to,
 | 
					            units: to,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -98,12 +90,7 @@ pub type ClipperBounds = Bounds<Micro>;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
pub trait DrawableRaw {
 | 
					pub trait DrawableRaw {
 | 
				
			||||||
    fn canvas_pos(&self) -> Point;
 | 
					    fn canvas_pos(&self) -> Point;
 | 
				
			||||||
    fn draw_egui(&self, ui: &mut Ui, selected: bool);
 | 
					 | 
				
			||||||
    fn draw_egui_plot(&self, ui: &mut PlotUi, colour: CanvasColour, selected: bool);
 | 
					    fn draw_egui_plot(&self, ui: &mut PlotUi, colour: CanvasColour, selected: bool);
 | 
				
			||||||
    fn to_paths(&self) -> ClipperPaths;
 | 
					    fn to_paths(&self) -> ClipperPaths;
 | 
				
			||||||
    fn outline(&self) -> ClipperPath;
 | 
					    fn outline(&self) -> ClipperPath;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
fn canvas_position_from_gerber(gerber_position: &Point, offset: Point) -> Point {
 | 
					 | 
				
			||||||
    gerber_position.shift_x(offset.x).shift_y(offset.y)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,80 +1,115 @@
 | 
				
			|||||||
use std::ops::{Add, Sub};
 | 
					use std::{
 | 
				
			||||||
 | 
					    fmt,
 | 
				
			||||||
 | 
					    marker::PhantomData,
 | 
				
			||||||
 | 
					    ops::{Add, Sub},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use clipper2::PointScaler;
 | 
				
			||||||
use eframe::egui::{Pos2, Vec2};
 | 
					use eframe::egui::{Pos2, Vec2};
 | 
				
			||||||
use egui_plot::PlotPoint;
 | 
					use egui_plot::PlotPoint;
 | 
				
			||||||
use gerber_types::Coordinates;
 | 
					use gerber_types::Coordinates;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{ClipperPoint, Unit};
 | 
					use super::{ClipperPoint, Micro, Unit};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, Copy, PartialEq)]
 | 
					#[derive(Copy, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub struct Point {
 | 
					pub struct Point<P: PointScaler = Micro> {
 | 
				
			||||||
    pub x: f64,
 | 
					    x: i64,
 | 
				
			||||||
    pub y: f64,
 | 
					    y: i64,
 | 
				
			||||||
 | 
					    phantom_data: PhantomData<P>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Point {
 | 
					impl fmt::Debug for Point {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        f.debug_struct("Point")
 | 
				
			||||||
 | 
					            .field("x", &self.x())
 | 
				
			||||||
 | 
					            .field("y", &self.y())
 | 
				
			||||||
 | 
					            .finish()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<P: PointScaler> Point<P> {
 | 
				
			||||||
    pub fn new(x: f64, y: f64) -> Self {
 | 
					    pub fn new(x: f64, y: f64) -> Self {
 | 
				
			||||||
        Point { x, y }
 | 
					        // Point { x, y }
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            x: P::scale(x) as i64,
 | 
				
			||||||
 | 
					            y: P::scale(y) as i64,
 | 
				
			||||||
 | 
					            phantom_data: PhantomData,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn shift_x(&self, shift: f64) -> Self {
 | 
					    pub fn shift_x(&self, shift: f64) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            x: self.x + shift,
 | 
					            x: self.x + P::scale(shift) as i64,
 | 
				
			||||||
            y: self.y,
 | 
					            y: self.y,
 | 
				
			||||||
 | 
					            phantom_data: PhantomData,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn shift_y(&self, shift: f64) -> Self {
 | 
					    pub fn shift_y(&self, shift: f64) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            x: self.x,
 | 
					            x: self.x,
 | 
				
			||||||
            y: self.y + shift,
 | 
					            y: self.y + P::scale(shift) as i64,
 | 
				
			||||||
 | 
					            phantom_data: PhantomData,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn div(&self, divisor: f64) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            x: (self.x as f64 / divisor) as i64,
 | 
				
			||||||
 | 
					            y: (self.y as f64 / divisor) as i64,
 | 
				
			||||||
 | 
					            phantom_data: PhantomData,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn len(&self) -> f64 {
 | 
					    pub fn len(&self) -> f64 {
 | 
				
			||||||
        (self.x.powi(2) + self.y.powi(2)).sqrt()
 | 
					        P::descale(((self.x.pow(2) + self.y.pow(2)) as f64).sqrt())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn normalize(&self) -> Self {
 | 
					    pub fn normalize(&self) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self::new(self.x() / self.len(), self.y() / self.len())
 | 
				
			||||||
            x: self.x / self.len(),
 | 
					 | 
				
			||||||
            y: self.y / self.len(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn invert_y(&self) -> Self {
 | 
					    pub fn invert_y(&self) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            x: self.x,
 | 
					            x: self.x,
 | 
				
			||||||
            y: -self.y,
 | 
					            y: -self.y,
 | 
				
			||||||
 | 
					            phantom_data: PhantomData,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn to_unit(&self, origin: Unit, to: Unit) -> Self {
 | 
					    pub fn into_unit(self, origin: Unit, to: Unit) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self::new(
 | 
				
			||||||
            x: convert_to_unit(self.x, origin, to),
 | 
					            convert_to_unit(self.x(), origin, to),
 | 
				
			||||||
            y: convert_to_unit(self.y, origin, to),
 | 
					            convert_to_unit(self.y(), origin, to),
 | 
				
			||||||
        }
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the x coordinate of the point.
 | 
				
			||||||
 | 
					    pub fn x(&self) -> f64 {
 | 
				
			||||||
 | 
					        P::descale(self.x as f64)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the y coordinate of the point.
 | 
				
			||||||
 | 
					    pub fn y(&self) -> f64 {
 | 
				
			||||||
 | 
					        P::descale(self.y as f64)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<Point> for (f64, f64) {
 | 
					impl From<Point> for (f64, f64) {
 | 
				
			||||||
    fn from(value: Point) -> Self {
 | 
					    fn from(value: Point) -> Self {
 | 
				
			||||||
        (value.x, value.y)
 | 
					        (value.x(), value.y())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<Point> for [f64; 2] {
 | 
					impl From<Point> for [f64; 2] {
 | 
				
			||||||
    fn from(value: Point) -> Self {
 | 
					    fn from(value: Point) -> Self {
 | 
				
			||||||
        [value.x, value.y]
 | 
					        [value.x(), value.y()]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<[f64; 2]> for Point {
 | 
					impl From<[f64; 2]> for Point {
 | 
				
			||||||
    fn from(value: [f64; 2]) -> Self {
 | 
					    fn from(value: [f64; 2]) -> Self {
 | 
				
			||||||
        Point {
 | 
					        Self::new(value[0], value[1])
 | 
				
			||||||
            x: value[0],
 | 
					 | 
				
			||||||
            y: value[1],
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -85,6 +120,7 @@ impl Add for Point {
 | 
				
			|||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            x: self.x + other.x,
 | 
					            x: self.x + other.x,
 | 
				
			||||||
            y: self.y + other.y,
 | 
					            y: self.y + other.y,
 | 
				
			||||||
 | 
					            phantom_data: PhantomData,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -96,6 +132,7 @@ impl Sub for Point {
 | 
				
			|||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            x: self.x - other.x,
 | 
					            x: self.x - other.x,
 | 
				
			||||||
            y: self.y - other.y,
 | 
					            y: self.y - other.y,
 | 
				
			||||||
 | 
					            phantom_data: PhantomData,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -103,8 +140,8 @@ impl Sub for Point {
 | 
				
			|||||||
impl From<Point> for Pos2 {
 | 
					impl From<Point> for Pos2 {
 | 
				
			||||||
    fn from(value: Point) -> Self {
 | 
					    fn from(value: Point) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            x: value.x as f32,
 | 
					            x: value.x() as f32,
 | 
				
			||||||
            y: value.y as f32,
 | 
					            y: value.y() as f32,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -112,33 +149,30 @@ impl From<Point> for Pos2 {
 | 
				
			|||||||
impl From<&Point> for Pos2 {
 | 
					impl From<&Point> for Pos2 {
 | 
				
			||||||
    fn from(value: &Point) -> Self {
 | 
					    fn from(value: &Point) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            x: value.x as f32,
 | 
					            x: value.x() as f32,
 | 
				
			||||||
            y: value.y as f32,
 | 
					            y: value.y() as f32,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<Pos2> for Point {
 | 
					impl From<Pos2> for Point {
 | 
				
			||||||
    fn from(value: Pos2) -> Self {
 | 
					    fn from(value: Pos2) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self::new(value.x.into(), value.y.into())
 | 
				
			||||||
            x: value.x.into(),
 | 
					 | 
				
			||||||
            y: value.y.into(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<Point> for Vec2 {
 | 
					impl From<Point> for Vec2 {
 | 
				
			||||||
    fn from(value: Point) -> Self {
 | 
					    fn from(value: Point) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            x: value.x as f32,
 | 
					            x: value.x() as f32,
 | 
				
			||||||
            y: value.y as f32,
 | 
					            y: value.y() as f32,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<&Point> for ClipperPoint {
 | 
					impl From<&Point> for ClipperPoint {
 | 
				
			||||||
    fn from(value: &Point) -> Self {
 | 
					    fn from(value: &Point) -> Self {
 | 
				
			||||||
        Self::new(value.x, value.y)
 | 
					        Self::from_scaled(value.x, value.y)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -150,7 +184,7 @@ impl From<&ClipperPoint> for Point {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl From<Point> for PlotPoint {
 | 
					impl From<Point> for PlotPoint {
 | 
				
			||||||
    fn from(value: Point) -> Self {
 | 
					    fn from(value: Point) -> Self {
 | 
				
			||||||
        Self::new(value.x, value.y)
 | 
					        Self::new(value.x(), value.y())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,30 +1,110 @@
 | 
				
			|||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use clipper2::{FillRule, One, Paths, PointInPolygonResult};
 | 
					use clipper2::{FillRule, PointInPolygonResult};
 | 
				
			||||||
 | 
					use itertools::Itertools;
 | 
				
			||||||
use crate::geometry::helpers::do_intersect;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    elements::{linepath::LinePath, Element},
 | 
					    elements::{linepath::LinePath, Element},
 | 
				
			||||||
    point::Point,
 | 
					    point::Point,
 | 
				
			||||||
    ClipperPaths,
 | 
					    ClipperBounds, ClipperPaths, ClipperPoint, Unit,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Default)]
 | 
				
			||||||
 | 
					pub struct UnitedNets {
 | 
				
			||||||
 | 
					    pub conductors: Vec<ConductorNet>,
 | 
				
			||||||
 | 
					    pub isolated_apertures: Vec<Element>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl UnitedNets {
 | 
				
			||||||
 | 
					    pub fn into_unit(self, origin: Unit, to: Unit) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            conductors: self
 | 
				
			||||||
 | 
					                .conductors
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .map(|net| net.to_unit(origin, to))
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
 | 
					            isolated_apertures: self
 | 
				
			||||||
 | 
					                .isolated_apertures
 | 
				
			||||||
 | 
					                .into_iter()
 | 
				
			||||||
 | 
					                .map(|el| el.into_unit(origin, to))
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn bounds(&self) -> ClipperBounds {
 | 
				
			||||||
 | 
					        let conductor_net = self
 | 
				
			||||||
 | 
					            .conductors
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .map(|net| net.outline.clone())
 | 
				
			||||||
 | 
					            .reduce(|mut acc, v| {
 | 
				
			||||||
 | 
					                acc.push(v);
 | 
				
			||||||
 | 
					                acc
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        let isolated_apertures_bound = ClipperPaths::new(
 | 
				
			||||||
 | 
					            self.isolated_apertures
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .map(|net| net.outline())
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .bounds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(net) = conductor_net {
 | 
				
			||||||
 | 
					            let net_bounds = net.bounds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ClipperBounds {
 | 
				
			||||||
 | 
					                min: ClipperPoint::new(
 | 
				
			||||||
 | 
					                    net_bounds.min.x().min(isolated_apertures_bound.min.x()),
 | 
				
			||||||
 | 
					                    net_bounds.min.y().min(isolated_apertures_bound.min.y()),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                max: ClipperPoint::new(
 | 
				
			||||||
 | 
					                    net_bounds.max.x().max(isolated_apertures_bound.max.x()),
 | 
				
			||||||
 | 
					                    net_bounds.max.y().max(isolated_apertures_bound.max.y()),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            isolated_apertures_bound
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub struct ConductorNet {
 | 
					pub struct ConductorNet {
 | 
				
			||||||
    pub outline: ClipperPaths,
 | 
					    pub outline: ClipperPaths,
 | 
				
			||||||
    pub included_points: Vec<Point>,
 | 
					    pub included_points: Vec<Point>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ConductorNet {
 | 
				
			||||||
 | 
					    pub fn to_unit(&self, origin: Unit, to: Unit) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            outline: self
 | 
				
			||||||
 | 
					                .outline
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .map(|p| {
 | 
				
			||||||
 | 
					                    p.iter()
 | 
				
			||||||
 | 
					                        .map(|p| (&Point::from(p).into_unit(origin, to)).into())
 | 
				
			||||||
 | 
					                        .collect()
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
 | 
					            included_points: self
 | 
				
			||||||
 | 
					                .included_points
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .map(|el| el.into_unit(origin, to))
 | 
				
			||||||
 | 
					                .collect(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn union_lines(lines: &[LinePath]) -> Vec<ConductorNet> {
 | 
					pub fn union_lines(lines: &[LinePath]) -> Vec<ConductorNet> {
 | 
				
			||||||
    let mut intersection_map = HashMap::new();
 | 
					    let mut intersection_map = HashMap::new();
 | 
				
			||||||
    println!(
 | 
					
 | 
				
			||||||
        "START LINE UNION of {:?}",
 | 
					    // TODO prevent double checking same combination
 | 
				
			||||||
        lines
 | 
					    let combinations = lines.iter().enumerate().combinations(2);
 | 
				
			||||||
            .iter()
 | 
					
 | 
				
			||||||
            .map(|l| l.points.clone())
 | 
					    for co in combinations {
 | 
				
			||||||
            .collect::<Vec<Vec<Point>>>()
 | 
					        let (index, line) = co[0];
 | 
				
			||||||
    );
 | 
					        // print!("{} mit {};", combi[0].0, combi[1].0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (index, line) in lines.iter().enumerate() {
 | 
					    for (index, line) in lines.iter().enumerate() {
 | 
				
			||||||
        if !line.is_empty() {
 | 
					        if !line.is_empty() {
 | 
				
			||||||
            // create list of intersecting lines
 | 
					            // create list of intersecting lines
 | 
				
			||||||
@ -36,16 +116,28 @@ pub fn union_lines(lines: &[LinePath]) -> Vec<ConductorNet> {
 | 
				
			|||||||
                    // do not check for intersection with itself
 | 
					                    // do not check for intersection with itself
 | 
				
			||||||
                    if index != i {
 | 
					                    if index != i {
 | 
				
			||||||
                        // check for all lines in path
 | 
					                        // check for all lines in path
 | 
				
			||||||
                        let intersect = l.points.windows(2).any(|w| {
 | 
					                        // let intersect = l.points.windows(2).any(|w| {
 | 
				
			||||||
                            let (p2, q2) = (w[0], w[1]);
 | 
					                        //     let (p2, q2) = (w[0], w[1]);
 | 
				
			||||||
                            // println!("LINE 2 {p2:?}, {q2:?}");
 | 
					                        //     // println!("LINE 2 {p2:?}, {q2:?}");
 | 
				
			||||||
                            do_intersect(p1, q1, p2, q2)
 | 
					                        //     // do_intersect(p1, q1, p2, q2)
 | 
				
			||||||
                        });
 | 
					                        //     line.outline
 | 
				
			||||||
 | 
					                        //         .get(0)
 | 
				
			||||||
 | 
					                        //         .unwrap()
 | 
				
			||||||
 | 
					                        //         .is_point_inside(ClipperPoint::from(&p2))
 | 
				
			||||||
 | 
					                        //         != PointInPolygonResult::IsOutside
 | 
				
			||||||
 | 
					                        //         || line
 | 
				
			||||||
 | 
					                        //             .outline
 | 
				
			||||||
 | 
					                        //             .get(0)
 | 
				
			||||||
 | 
					                        //             .unwrap()
 | 
				
			||||||
 | 
					                        //             .is_point_inside(ClipperPoint::from(&q2))
 | 
				
			||||||
 | 
					                        //             != PointInPolygonResult::IsOutside
 | 
				
			||||||
 | 
					                        // });
 | 
				
			||||||
 | 
					                        let intersect = line.outline_includes_points(&l.points);
 | 
				
			||||||
                        // println!("INTERSECTING: {intersect}");
 | 
					                        // println!("INTERSECTING: {intersect}");
 | 
				
			||||||
                        if intersect {
 | 
					                        if intersect {
 | 
				
			||||||
                            // let entry = intersection_map.entry(index).or_insert(Vec::new());
 | 
					                            // let entry = intersection_map.entry(index).or_insert(Vec::new());
 | 
				
			||||||
                            // entry.push(i);
 | 
					                            // entry.push(i);
 | 
				
			||||||
                            println!("INTERSECTING {:?} and {:?}", line.points, l.points);
 | 
					                            // println!("INTERSECTING {:?} and {:?}", line.points, l.points);
 | 
				
			||||||
                            intersections.push(i);
 | 
					                            intersections.push(i);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -56,34 +148,20 @@ pub fn union_lines(lines: &[LinePath]) -> Vec<ConductorNet> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    println!("{intersection_map:?}");
 | 
					 | 
				
			||||||
    let mut final_geo = Vec::new();
 | 
					    let mut final_geo = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // go through all line segments
 | 
					    // go through all line segments
 | 
				
			||||||
    for i in 0..intersection_map.len() {
 | 
					    for i in 0..intersection_map.len() {
 | 
				
			||||||
        if let Some(mut intersections) = intersection_map.remove(&i) {
 | 
					        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
 | 
					            // get current line path
 | 
				
			||||||
            let mut geo = lines[i].outline.clone();
 | 
					            let mut geo = lines[i].outline.clone();
 | 
				
			||||||
            let mut included_points = lines[i].points.clone();
 | 
					            let mut included_points = lines[i].points.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // union with intersecting lines until done
 | 
					            // union with intersecting lines until done
 | 
				
			||||||
            println!("Intersection points: {intersections:?}");
 | 
					 | 
				
			||||||
            while let Some(other) = intersections.pop() {
 | 
					            while let Some(other) = intersections.pop() {
 | 
				
			||||||
                println!("Intersecting with line # {other:?}");
 | 
					 | 
				
			||||||
                // union with intersecting line
 | 
					                // union with intersecting line
 | 
				
			||||||
                let intersecting_line = &lines[other];
 | 
					                let intersecting_line = &lines[other];
 | 
				
			||||||
                if let Some(union) = union(&geo, &intersecting_line.outline) {
 | 
					                if let Some(union) = union_function(&geo, &intersecting_line.outline) {
 | 
				
			||||||
                    geo = union;
 | 
					                    geo = union;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                // add points of added line to included points
 | 
					                // add points of added line to included points
 | 
				
			||||||
@ -115,23 +193,16 @@ pub fn union_lines(lines: &[LinePath]) -> Vec<ConductorNet> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 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
 | 
					    final_geo
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn union_with_apertures(
 | 
					pub fn union_with_apertures(
 | 
				
			||||||
    apertures: &Vec<Element>,
 | 
					    apertures: &Vec<Element>,
 | 
				
			||||||
    conductors: Vec<ConductorNet>,
 | 
					    conductors: Vec<ConductorNet>,
 | 
				
			||||||
) -> Option<ClipperPaths> {
 | 
					) -> Option<UnitedNets> {
 | 
				
			||||||
    let mut finalized_paths = Vec::new(); // handle apertures without connection
 | 
					    let mut isolated_apertures = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // let mut finalized_paths = Vec::new(); // handle apertures without connection
 | 
				
			||||||
    let mut current_conductors = conductors;
 | 
					    let mut current_conductors = conductors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // go through all apertures
 | 
					    // go through all apertures
 | 
				
			||||||
@ -152,7 +223,7 @@ pub fn union_with_apertures(
 | 
				
			|||||||
                .any(|c| ap.outline().is_point_inside(c.into()) != PointInPolygonResult::IsOutside)
 | 
					                .any(|c| ap.outline().is_point_inside(c.into()) != PointInPolygonResult::IsOutside)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                // union aperture with conductor net
 | 
					                // union aperture with conductor net
 | 
				
			||||||
                let geo = union(&geo, &conductor.outline)?;
 | 
					                let geo = union_function(&geo, &conductor.outline)?;
 | 
				
			||||||
                let mut cond = conductor;
 | 
					                let mut cond = conductor;
 | 
				
			||||||
                cond.outline = geo;
 | 
					                cond.outline = geo;
 | 
				
			||||||
                isolated = false;
 | 
					                isolated = false;
 | 
				
			||||||
@ -164,24 +235,21 @@ pub fn union_with_apertures(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // add aperture to extra container if isolated
 | 
					        // add aperture to extra container if isolated
 | 
				
			||||||
        if isolated {
 | 
					        if isolated {
 | 
				
			||||||
            finalized_paths.push(geo);
 | 
					            // finalized_paths.push(geo);
 | 
				
			||||||
 | 
					            isolated_apertures.push(ap.clone());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // update current conductors
 | 
					        // update current conductors
 | 
				
			||||||
        current_conductors = new_conductors;
 | 
					        current_conductors = new_conductors;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for conductor in current_conductors {
 | 
					    Some(UnitedNets {
 | 
				
			||||||
        finalized_paths.push(conductor.outline);
 | 
					        conductors: current_conductors,
 | 
				
			||||||
    }
 | 
					        isolated_apertures,
 | 
				
			||||||
 | 
					 | 
				
			||||||
    finalized_paths.into_iter().reduce(|mut all, paths| {
 | 
					 | 
				
			||||||
        all.push(paths);
 | 
					 | 
				
			||||||
        all
 | 
					 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn union(path1: &ClipperPaths, path2: &ClipperPaths) -> Option<ClipperPaths> {
 | 
					pub fn union_function(path1: &ClipperPaths, path2: &ClipperPaths) -> Option<ClipperPaths> {
 | 
				
			||||||
    path1
 | 
					    path1
 | 
				
			||||||
        .to_clipper_subject()
 | 
					        .to_clipper_subject()
 | 
				
			||||||
        .add_clip(path2.clone())
 | 
					        .add_clip(path2.clone())
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,170 @@
 | 
				
			|||||||
use gerber_types::MacroContent;
 | 
					use std::str::Chars;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn parse(data: &str) -> Option<MacroContent> {
 | 
					use gerber_types::{
 | 
				
			||||||
    todo!()
 | 
					    ApertureMacro, CenterLinePrimitive, CirclePrimitive, MacroContent, MacroDecimal,
 | 
				
			||||||
 | 
					    OutlinePrimitive, PolygonPrimitive, VectorLinePrimitive,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::doc::GerberDoc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn start_macro(doc: &mut GerberDoc, macro_lines: &mut Option<Vec<String>>, line: &str) {
 | 
				
			||||||
 | 
					    *macro_lines = Some(vec![line.replacen("%AM", "", 1)]);
 | 
				
			||||||
 | 
					    if line.ends_with("*%") {
 | 
				
			||||||
 | 
					        if let Some(aperture_macro) = parse(macro_lines.as_ref()) {
 | 
				
			||||||
 | 
					            doc.aperture_macros
 | 
				
			||||||
 | 
					                .insert(aperture_macro.name.to_string(), aperture_macro);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        *macro_lines = None;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn continue_macro(doc: &mut GerberDoc, macro_lines: &mut Option<Vec<String>>, line: &str) {
 | 
				
			||||||
 | 
					    if let Some(lines) = macro_lines {
 | 
				
			||||||
 | 
					        lines.push(line.to_string());
 | 
				
			||||||
 | 
					        if line.ends_with("*%") {
 | 
				
			||||||
 | 
					            if let Some(aperture_macro) = parse(macro_lines.as_ref()) {
 | 
				
			||||||
 | 
					                doc.aperture_macros
 | 
				
			||||||
 | 
					                    .insert(aperture_macro.name.to_string(), aperture_macro);
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            *macro_lines = None;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        start_macro(doc, macro_lines, line);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn parse(data: Option<&Vec<String>>) -> Option<ApertureMacro> {
 | 
				
			||||||
 | 
					    let mut name = "";
 | 
				
			||||||
 | 
					    let mut macro_content = Vec::new();
 | 
				
			||||||
 | 
					    let lines = data?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Go through every very part in the macro
 | 
				
			||||||
 | 
					    for (index, line) in lines.iter().enumerate() {
 | 
				
			||||||
 | 
					        // get name
 | 
				
			||||||
 | 
					        if index == 0 {
 | 
				
			||||||
 | 
					            name = line.strip_suffix("*")?;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut chars = line.chars();
 | 
				
			||||||
 | 
					        // remove % char of last line
 | 
				
			||||||
 | 
					        if index == lines.len() - 1 {
 | 
				
			||||||
 | 
					            chars.next_back();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match chars.next()? {
 | 
				
			||||||
 | 
					            // Comment
 | 
				
			||||||
 | 
					            '0' => macro_content.push(MacroContent::Comment(line.to_string())),
 | 
				
			||||||
 | 
					            // Circle
 | 
				
			||||||
 | 
					            '1' => {
 | 
				
			||||||
 | 
					                let args = get_attr_args(chars)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                macro_content.push(MacroContent::Circle(CirclePrimitive {
 | 
				
			||||||
 | 
					                    exposure: args.first()? == &MacroDecimal::Value(1.0),
 | 
				
			||||||
 | 
					                    diameter: args.get(1)?.clone(),
 | 
				
			||||||
 | 
					                    center: (args.get(2)?.clone(), args.get(3)?.clone()),
 | 
				
			||||||
 | 
					                    angle: match args.get(4) {
 | 
				
			||||||
 | 
					                        Some(arg) => Some(arg.clone()),
 | 
				
			||||||
 | 
					                        None => Some(MacroDecimal::Value(0.)),
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                }));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            '2' => {
 | 
				
			||||||
 | 
					                match chars.next()? {
 | 
				
			||||||
 | 
					                    // Vector Line
 | 
				
			||||||
 | 
					                    '0' => {
 | 
				
			||||||
 | 
					                        let args = get_attr_args(chars)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        macro_content.push(MacroContent::VectorLine(VectorLinePrimitive {
 | 
				
			||||||
 | 
					                            exposure: args.first()? == &MacroDecimal::Value(1.0),
 | 
				
			||||||
 | 
					                            end: (args.get(4)?.clone(), args.get(5)?.clone()),
 | 
				
			||||||
 | 
					                            width: args.get(1)?.clone(),
 | 
				
			||||||
 | 
					                            start: (args.get(2)?.clone(), args.get(3)?.clone()),
 | 
				
			||||||
 | 
					                            angle: match args.get(6) {
 | 
				
			||||||
 | 
					                                Some(arg) => arg.clone(),
 | 
				
			||||||
 | 
					                                None => MacroDecimal::Value(0.),
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                        }));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    // Center Line
 | 
				
			||||||
 | 
					                    '1' => {
 | 
				
			||||||
 | 
					                        let args = get_attr_args(chars)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        macro_content.push(MacroContent::CenterLine(CenterLinePrimitive {
 | 
				
			||||||
 | 
					                            exposure: args.first()? == &MacroDecimal::Value(1.0),
 | 
				
			||||||
 | 
					                            dimensions: (args.get(1)?.clone(), args.get(2)?.clone()),
 | 
				
			||||||
 | 
					                            center: (args.get(3)?.clone(), args.get(4)?.clone()),
 | 
				
			||||||
 | 
					                            angle: match args.get(6) {
 | 
				
			||||||
 | 
					                                Some(arg) => arg.clone(),
 | 
				
			||||||
 | 
					                                None => MacroDecimal::Value(0.),
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                        }));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    _ => panic!("Unknown command in macro line:\n{} | {}", index, line),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // Outline
 | 
				
			||||||
 | 
					            '4' => {
 | 
				
			||||||
 | 
					                let args = get_attr_args(chars)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                macro_content.push(MacroContent::Outline(OutlinePrimitive {
 | 
				
			||||||
 | 
					                    exposure: args.first()? == &MacroDecimal::Value(1.0),
 | 
				
			||||||
 | 
					                    points: (args)
 | 
				
			||||||
 | 
					                        .chunks_exact(2)
 | 
				
			||||||
 | 
					                        .map(|c| (c[0].clone(), c[1].clone()))
 | 
				
			||||||
 | 
					                        .collect(),
 | 
				
			||||||
 | 
					                    angle: args.last()?.clone(),
 | 
				
			||||||
 | 
					                }));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // Polygon
 | 
				
			||||||
 | 
					            '5' => {
 | 
				
			||||||
 | 
					                let args = get_attr_args(chars)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                macro_content.push(MacroContent::Polygon(PolygonPrimitive {
 | 
				
			||||||
 | 
					                    exposure: args.first()? == &MacroDecimal::Value(1.0),
 | 
				
			||||||
 | 
					                    vertices: match args.get(1)? {
 | 
				
			||||||
 | 
					                        MacroDecimal::Value(v) => *v as u8,
 | 
				
			||||||
 | 
					                        _ => 0,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    center: (args.get(2)?.clone(), args.get(3)?.clone()),
 | 
				
			||||||
 | 
					                    diameter: args.get(4)?.clone(),
 | 
				
			||||||
 | 
					                    angle: match args.get(5) {
 | 
				
			||||||
 | 
					                        Some(arg) => arg.clone(),
 | 
				
			||||||
 | 
					                        None => MacroDecimal::Value(0.),
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                }));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            '6' => todo!(), // Moiré
 | 
				
			||||||
 | 
					            '7' => todo!(), // Thermal
 | 
				
			||||||
 | 
					            '$' => todo!(), // Define variable
 | 
				
			||||||
 | 
					            _ => panic!("Unknown command in macro line:\n{} | {}", index, line),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Some(ApertureMacro {
 | 
				
			||||||
 | 
					        name: name.to_string(),
 | 
				
			||||||
 | 
					        content: macro_content,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn get_attr_args(mut attribute_chars: Chars) -> Option<Vec<MacroDecimal>> {
 | 
				
			||||||
 | 
					    attribute_chars.next_back()?;
 | 
				
			||||||
 | 
					    attribute_chars.next()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Some(
 | 
				
			||||||
 | 
					        attribute_chars
 | 
				
			||||||
 | 
					            .as_str()
 | 
				
			||||||
 | 
					            .split(",")
 | 
				
			||||||
 | 
					            .map(|el| el.trim())
 | 
				
			||||||
 | 
					            .map(|el| {
 | 
				
			||||||
 | 
					                if el.starts_with("$") {
 | 
				
			||||||
 | 
					                    MacroDecimal::Expression(el.to_string())
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    MacroDecimal::Value(el.parse::<f64>().unwrap())
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .collect(),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// def parse_content(self):
 | 
					// def parse_content(self):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,8 @@
 | 
				
			|||||||
use ::std::collections::HashMap;
 | 
					use ::std::collections::HashMap;
 | 
				
			||||||
use gerber_types::{Aperture, ApertureDefinition, Command, CoordinateFormat, ExtendedCode, Unit};
 | 
					use gerber_types::{
 | 
				
			||||||
 | 
					    Aperture, ApertureDefinition, ApertureMacro, Command, CoordinateFormat, ExtendedCode, Unit,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
use std::fmt;
 | 
					use std::fmt;
 | 
				
			||||||
use std::iter::repeat;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, PartialEq)]
 | 
					#[derive(Debug, PartialEq)]
 | 
				
			||||||
// Representation of Gerber document
 | 
					// Representation of Gerber document
 | 
				
			||||||
@ -12,6 +13,8 @@ pub struct GerberDoc {
 | 
				
			|||||||
    pub format_specification: Option<CoordinateFormat>,
 | 
					    pub format_specification: Option<CoordinateFormat>,
 | 
				
			||||||
    /// map of apertures which can be used in draw commands later on in the document.
 | 
					    /// map of apertures which can be used in draw commands later on in the document.
 | 
				
			||||||
    pub apertures: HashMap<i32, Aperture>,
 | 
					    pub apertures: HashMap<i32, Aperture>,
 | 
				
			||||||
 | 
					    /// map of aperture macro which can be used to create aperture definitions
 | 
				
			||||||
 | 
					    pub aperture_macros: HashMap<String, ApertureMacro>,
 | 
				
			||||||
    // Anything else, draw commands, comments, attributes
 | 
					    // Anything else, draw commands, comments, attributes
 | 
				
			||||||
    pub commands: Vec<Command>,
 | 
					    pub commands: Vec<Command>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -23,6 +26,7 @@ impl GerberDoc {
 | 
				
			|||||||
            units: None,
 | 
					            units: None,
 | 
				
			||||||
            format_specification: None,
 | 
					            format_specification: None,
 | 
				
			||||||
            apertures: HashMap::new(),
 | 
					            apertures: HashMap::new(),
 | 
				
			||||||
 | 
					            aperture_macros: HashMap::new(),
 | 
				
			||||||
            commands: Vec::new(),
 | 
					            commands: Vec::new(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -33,26 +37,24 @@ impl GerberDoc {
 | 
				
			|||||||
    /// in the gerber-types rust crate. Note that aperture definitions will be sorted by code number
 | 
					    /// in the gerber-types rust crate. Note that aperture definitions will be sorted by code number
 | 
				
			||||||
    /// with lower codes being at the top of the command. This is independent of their order during
 | 
					    /// with lower codes being at the top of the command. This is independent of their order during
 | 
				
			||||||
    /// parsing.
 | 
					    /// parsing.
 | 
				
			||||||
    pub fn to_commands(mut self) -> Vec<Command> {
 | 
					    pub fn into_commands(self) -> Vec<Command> {
 | 
				
			||||||
 | 
					        let mut gerber_doc = self;
 | 
				
			||||||
        let mut gerber_cmds: Vec<Command> = Vec::new();
 | 
					        let mut gerber_cmds: Vec<Command> = Vec::new();
 | 
				
			||||||
        gerber_cmds.push(ExtendedCode::CoordinateFormat(self.format_specification.unwrap()).into());
 | 
					        gerber_cmds
 | 
				
			||||||
        gerber_cmds.push(ExtendedCode::Unit(self.units.unwrap()).into());
 | 
					            .push(ExtendedCode::CoordinateFormat(gerber_doc.format_specification.unwrap()).into());
 | 
				
			||||||
 | 
					        gerber_cmds.push(ExtendedCode::Unit(gerber_doc.units.unwrap()).into());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // we add the apertures to the list, but we sort by code. This means the order of the output
 | 
					        // we add the apertures to the list, but we sort by code. This means the order of the output
 | 
				
			||||||
        // is reproducible every time.
 | 
					        // is reproducible every time.
 | 
				
			||||||
        let mut apertures = self.apertures.into_iter().collect::<Vec<_>>();
 | 
					        let mut apertures = gerber_doc.apertures.into_iter().collect::<Vec<_>>();
 | 
				
			||||||
        apertures.sort_by_key(|tup| tup.0);
 | 
					        apertures.sort_by_key(|tup| tup.0);
 | 
				
			||||||
        for (code, aperture) in apertures {
 | 
					        for (code, aperture) in apertures {
 | 
				
			||||||
            gerber_cmds.push(
 | 
					            gerber_cmds.push(
 | 
				
			||||||
                ExtendedCode::ApertureDefinition(ApertureDefinition {
 | 
					                ExtendedCode::ApertureDefinition(ApertureDefinition { code, aperture }).into(),
 | 
				
			||||||
                    code: code,
 | 
					 | 
				
			||||||
                    aperture: aperture,
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .into(),
 | 
					 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        gerber_cmds.append(&mut self.commands);
 | 
					        gerber_cmds.append(&mut gerber_doc.commands);
 | 
				
			||||||
        // TODO implement for units
 | 
					        // TODO implement for units
 | 
				
			||||||
        gerber_cmds
 | 
					        gerber_cmds
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,7 @@
 | 
				
			|||||||
 | 
					mod aperture_macros;
 | 
				
			||||||
pub mod doc;
 | 
					pub mod doc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use aperture_macros::{continue_macro, start_macro};
 | 
				
			||||||
use doc::GerberDoc;
 | 
					use doc::GerberDoc;
 | 
				
			||||||
use gerber_types::{
 | 
					use gerber_types::{
 | 
				
			||||||
    Aperture, ApertureAttribute, ApertureFunction, Circle, Command, CoordinateFormat,
 | 
					    Aperture, ApertureAttribute, ApertureFunction, Circle, Command, CoordinateFormat,
 | 
				
			||||||
@ -25,12 +27,14 @@ pub fn parse_gerber<T: Read>(reader: BufReader<T>) -> GerberDoc {
 | 
				
			|||||||
    // By default the 'last coordinate' can be taken to be (0,0)
 | 
					    // By default the 'last coordinate' can be taken to be (0,0)
 | 
				
			||||||
    let mut last_coords = (0i64, 0i64);
 | 
					    let mut last_coords = (0i64, 0i64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut aperture_macro: Option<Vec<String>> = None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // naively define some regex terms
 | 
					    // naively define some regex terms
 | 
				
			||||||
    // TODO see which ones can be done without regex for better performance?
 | 
					    // TODO see which ones can be done without regex for better performance?
 | 
				
			||||||
    let re_units = regex!(r#"%MO(.*)\*%"#);
 | 
					    let re_units = regex!(r#"%MO(.*)\*%"#);
 | 
				
			||||||
    let re_comment = regex!(r#"G04 (.*)\*"#);
 | 
					    let re_comment = regex!(r#"G04 (.*)\*"#);
 | 
				
			||||||
    let re_formatspec = regex!(r#"%FSLAX(.*)Y(.*)\*%"#);
 | 
					    let re_formatspec = regex!(r#"%FSLAX(.*)Y(.*)\*%"#);
 | 
				
			||||||
    let re_aperture = regex!(r#"%ADD([0-9]+)([A-Z]),(.*)\*%"#);
 | 
					    let re_aperture = regex!(r#"%ADD([0-9]+)(\w*),(.*)\*%"#);
 | 
				
			||||||
    let re_interpolation = regex!(r#"X?(-?[0-9]+)?Y?(-?[0-9]+)?I?(-?[0-9]+)?J?(-?[0-9]+)?D01\*"#);
 | 
					    let re_interpolation = regex!(r#"X?(-?[0-9]+)?Y?(-?[0-9]+)?I?(-?[0-9]+)?J?(-?[0-9]+)?D01\*"#);
 | 
				
			||||||
    let re_move_or_flash = regex!(r#"X?(-?[0-9]+)?Y?(-?[0-9]+)?D0[2-3]*"#);
 | 
					    let re_move_or_flash = regex!(r#"X?(-?[0-9]+)?Y?(-?[0-9]+)?D0[2-3]*"#);
 | 
				
			||||||
    // TODO: handle escaped characters for attributes
 | 
					    // TODO: handle escaped characters for attributes
 | 
				
			||||||
@ -46,6 +50,12 @@ pub fn parse_gerber<T: Read>(reader: BufReader<T>) -> GerberDoc {
 | 
				
			|||||||
        debug!("{}. {}", index + 1, &line);
 | 
					        debug!("{}. {}", index + 1, &line);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if !line.is_empty() {
 | 
					        if !line.is_empty() {
 | 
				
			||||||
 | 
					            // continue aperture macro if one is active
 | 
				
			||||||
 | 
					            if aperture_macro.is_some() {
 | 
				
			||||||
 | 
					                continue_macro(&mut gerber_doc, &mut aperture_macro, line);
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let mut linechars = line.chars();
 | 
					            let mut linechars = line.chars();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            match linechars.next().unwrap() {
 | 
					            match linechars.next().unwrap() {
 | 
				
			||||||
@ -114,7 +124,8 @@ pub fn parse_gerber<T: Read>(reader: BufReader<T>) -> GerberDoc {
 | 
				
			|||||||
                        'A' => match linechars.next().unwrap() {
 | 
					                        'A' => match linechars.next().unwrap() {
 | 
				
			||||||
                            'D' => parse_aperture_defs(line, re_aperture, &mut gerber_doc), // AD
 | 
					                            'D' => parse_aperture_defs(line, re_aperture, &mut gerber_doc), // AD
 | 
				
			||||||
                            'M' => {
 | 
					                            'M' => {
 | 
				
			||||||
                                panic!("Aperture Macros (AM) are not supported yet.")
 | 
					                                start_macro(&mut gerber_doc, &mut aperture_macro, line);
 | 
				
			||||||
 | 
					                                // panic!("Aperture Macros (AM) are not supported yet.")
 | 
				
			||||||
                            } // AM
 | 
					                            } // AM
 | 
				
			||||||
                            _ => line_parse_failure(line, index),
 | 
					                            _ => line_parse_failure(line, index),
 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
@ -348,7 +359,14 @@ fn parse_aperture_defs(line: &str, re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
				
			|||||||
                }),
 | 
					                }),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            _ => {
 | 
					            _ => {
 | 
				
			||||||
                panic!("Encountered unknown aperture definition statement")
 | 
					                if let Some(ap_macro) = gerber_doc.aperture_macros.get(aperture_type) {
 | 
				
			||||||
 | 
					                    gerber_doc.apertures.insert(
 | 
				
			||||||
 | 
					                        code,
 | 
				
			||||||
 | 
					                        Aperture::Other(format!("{}/{}", ap_macro.name, aperture_args.join(","))),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    panic!("Encountered unknown aperture definition statement")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -511,7 +529,7 @@ fn parse_move_or_flash(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_load_mirroring(mut linechars: Chars, gerber_doc: &mut GerberDoc) {
 | 
					fn parse_load_mirroring(mut _linechars: Chars, _gerber_doc: &mut GerberDoc) {
 | 
				
			||||||
    // match linechars.next().unwrap() {
 | 
					    // match linechars.next().unwrap() {
 | 
				
			||||||
    //     'N' => gerber_doc.commands.push(value), //LMN
 | 
					    //     'N' => gerber_doc.commands.push(value), //LMN
 | 
				
			||||||
    //     'Y' => gerber_doc.commands.push(value), // LMY
 | 
					    //     'Y' => gerber_doc.commands.push(value), // LMY
 | 
				
			||||||
@ -528,7 +546,7 @@ fn parse_load_mirroring(mut linechars: Chars, gerber_doc: &mut GerberDoc) {
 | 
				
			|||||||
// a step and repeat open statement has four (required) parameters that we need to extract
 | 
					// a step and repeat open statement has four (required) parameters that we need to extract
 | 
				
			||||||
// X (pos int) Y (pos int), I (decimal), J (decimal)
 | 
					// X (pos int) Y (pos int), I (decimal), J (decimal)
 | 
				
			||||||
fn parse_step_repeat_open(line: &str, re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
					fn parse_step_repeat_open(line: &str, re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
				
			||||||
    println!("SR line: {}", &line);
 | 
					    // println!("SR line: {}", &line);
 | 
				
			||||||
    if let Some(regmatch) = re.captures(line) {
 | 
					    if let Some(regmatch) = re.captures(line) {
 | 
				
			||||||
        gerber_doc.commands.push(
 | 
					        gerber_doc.commands.push(
 | 
				
			||||||
            ExtendedCode::StepAndRepeat(StepAndRepeat::Open {
 | 
					            ExtendedCode::StepAndRepeat(StepAndRepeat::Open {
 | 
				
			||||||
@ -577,7 +595,7 @@ fn parse_step_repeat_open(line: &str, re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
				
			|||||||
/// ⚠️ Any other Attributes (which seem to be valid within the gerber spec) we will **fail** to parse!
 | 
					/// ⚠️ Any other Attributes (which seem to be valid within the gerber spec) we will **fail** to parse!
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// ⚠️ This parsing statement needs a lot of tests and validation at the current stage!
 | 
					/// ⚠️ This parsing statement needs a lot of tests and validation at the current stage!
 | 
				
			||||||
fn parse_file_attribute(line: Chars, re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
					fn _parse_file_attribute(line: Chars, _re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
				
			||||||
    let attr_args = get_attr_args(line);
 | 
					    let attr_args = get_attr_args(line);
 | 
				
			||||||
    if attr_args.len() >= 2 {
 | 
					    if attr_args.len() >= 2 {
 | 
				
			||||||
        // we must have at least 1 field
 | 
					        // we must have at least 1 field
 | 
				
			||||||
@ -626,7 +644,7 @@ fn parse_file_attribute(line: Chars, re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
				
			|||||||
/// ⚠️ Any other Attributes (which seem to be valid within the gerber spec) we will **fail** to parse!
 | 
					/// ⚠️ Any other Attributes (which seem to be valid within the gerber spec) we will **fail** to parse!
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// ⚠️ This parsing statement needs a lot of tests and validation at the current stage!
 | 
					/// ⚠️ This parsing statement needs a lot of tests and validation at the current stage!
 | 
				
			||||||
fn parse_aperture_attribute(line: Chars, re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
					fn parse_aperture_attribute(line: Chars, _re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
				
			||||||
    let attr_args = get_attr_args(line);
 | 
					    let attr_args = get_attr_args(line);
 | 
				
			||||||
    if attr_args.len() >= 2 {
 | 
					    if attr_args.len() >= 2 {
 | 
				
			||||||
        // we must have at least 1 field
 | 
					        // we must have at least 1 field
 | 
				
			||||||
@ -708,7 +726,7 @@ fn parse_aperture_attribute(line: Chars, re: &Regex, gerber_doc: &mut GerberDoc)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_object_attribute(line: Chars, re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
					fn parse_object_attribute(line: Chars, _re: &Regex, _gerber_doc: &mut GerberDoc) {
 | 
				
			||||||
    let attr_args = get_attr_args(line);
 | 
					    let attr_args = get_attr_args(line);
 | 
				
			||||||
    if attr_args.len() >= 2 {
 | 
					    if attr_args.len() >= 2 {
 | 
				
			||||||
        // gerber_doc.commands.push(
 | 
					        // gerber_doc.commands.push(
 | 
				
			||||||
@ -728,7 +746,7 @@ fn parse_object_attribute(line: Chars, re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_delete_attribute(line: Chars, re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
					fn parse_delete_attribute(line: Chars, _re: &Regex, gerber_doc: &mut GerberDoc) {
 | 
				
			||||||
    let attr_args = get_attr_args(line);
 | 
					    let attr_args = get_attr_args(line);
 | 
				
			||||||
    match attr_args.len() {
 | 
					    match attr_args.len() {
 | 
				
			||||||
        1 => gerber_doc
 | 
					        1 => gerber_doc
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										29
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/main.rs
									
									
									
									
									
								
							@ -6,13 +6,15 @@ mod export;
 | 
				
			|||||||
mod geometry;
 | 
					mod geometry;
 | 
				
			||||||
mod gerber;
 | 
					mod gerber;
 | 
				
			||||||
mod outline_geometry;
 | 
					mod outline_geometry;
 | 
				
			||||||
 | 
					mod resources;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use application::Application;
 | 
					use application::Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use eframe::egui;
 | 
					use eframe::egui::{self, IconData};
 | 
				
			||||||
use tracing_subscriber::{filter, layer::SubscriberExt, util::SubscriberInitExt, Layer};
 | 
					use tracing_subscriber::{filter, layer::SubscriberExt, util::SubscriberInitExt, Layer};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const APP_NAME: &str = "Outlinify";
 | 
					const APP_NAME: &str = "Outlinify";
 | 
				
			||||||
 | 
					const ICON: &[u8] = include_bytes!("../resources/icon.png");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() -> eframe::Result {
 | 
					fn main() -> eframe::Result {
 | 
				
			||||||
    let stdout_log = tracing_subscriber::fmt::layer()
 | 
					    let stdout_log = tracing_subscriber::fmt::layer()
 | 
				
			||||||
@ -28,17 +30,38 @@ fn main() -> eframe::Result {
 | 
				
			|||||||
    let application = Application::new();
 | 
					    let application = Application::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let options = eframe::NativeOptions {
 | 
					    let options = eframe::NativeOptions {
 | 
				
			||||||
        viewport: egui::ViewportBuilder::default().with_inner_size([900.0, 700.0]),
 | 
					        viewport: egui::ViewportBuilder::default()
 | 
				
			||||||
 | 
					            .with_inner_size([900.0, 700.0])
 | 
				
			||||||
 | 
					            .with_icon(load_icon()),
 | 
				
			||||||
        ..Default::default()
 | 
					        ..Default::default()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    eframe::run_native(
 | 
					    eframe::run_native(
 | 
				
			||||||
        APP_NAME,
 | 
					        APP_NAME,
 | 
				
			||||||
        options,
 | 
					        options,
 | 
				
			||||||
        Box::new(|cc| {
 | 
					        Box::new(|cc| {
 | 
				
			||||||
            // This gives us image support:
 | 
					            // This gives us image support:
 | 
				
			||||||
            // egui_extras::install_image_loaders(&cc.egui_ctx);
 | 
					            egui_extras::install_image_loaders(&cc.egui_ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Ok(Box::new(application))
 | 
					            Ok(Box::new(application))
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn load_icon() -> IconData {
 | 
				
			||||||
 | 
					    let (icon_rgba, icon_width, icon_height) = {
 | 
				
			||||||
 | 
					        let icon = ICON;
 | 
				
			||||||
 | 
					        let image = image::load_from_memory(icon)
 | 
				
			||||||
 | 
					            .expect("Failed to open icon path")
 | 
				
			||||||
 | 
					            .into_rgba8();
 | 
				
			||||||
 | 
					        let (width, height) = image.dimensions();
 | 
				
			||||||
 | 
					        let rgba = image.into_raw();
 | 
				
			||||||
 | 
					        (rgba, width, height)
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IconData {
 | 
				
			||||||
 | 
					        rgba: icon_rgba,
 | 
				
			||||||
 | 
					        width: icon_width,
 | 
				
			||||||
 | 
					        height: icon_height,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,11 @@
 | 
				
			|||||||
use clipper2::{Bounds, EndType, JoinType, One, Paths};
 | 
					use clipper2::{EndType, JoinType};
 | 
				
			||||||
use eframe::{
 | 
					 | 
				
			||||||
    egui::{Rect, Shape, Stroke},
 | 
					 | 
				
			||||||
    epaint::{PathShape, PathStroke},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use egui_plot::{PlotBounds, PlotItem, PlotPoint, Polygon};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    application::CanvasColour,
 | 
					    excellon::drills::Drills,
 | 
				
			||||||
    geometry::{elements::circle::Circle, point::Point, ClipperBounds, ClipperPaths, Unit},
 | 
					    geometry::{
 | 
				
			||||||
 | 
					        point::Point, union::UnitedNets, ClipperBounds, ClipperPath, ClipperPaths, DrawableRaw,
 | 
				
			||||||
 | 
					        Unit,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
@ -16,10 +14,8 @@ pub enum GeometryType {
 | 
				
			|||||||
    Points(Vec<Point>),
 | 
					    Points(Vec<Point>),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub struct OutlineGeometry {
 | 
					pub struct OutlineGeometry {
 | 
				
			||||||
    // pub path: ClipperPaths,
 | 
					 | 
				
			||||||
    // pub points: Vec<Point>,
 | 
					 | 
				
			||||||
    items: GeometryType,
 | 
					    items: GeometryType,
 | 
				
			||||||
    pub stroke: f32,
 | 
					    pub stroke: f32,
 | 
				
			||||||
    pub unit: Unit,
 | 
					    pub unit: Unit,
 | 
				
			||||||
@ -28,26 +24,31 @@ pub struct OutlineGeometry {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl OutlineGeometry {
 | 
					impl OutlineGeometry {
 | 
				
			||||||
    pub fn new(
 | 
					    pub fn new(united_nets: &UnitedNets, stroke: f32, unit: Unit, bounds_from: &str) -> Self {
 | 
				
			||||||
        outline: &ClipperPaths,
 | 
					        let mut outline_paths = ClipperPaths::new(vec![]);
 | 
				
			||||||
        stroke: f32,
 | 
					        // inflate conductor net paths
 | 
				
			||||||
        unit: Unit,
 | 
					        for net in &united_nets.conductors {
 | 
				
			||||||
        bounds_from: &str,
 | 
					            outline_paths.push(
 | 
				
			||||||
        bounds: ClipperBounds,
 | 
					                net.outline
 | 
				
			||||||
    ) -> Self {
 | 
					                    .inflate((stroke / 2.).into(), JoinType::Round, EndType::Polygon, 2.)
 | 
				
			||||||
        // inflate given path
 | 
					                    .simplify(0.001, false),
 | 
				
			||||||
        let outline = outline
 | 
					            )
 | 
				
			||||||
            .clone()
 | 
					        }
 | 
				
			||||||
            .inflate((stroke / 2.).into(), JoinType::Miter, EndType::Polygon, 30.)
 | 
					        // inflate isolated apertures
 | 
				
			||||||
            .simplify(0.01, false);
 | 
					        for ap in &united_nets.isolated_apertures {
 | 
				
			||||||
 | 
					            outline_paths.push(
 | 
				
			||||||
 | 
					                ap.to_paths()
 | 
				
			||||||
 | 
					                    .inflate((stroke / 2.).into(), JoinType::Round, EndType::Polygon, 2.)
 | 
				
			||||||
 | 
					                    .simplify(0.001, false),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            // path: outline,
 | 
					            items: GeometryType::Paths(outline_paths),
 | 
				
			||||||
            // points: Vec::new(),
 | 
					 | 
				
			||||||
            items: GeometryType::Paths(outline),
 | 
					 | 
				
			||||||
            stroke,
 | 
					            stroke,
 | 
				
			||||||
            unit,
 | 
					            unit,
 | 
				
			||||||
            bounds_from: bounds_from.into(),
 | 
					            bounds_from: bounds_from.into(),
 | 
				
			||||||
            bounding_box: bounds,
 | 
					            bounding_box: united_nets.bounds(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -56,34 +57,30 @@ impl OutlineGeometry {
 | 
				
			|||||||
        stroke: f32,
 | 
					        stroke: f32,
 | 
				
			||||||
        unit: Unit,
 | 
					        unit: Unit,
 | 
				
			||||||
        bounds_from: &str,
 | 
					        bounds_from: &str,
 | 
				
			||||||
        bounds: ClipperBounds,
 | 
					 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            // path: outline.clone(),
 | 
					 | 
				
			||||||
            // points: Vec::new(),
 | 
					 | 
				
			||||||
            items: GeometryType::Paths(outline.clone()),
 | 
					            items: GeometryType::Paths(outline.clone()),
 | 
				
			||||||
            stroke,
 | 
					            stroke,
 | 
				
			||||||
            unit,
 | 
					            unit,
 | 
				
			||||||
            bounds_from: bounds_from.into(),
 | 
					            bounds_from: bounds_from.into(),
 | 
				
			||||||
            bounding_box: bounds,
 | 
					            bounding_box: outline.bounds(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn point_marker(
 | 
					    pub fn drill_marker(drills: &Drills, stroke: f32, unit: Unit, bounds_from: &str) -> Self {
 | 
				
			||||||
        points: Vec<Point>,
 | 
					 | 
				
			||||||
        stroke: f32,
 | 
					 | 
				
			||||||
        unit: Unit,
 | 
					 | 
				
			||||||
        bounds_from: &str,
 | 
					 | 
				
			||||||
        bounds: ClipperBounds,
 | 
					 | 
				
			||||||
    ) -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            // path: Paths::new(vec![]),
 | 
					            items: GeometryType::Points(drills.holes.iter().map(|c| c.canvas_pos()).collect()),
 | 
				
			||||||
            // points,
 | 
					 | 
				
			||||||
            items: GeometryType::Points(points),
 | 
					 | 
				
			||||||
            stroke,
 | 
					            stroke,
 | 
				
			||||||
            unit,
 | 
					            unit,
 | 
				
			||||||
            bounds_from: bounds_from.into(),
 | 
					            bounds_from: bounds_from.into(),
 | 
				
			||||||
            bounding_box: bounds,
 | 
					            bounding_box: ClipperPaths::from(
 | 
				
			||||||
 | 
					                drills
 | 
				
			||||||
 | 
					                    .holes
 | 
				
			||||||
 | 
					                    .iter()
 | 
				
			||||||
 | 
					                    .map(|hole| hole.outline.clone())
 | 
				
			||||||
 | 
					                    .collect::<Vec<ClipperPath>>(),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .bounds(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -101,114 +98,3 @@ impl OutlineGeometry {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
pub struct OutlineShape {
 | 
					 | 
				
			||||||
    stroke: f32,
 | 
					 | 
				
			||||||
    selected: bool,
 | 
					 | 
				
			||||||
    colour: CanvasColour,
 | 
					 | 
				
			||||||
    items: GeometryType,
 | 
					 | 
				
			||||||
    bounds: ClipperBounds,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// impl OutlineShape {
 | 
					 | 
				
			||||||
//     pub fn new_from_geometry(
 | 
					 | 
				
			||||||
//         geometry: &OutlineGeometry,
 | 
					 | 
				
			||||||
//         colour: CanvasColour,
 | 
					 | 
				
			||||||
//         selected: bool,
 | 
					 | 
				
			||||||
//     ) -> Self {
 | 
					 | 
				
			||||||
//         Self {
 | 
					 | 
				
			||||||
//             stroke: geometry.stroke,
 | 
					 | 
				
			||||||
//             selected,
 | 
					 | 
				
			||||||
//             items: geometry.items.clone(),
 | 
					 | 
				
			||||||
//             bounds: geometry.bounding_box,
 | 
					 | 
				
			||||||
//             colour,
 | 
					 | 
				
			||||||
//         }
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// impl PlotItem for OutlineShape {
 | 
					 | 
				
			||||||
//     fn shapes(
 | 
					 | 
				
			||||||
//         &self,
 | 
					 | 
				
			||||||
//         _ui: &eframe::egui::Ui,
 | 
					 | 
				
			||||||
//         transform: &egui_plot::PlotTransform,
 | 
					 | 
				
			||||||
//         shapes: &mut Vec<eframe::egui::Shape>,
 | 
					 | 
				
			||||||
//     ) {
 | 
					 | 
				
			||||||
//         match &self.items {
 | 
					 | 
				
			||||||
//             GeometryType::Paths(paths) => {
 | 
					 | 
				
			||||||
//                 for path in paths.iter() {
 | 
					 | 
				
			||||||
//                     let values_tf: Vec<_> = path
 | 
					 | 
				
			||||||
//                         .iter()
 | 
					 | 
				
			||||||
//                         .map(|v| transform.position_from_point(&PlotPoint::from(Point::from(v))))
 | 
					 | 
				
			||||||
//                         .collect();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//                     let shape = PathShape::closed_line(
 | 
					 | 
				
			||||||
//                         values_tf.clone(),
 | 
					 | 
				
			||||||
//                         PathStroke::new(20., self.colour.to_colour32(self.selected)),
 | 
					 | 
				
			||||||
//                     );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//                     shapes.push(shape.into());
 | 
					 | 
				
			||||||
//                 }
 | 
					 | 
				
			||||||
//             }
 | 
					 | 
				
			||||||
//             GeometryType::Points(points) => {
 | 
					 | 
				
			||||||
//                 for point in points {
 | 
					 | 
				
			||||||
//                     // draw outline of hole
 | 
					 | 
				
			||||||
//                     let points = Circle::circle_segment_points(
 | 
					 | 
				
			||||||
//                         Point::from(transform.position_from_point(&PlotPoint::from(*point))),
 | 
					 | 
				
			||||||
//                         self.stroke.into(),
 | 
					 | 
				
			||||||
//                         1.,
 | 
					 | 
				
			||||||
//                         0.,
 | 
					 | 
				
			||||||
//                     )
 | 
					 | 
				
			||||||
//                     .into_iter()
 | 
					 | 
				
			||||||
//                     .map(|p| Point::from(p).into())
 | 
					 | 
				
			||||||
//                     .collect();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//                     let polygon = Shape::convex_polygon(
 | 
					 | 
				
			||||||
//                         points,
 | 
					 | 
				
			||||||
//                         self.colour.to_colour32(self.selected),
 | 
					 | 
				
			||||||
//                         Stroke::NONE,
 | 
					 | 
				
			||||||
//                     );
 | 
					 | 
				
			||||||
//                     shapes.push(polygon);
 | 
					 | 
				
			||||||
//                 }
 | 
					 | 
				
			||||||
//                 // TODO draw point circles
 | 
					 | 
				
			||||||
//             }
 | 
					 | 
				
			||||||
//         }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//         // todo!()
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     fn initialize(&mut self, x_range: std::ops::RangeInclusive<f64>) {
 | 
					 | 
				
			||||||
//         {}
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     fn name(&self) -> &str {
 | 
					 | 
				
			||||||
//         "Test"
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     fn color(&self) -> eframe::egui::Color32 {
 | 
					 | 
				
			||||||
//         self.colour.to_colour32(self.selected)
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     fn highlight(&mut self) {
 | 
					 | 
				
			||||||
//         {}
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     fn highlighted(&self) -> bool {
 | 
					 | 
				
			||||||
//         false
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     fn allow_hover(&self) -> bool {
 | 
					 | 
				
			||||||
//         false
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     fn geometry(&self) -> egui_plot::PlotGeometry<'_> {
 | 
					 | 
				
			||||||
//         todo!()
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     fn bounds(&self) -> PlotBounds {
 | 
					 | 
				
			||||||
//         PlotBounds::from_min_max(self.bounds.min.into(), self.bounds.max.into())
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     fn id(&self) -> Option<eframe::egui::Id> {
 | 
					 | 
				
			||||||
//         None
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
// }
 | 
					 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								src/resources/errors.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/resources/errors.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use error_stack::{Context, Report};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct ResourceError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ResourceError {
 | 
				
			||||||
 | 
					    pub fn new(text: &str) -> Report<Self> {
 | 
				
			||||||
 | 
					        Report::new(Self).attach_printable(text.to_string())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for ResourceError {
 | 
				
			||||||
 | 
					    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        fmt.write_str("Error loading resource")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Context for ResourceError {}
 | 
				
			||||||
							
								
								
									
										37
									
								
								src/resources/fonts.rs
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										37
									
								
								src/resources/fonts.rs
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					use eframe::egui::{FontData, FontDefinitions};
 | 
				
			||||||
 | 
					// const FONT_ROBOTO: &[u8] = include_bytes!("../../resources/Roboto-Light.ttf");
 | 
				
			||||||
 | 
					// const FONT_ROBOTO_MONO: &[u8] = include_bytes!("../../resources/RobotoMono-Light.ttf");
 | 
				
			||||||
 | 
					const FONT_OPEN_SANS: &[u8] = include_bytes!("../../resources/OpenSans-Light.ttf");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// pub fn register_fonts(style: &mut Style) {
 | 
				
			||||||
 | 
					//     use eframe::epaint::FontFamily::{Monospace, Proportional};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//     style.text_styles = [
 | 
				
			||||||
 | 
					//         (TextStyle::Body, FontId::new(21.0, Proportional)),
 | 
				
			||||||
 | 
					//         (TextStyle::Button, FontId::new(18.0, Monospace)),
 | 
				
			||||||
 | 
					//         (TextStyle::Monospace, FontId::new(18.0, Monospace)),
 | 
				
			||||||
 | 
					//         (TextStyle::Small, FontId::new(16.0, Proportional)),
 | 
				
			||||||
 | 
					//         (TextStyle::Heading, FontId::new(48.0, Proportional)),
 | 
				
			||||||
 | 
					//     ]
 | 
				
			||||||
 | 
					//     .into();
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn load_fonts() -> FontDefinitions {
 | 
				
			||||||
 | 
					    use eframe::epaint::FontFamily::{Monospace, Proportional};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut fonts = FontDefinitions::default();
 | 
				
			||||||
 | 
					    // let roboto = FontData::from_static(FONT_ROBOTO);
 | 
				
			||||||
 | 
					    // let roboto_mono = FontData::from_static(FONT_ROBOTO_MONO);
 | 
				
			||||||
 | 
					    let open_sans = FontData::from_static(FONT_OPEN_SANS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // fonts.font_data.insert("roboto".into(), roboto);
 | 
				
			||||||
 | 
					    // fonts.font_data.insert("roboto_mono".into(), roboto_mono);
 | 
				
			||||||
 | 
					    fonts.font_data.insert("OpenSans".into(), open_sans);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // fonts.families.insert(Monospace, vec!["roboto_mono".into()]);
 | 
				
			||||||
 | 
					    // fonts.families.insert(Monospace, vec!["roboto".into()]);
 | 
				
			||||||
 | 
					    fonts.families.insert(Proportional, vec!["OpenSans".into()]);
 | 
				
			||||||
 | 
					    fonts.families.insert(Monospace, vec!["OpenSans".into()]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fonts
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								src/resources/icons.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/resources/icons.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					use eframe::egui::{include_image, ImageSource};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Icons<'a> {
 | 
				
			||||||
 | 
					    pub gerber_file: ImageSource<'a>,
 | 
				
			||||||
 | 
					    pub excellon_file: ImageSource<'a>,
 | 
				
			||||||
 | 
					    pub geometry_file: ImageSource<'a>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a> Icons<'a> {
 | 
				
			||||||
 | 
					    pub fn preload() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            gerber_file: include_image!("../../resources/gerber_file.png"),
 | 
				
			||||||
 | 
					            excellon_file: include_image!("../../resources/excellon_file.png"),
 | 
				
			||||||
 | 
					            geometry_file: include_image!("../../resources/geometry_file.png"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/resources/mod.rs
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										25
									
								
								src/resources/mod.rs
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					// mod fonts;
 | 
				
			||||||
 | 
					mod errors;
 | 
				
			||||||
 | 
					mod icons;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub use self::icons::Icons;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct ResourceLoader<'a> {
 | 
				
			||||||
 | 
					    icons: Icons<'a>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a> ResourceLoader<'a> {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            icons: Icons::preload(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // pub fn fonts(&self) -> FontDefinitions {
 | 
				
			||||||
 | 
					    //     fonts::load_fonts()
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn icons(&self) -> &Icons {
 | 
				
			||||||
 | 
					        &self.icons
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user