use crate::types::{Coord, Layer, ParameterCollection};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum PolygonType {
#[default]
Polygon,
SplitPlane,
Cutout,
}
impl PolygonType {
pub fn parse(s: &str) -> Self {
match s.to_uppercase().as_str() {
"POLYGON" => PolygonType::Polygon,
"SPLITPLANE" => PolygonType::SplitPlane,
"CUTOUT" => PolygonType::Cutout,
_ => PolygonType::Polygon,
}
}
pub fn as_str(&self) -> &'static str {
match self {
PolygonType::Polygon => "Polygon",
PolygonType::SplitPlane => "SplitPlane",
PolygonType::Cutout => "Cutout",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum HatchStyle {
None,
Hatch45,
Hatch90,
HatchHorizontal,
HatchVertical,
#[default]
Solid,
}
impl HatchStyle {
pub fn parse(s: &str) -> Self {
match s.to_uppercase().as_str() {
"NONE" => HatchStyle::None,
"45DEGREE" | "HATCH45" => HatchStyle::Hatch45,
"90DEGREE" | "HATCH90" => HatchStyle::Hatch90,
"HORIZONTAL" => HatchStyle::HatchHorizontal,
"VERTICAL" => HatchStyle::HatchVertical,
"SOLID" => HatchStyle::Solid,
_ => HatchStyle::Solid,
}
}
pub fn as_str(&self) -> &'static str {
match self {
HatchStyle::None => "None",
HatchStyle::Hatch45 => "45Degree",
HatchStyle::Hatch90 => "90Degree",
HatchStyle::HatchHorizontal => "Horizontal",
HatchStyle::HatchVertical => "Vertical",
HatchStyle::Solid => "Solid",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum PolygonVertexKind {
#[default]
Line = 0,
Arc = 1,
}
impl PolygonVertexKind {
pub fn from_int(value: i32) -> Self {
match value {
1 => PolygonVertexKind::Arc,
_ => PolygonVertexKind::Line,
}
}
pub fn to_int(self) -> i32 {
self as i32
}
}
#[derive(Debug, Clone, Default)]
pub struct PolygonVertex {
pub kind: PolygonVertexKind,
pub x: Coord,
pub y: Coord,
pub center_x: Coord,
pub center_y: Coord,
pub start_angle: f64,
pub end_angle: f64,
pub radius: Coord,
}
#[derive(Debug, Clone, Default)]
pub struct PcbPolygon {
pub layer: Layer,
pub locked: bool,
pub polygon_outline: bool,
pub user_routed: bool,
pub keepout: bool,
pub union_index: i32,
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 net_name: String,
pub unique_id: String,
pub vertices: Vec<PolygonVertex>,
pub params: ParameterCollection,
}
impl PcbPolygon {
pub fn from_params(params: &ParameterCollection) -> Self {
let mut polygon = 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),
user_routed: params
.get("USERROUTED")
.map(|v| v.as_bool_or(true))
.unwrap_or(true),
keepout: params
.get("KEEPOUT")
.map(|v| v.as_bool_or(false))
.unwrap_or(false),
union_index: params
.get("UNIONINDEX")
.map(|v| v.as_int_or(0))
.unwrap_or(0),
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(),
net_name: params
.get("NET")
.or_else(|| params.get("NETNAME"))
.map(|v| v.as_str().to_string())
.unwrap_or_default(),
unique_id: params
.get("UNIQUEID")
.map(|v| v.as_str().to_string())
.unwrap_or_default(),
vertices: Vec::new(),
params: params.clone(),
};
let mut idx = 0;
loop {
let kind_key = format!("KIND{}", idx);
let vx_key = format!("VX{}", idx);
let vy_key = format!("VY{}", idx);
if !params.contains(&vx_key) {
break;
}
let vertex = PolygonVertex {
kind: params
.get(&kind_key)
.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(),
};
polygon.vertices.push(vertex);
idx += 1;
}
polygon
}
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(
"USERROUTED",
if self.user_routed { "TRUE" } else { "FALSE" },
);
params.add("KEEPOUT", if self.keepout { "TRUE" } else { "FALSE" });
params.add_int("UNIONINDEX", self.union_index);
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);
if !self.net_name.is_empty() {
params.add("NET", &self.net_name);
}
if !self.unique_id.is_empty() {
params.add("UNIQUEID", &self.unique_id);
}
for (idx, vertex) in self.vertices.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 vertex_count(&self) -> usize {
self.vertices.len()
}
}