use crate::types::{Coord, Layer, ParameterCollection};
use super::polygon::{HatchStyle, PolygonType, PolygonVertex, PolygonVertexKind};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum DisplayUnit {
#[default]
Imperial = 0,
Metric = 1,
}
impl DisplayUnit {
pub fn from_int(value: i32) -> Self {
match value {
1 => DisplayUnit::Metric,
_ => DisplayUnit::Imperial,
}
}
pub fn to_int(self) -> i32 {
self as i32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum DesignatorDisplayMode {
#[default]
Physical = 0,
Logical = 1,
}
impl DesignatorDisplayMode {
pub fn from_int(value: i32) -> Self {
match value {
1 => DesignatorDisplayMode::Logical,
_ => DesignatorDisplayMode::Physical,
}
}
pub fn to_int(self) -> i32 {
self as i32
}
}
#[derive(Debug, Clone, Default)]
pub struct PcbBoard {
pub layer: Layer,
pub locked: bool,
pub polygon_outline: bool,
pub filename: String,
pub kind: String,
pub version: String,
pub date: String,
pub time: String,
pub origin_x: Coord,
pub origin_y: Coord,
pub big_visible_grid_size: f64,
pub visible_grid_size: f64,
pub electrical_grid_range: Coord,
pub electrical_grid_enabled: bool,
pub snap_grid_size: f64,
pub snap_grid_size_x: f64,
pub snap_grid_size_y: f64,
pub track_grid_size: f64,
pub via_grid_size: f64,
pub component_grid_size: f64,
pub component_grid_size_x: f64,
pub component_grid_size_y: f64,
pub dot_grid: bool,
pub display_unit: DisplayUnit,
pub designator_display_mode: DesignatorDisplayMode,
pub primitive_lock: bool,
pub polygon_type: PolygonType,
pub pour_over: bool,
pub remove_dead: bool,
pub grid_size: Coord,
pub track_width: Coord,
pub hatch_style: HatchStyle,
pub use_octagons: bool,
pub min_prim_length: Coord,
pub outline: Vec<PolygonVertex>,
pub params: ParameterCollection,
}
impl PcbBoard {
pub fn from_params(params: &ParameterCollection) -> Self {
let mut board = Self {
layer: params
.get("LAYER")
.map(|v| v.as_layer())
.unwrap_or_default(),
locked: params
.get("LOCKED")
.map(|v| v.as_bool_or(false))
.unwrap_or(false),
polygon_outline: params
.get("POLYGONOUTLINE")
.map(|v| v.as_bool_or(false))
.unwrap_or(false),
filename: params
.get("FILENAME")
.map(|v| v.as_str().to_string())
.unwrap_or_default(),
kind: params
.get("KIND")
.map(|v| v.as_str().to_string())
.unwrap_or_default(),
version: params
.get("VERSION")
.map(|v| v.as_str().to_string())
.unwrap_or_default(),
date: params
.get("DATE")
.map(|v| v.as_str().to_string())
.unwrap_or_default(),
time: params
.get("TIME")
.map(|v| v.as_str().to_string())
.unwrap_or_default(),
origin_x: params
.get("ORIGINX")
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
origin_y: params
.get("ORIGINY")
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
big_visible_grid_size: params
.get("BIGVISIBLEGRIDSIZE")
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
visible_grid_size: params
.get("VISIBLEGRIDSIZE")
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
electrical_grid_range: params
.get("ELECTRICALGRIDRANGE")
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
electrical_grid_enabled: params
.get("ELECTRICALGRIDENABLED")
.map(|v| v.as_bool_or(true))
.unwrap_or(true),
snap_grid_size: params
.get("SNAPGRIDSIZE")
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
snap_grid_size_x: params
.get("SNAPGRIDSIZEX")
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
snap_grid_size_y: params
.get("SNAPGRIDSIZEY")
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
track_grid_size: params
.get("TRACKGRIDSIZE")
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
via_grid_size: params
.get("VIAGRIDSIZE")
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
component_grid_size: params
.get("COMPONENTGRIDSIZE")
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
component_grid_size_x: params
.get("COMPONENTGRIDSIZEX")
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
component_grid_size_y: params
.get("COMPONENTGRIDSIZEY")
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
dot_grid: params
.get("DOTGRID")
.map(|v| v.as_bool_or(true))
.unwrap_or(true),
display_unit: params
.get("DISPLAYUNIT")
.map(|v| DisplayUnit::from_int(v.as_int_or(0)))
.unwrap_or_default(),
designator_display_mode: params
.get("DESIGNATORDISPLAYMODE")
.map(|v| DesignatorDisplayMode::from_int(v.as_int_or(0)))
.unwrap_or_default(),
primitive_lock: params
.get("PRIMITIVELOCK")
.map(|v| v.as_bool_or(false))
.unwrap_or(false),
polygon_type: params
.get("POLYGONTYPE")
.map(|v| PolygonType::parse(v.as_str()))
.unwrap_or_default(),
pour_over: params
.get("POUROVER")
.map(|v| v.as_bool_or(false))
.unwrap_or(false),
remove_dead: params
.get("REMOVEDEAD")
.map(|v| v.as_bool_or(false))
.unwrap_or(false),
grid_size: params
.get("GRIDSIZE")
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
track_width: params
.get("TRACKWIDTH")
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
hatch_style: params
.get("HATCHSTYLE")
.map(|v| HatchStyle::parse(v.as_str()))
.unwrap_or_default(),
use_octagons: params
.get("USEOCTAGONS")
.map(|v| v.as_bool_or(false))
.unwrap_or(false),
min_prim_length: params
.get("MINPRIMLENGTH")
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
outline: Vec::new(),
params: params.clone(),
};
let mut idx = 0;
loop {
let vx_key = format!("VX{}", idx);
let vy_key = format!("VY{}", idx);
if !params.contains(&vx_key) {
break;
}
let vertex = PolygonVertex {
kind: params
.get(&format!("KIND{}", idx))
.map(|v| PolygonVertexKind::from_int(v.as_int_or(0)))
.unwrap_or_default(),
x: params
.get(&vx_key)
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
y: params
.get(&vy_key)
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
center_x: params
.get(&format!("CX{}", idx))
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
center_y: params
.get(&format!("CY{}", idx))
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
start_angle: params
.get(&format!("SA{}", idx))
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
end_angle: params
.get(&format!("EA{}", idx))
.map(|v| v.as_double_or(0.0))
.unwrap_or(0.0),
radius: params
.get(&format!("R{}", idx))
.and_then(|v| v.as_coord().ok())
.unwrap_or_default(),
};
board.outline.push(vertex);
idx += 1;
}
board
}
pub fn to_params(&self) -> ParameterCollection {
let mut params = self.params.clone();
params.add("LAYER", &self.layer.to_string());
params.add("LOCKED", if self.locked { "TRUE" } else { "FALSE" });
params.add(
"POLYGONOUTLINE",
if self.polygon_outline {
"TRUE"
} else {
"FALSE"
},
);
params.add("FILENAME", &self.filename);
params.add("KIND", &self.kind);
params.add("VERSION", &self.version);
params.add("DATE", &self.date);
params.add("TIME", &self.time);
params.add_coord("ORIGINX", self.origin_x);
params.add_coord("ORIGINY", self.origin_y);
params.add_double("BIGVISIBLEGRIDSIZE", self.big_visible_grid_size, 3);
params.add_double("VISIBLEGRIDSIZE", self.visible_grid_size, 3);
params.add_coord("ELECTRICALGRIDRANGE", self.electrical_grid_range);
params.add(
"ELECTRICALGRIDENABLED",
if self.electrical_grid_enabled {
"TRUE"
} else {
"FALSE"
},
);
params.add_double("SNAPGRIDSIZE", self.snap_grid_size, 6);
params.add_double("SNAPGRIDSIZEX", self.snap_grid_size_x, 6);
params.add_double("SNAPGRIDSIZEY", self.snap_grid_size_y, 6);
params.add_double("TRACKGRIDSIZE", self.track_grid_size, 6);
params.add_double("VIAGRIDSIZE", self.via_grid_size, 6);
params.add_double("COMPONENTGRIDSIZE", self.component_grid_size, 6);
params.add_double("COMPONENTGRIDSIZEX", self.component_grid_size_x, 6);
params.add_double("COMPONENTGRIDSIZEY", self.component_grid_size_y, 6);
params.add("DOTGRID", if self.dot_grid { "TRUE" } else { "FALSE" });
params.add_int("DISPLAYUNIT", self.display_unit.to_int());
params.add_int(
"DESIGNATORDISPLAYMODE",
self.designator_display_mode.to_int(),
);
params.add(
"PRIMITIVELOCK",
if self.primitive_lock { "TRUE" } else { "FALSE" },
);
params.add("POLYGONTYPE", self.polygon_type.as_str());
params.add("POUROVER", if self.pour_over { "TRUE" } else { "FALSE" });
params.add(
"REMOVEDEAD",
if self.remove_dead { "TRUE" } else { "FALSE" },
);
params.add_coord("GRIDSIZE", self.grid_size);
params.add_coord("TRACKWIDTH", self.track_width);
params.add("HATCHSTYLE", self.hatch_style.as_str());
params.add(
"USEOCTAGONS",
if self.use_octagons { "TRUE" } else { "FALSE" },
);
params.add_coord("MINPRIMLENGTH", self.min_prim_length);
for (idx, vertex) in self.outline.iter().enumerate() {
params.add_int(&format!("KIND{}", idx), vertex.kind.to_int());
params.add_coord(&format!("VX{}", idx), vertex.x);
params.add_coord(&format!("VY{}", idx), vertex.y);
params.add_coord(&format!("CX{}", idx), vertex.center_x);
params.add_coord(&format!("CY{}", idx), vertex.center_y);
params.add_double(&format!("SA{}", idx), vertex.start_angle, 14);
params.add_double(&format!("EA{}", idx), vertex.end_angle, 14);
params.add_coord(&format!("R{}", idx), vertex.radius);
}
params
}
pub fn outline_vertex_count(&self) -> usize {
self.outline.len()
}
pub fn is_metric(&self) -> bool {
matches!(self.display_unit, DisplayUnit::Metric)
}
}