use str_error;
use footprint;
use Sexp;
use layout::{Adjust, Bound, BoundingBox};
use std::{fmt, result};
use KicadError;
#[derive(Debug)]
pub struct Layout {
pub version: i64,
pub host: Host,
pub general: General,
pub page: String,
pub setup: Setup,
pub layers: Vec<Layer>,
pub elements: Vec<Element>,
}
#[derive(Clone, Debug)]
pub enum Element {
Module(footprint::Module),
Net(Net),
NetClass(NetClass),
GrText(GrText),
GrLine(GrLine),
GrArc(GrArc),
GrCircle(GrCircle),
Dimension(Dimension),
Segment(Segment),
Via(Via),
Zone(Zone),
Other(Sexp),
}
#[derive(Clone, Debug)]
pub struct Zone {
pub net: i64,
pub net_name: NetName,
pub layer: footprint::Layer,
pub tstamp: String,
pub hatch: Hatch,
pub priority: u64,
pub connect_pads: ConnectPads,
pub min_thickness: f64,
pub keepout: Option<Keepout>,
pub fill: Fill,
pub polygons: Vec<footprint::Pts>,
pub filled_polygons: Vec<footprint::Pts>,
pub fill_segments: Option<footprint::Pts>,
pub other: Vec<Sexp>,
}
impl Adjust for Zone {
fn adjust(&mut self, x: f64, y: f64) {
for p in &mut self.polygons {
p.adjust(x, y)
}
for p in &mut self.filled_polygons {
p.adjust(x, y)
}
for p in &mut self.fill_segments {
p.adjust(x, y)
}
}
}
impl BoundingBox for Zone {
fn bounding_box(&self) -> Bound {
let mut b = Bound::default();
for p in &self.polygons {
b.update(&p.bounding_box());
}
for p in &self.filled_polygons {
b.update(&p.bounding_box());
}
for p in &self.fill_segments {
b.update(&p.bounding_box());
}
b.swap_if_needed();
b
}
}
#[derive(Clone, Debug, Default)]
pub struct Hatch {
pub style: String,
pub pitch: f64,
}
#[derive(Clone, Debug, Default)]
pub struct ConnectPads {
pub connection: Option<String>,
pub clearance: f64,
}
#[derive(Clone, Debug, Default)]
pub struct Keepout {
pub tracks: bool,
pub vias: bool,
pub copperpour: bool,
}
#[derive(Clone, Debug, Default)]
pub struct Fill {
pub filled: bool,
pub segment: bool,
pub arc_segments: i64,
pub thermal_gap: f64,
pub thermal_bridge_width: f64,
pub smoothing: Option<String>,
pub corner_radius: f64,
}
#[derive(Clone, Debug)]
pub struct Host {
pub tool: String,
pub build: String,
}
#[derive(Clone, Debug)]
pub struct General {
pub links: i64,
pub no_connects: i64,
pub area: Area,
pub thickness: f64,
pub drawings: i64,
pub tracks: i64,
pub zones: i64,
pub modules: i64,
pub nets: i64,
}
#[derive(Clone, Debug, Default)]
pub struct Area {
pub x1: f64,
pub y1: f64,
pub x2: f64,
pub y2: f64,
}
#[derive(Clone, Debug)]
pub struct Layer {
pub num: i64,
pub layer: footprint::Layer,
pub layer_type: LayerType,
pub hide: bool,
}
#[derive(Clone, Debug)]
pub enum LayerType {
Signal,
Power,
Mixed,
Jumper,
User,
}
#[derive(Clone, Debug, Default)]
pub struct Setup {
pub elements: Vec<SetupElement>,
pub pcbplotparams: Vec<SetupElement>,
}
#[derive(Clone, Debug)]
pub struct SetupElement {
pub name: String,
pub value1: String,
pub value2: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct NetName(pub String);
impl<'a> From<&'a str> for NetName {
fn from(s: &'a str) -> NetName {
NetName(s.into())
}
}
impl From<String> for NetName {
fn from(s: String) -> NetName {
NetName(s)
}
}
impl NetName {
pub fn replace_block(&mut self, from: &str, to: &str) {
let from_pat = format!("/{}/", from);
let to_pat = format!("/{}/", to);
let n = self.0.replace(&from_pat, &to_pat);
self.0 = n;
}
pub fn rename(&mut self, from: &str, to: &str) {
if self.0 == from {
self.0 = to.into();
}
}
}
#[derive(Clone, PartialEq, Debug, Default)]
pub struct Net {
pub num: i64,
pub name: NetName,
}
#[derive(Clone, PartialEq, Debug)]
pub struct NetClass {
pub name: String,
pub desc: String,
pub clearance: f64,
pub trace_width: f64,
pub via_dia: f64,
pub via_drill: f64,
pub uvia_dia: f64,
pub uvia_drill: f64,
pub diff_pair_gap: Option<f64>,
pub diff_pair_width: Option<f64>,
pub nets: Vec<NetName>,
}
#[derive(Clone, Debug, Default)]
pub struct GrText {
pub value: String,
pub at: footprint::At,
pub layer: footprint::Layer,
pub effects: footprint::Effects,
pub tstamp: Option<String>,
}
impl Adjust for GrText {
fn adjust(&mut self, x: f64, y: f64) {
self.at.adjust(x, y)
}
}
impl BoundingBox for GrText {
fn bounding_box(&self) -> Bound {
debug!("poor bounding box for GrText");
Bound::new(self.at.x, self.at.y, self.at.x, self.at.y)
}
}
#[derive(Clone, Debug, Default)]
pub struct GrLine {
pub start: footprint::Xy,
pub end: footprint::Xy,
pub angle: f64,
pub layer: footprint::Layer,
pub width: f64,
pub tstamp: Option<String>,
}
impl Adjust for GrLine {
fn adjust(&mut self, x: f64, y: f64) {
self.start.adjust(x, y);
self.end.adjust(x, y)
}
}
impl BoundingBox for GrLine {
fn bounding_box(&self) -> Bound {
let x1 = self.start.x.min(self.end.x);
let y1 = self.start.y.min(self.end.y);
let x2 = self.start.x.max(self.end.x);
let y2 = self.start.y.max(self.end.y);
Bound::new(x1, y1, x2, y2)
}
}
#[derive(Clone, Debug, Default)]
pub struct GrArc {
pub start: footprint::Xy,
pub end: footprint::Xy,
pub angle: f64,
pub layer: footprint::Layer,
pub width: f64,
pub tstamp: Option<String>,
}
impl Adjust for GrArc {
fn adjust(&mut self, x: f64, y: f64) {
self.start.adjust(x, y);
self.end.adjust(x, y);
}
}
impl BoundingBox for GrArc {
fn bounding_box(&self) -> Bound {
let x1 = self.start.x.min(self.end.x);
let y1 = self.start.y.min(self.end.y);
let x2 = self.start.x.max(self.end.x);
let y2 = self.start.y.max(self.end.y);
Bound::new(x1, y1, x2, y2)
}
}
#[derive(Clone, Debug, Default)]
pub struct GrCircle {
pub center: footprint::Xy,
pub end: footprint::Xy,
pub layer: footprint::Layer,
pub width: f64,
pub tstamp: Option<String>,
}
impl Adjust for GrCircle {
fn adjust(&mut self, x: f64, y: f64) {
self.center.x += x;
self.center.y += y;
self.end.x += x;
self.end.y += y;
}
}
impl BoundingBox for GrCircle {
fn bounding_box(&self) -> Bound {
let r = (self.center.x - self.end.x) * (self.center.x - self.end.x)
+ (self.center.y - self.end.y) * (self.center.y - self.end.y);
let r = r.sqrt();
Bound::new(
self.center.x - r,
self.center.y - r,
self.center.x + r,
self.center.y + r,
)
}
}
#[derive(Clone, Debug, Default)]
pub struct Dimension {
pub name: String,
pub width: f64,
pub layer: footprint::Layer,
pub tstamp: Option<String>,
pub text: GrText,
pub feature1: footprint::Pts,
pub feature2: footprint::Pts,
pub crossbar: footprint::Pts,
pub arrow1a: footprint::Pts,
pub arrow1b: footprint::Pts,
pub arrow2a: footprint::Pts,
pub arrow2b: footprint::Pts,
}
impl Adjust for Dimension {
fn adjust(&mut self, x: f64, y: f64) {
self.text.adjust(x, y);
self.feature1.adjust(x, y);
self.feature2.adjust(x, y);
self.crossbar.adjust(x, y);
self.arrow1a.adjust(x, y);
self.arrow1b.adjust(x, y);
self.arrow2a.adjust(x, y);
self.arrow2b.adjust(x, y)
}
}
impl BoundingBox for Dimension {
fn bounding_box(&self) -> Bound {
let mut b = Bound::default();
b.update(&self.text.bounding_box());
b.update(&self.feature1.bounding_box());
b.update(&self.feature2.bounding_box());
b.update(&self.crossbar.bounding_box());
b.update(&self.arrow1a.bounding_box());
b.update(&self.arrow1b.bounding_box());
b.update(&self.arrow2a.bounding_box());
b.update(&self.arrow2b.bounding_box());
b
}
}
#[derive(Clone, Debug, Default)]
pub struct Segment {
pub start: footprint::Xy,
pub end: footprint::Xy,
pub width: f64,
pub layer: footprint::Layer,
pub net: i64,
pub tstamp: Option<String>,
pub status: Option<String>,
}
impl Adjust for Segment {
fn adjust(&mut self, x: f64, y: f64) {
self.start.adjust(x, y);
self.end.adjust(x, y)
}
}
impl BoundingBox for Segment {
fn bounding_box(&self) -> Bound {
let x1 = self.start.x.min(self.end.x);
let y1 = self.start.y.min(self.end.y);
let x2 = self.start.x.max(self.end.x);
let y2 = self.start.y.max(self.end.y);
Bound::new(x1, y1, x2, y2)
}
}
#[derive(Clone, Debug, Default)]
pub struct Via {
pub blind: bool,
pub micro: bool,
pub at: footprint::At,
pub size: f64,
pub drill: f64,
pub layers: footprint::Layers,
pub net: i64,
}
impl Adjust for Via {
fn adjust(&mut self, x: f64, y: f64) {
self.at.adjust(x, y);
}
}
impl BoundingBox for Via {
fn bounding_box(&self) -> Bound {
let x1 = self.at.x - self.size;
let y1 = self.at.y - self.size;
let x2 = self.at.x + self.size;
let y2 = self.at.y + self.size;
Bound::new(x1, y1, x2, y2)
}
}
impl Default for Layout {
fn default() -> Layout {
Layout {
version: 4,
host: Host::default(),
elements: vec![],
setup: Setup::default(),
general: General::default(),
page: String::from("A4"),
layers: vec![],
}
}
}
impl Adjust for Layout {
fn adjust(&mut self, x: f64, y: f64) {
for e in &mut self.elements {
e.adjust(x, y)
}
}
}
impl BoundingBox for Layout {
fn bounding_box(&self) -> Bound {
let mut bound = Bound::default();
for e in &self.elements {
bound.update(&e.bounding_box());
}
bound.swap_if_needed();
bound
}
}
impl Layout {
pub fn nets(&self) -> Vec<&Net> {
let mut v = vec![];
for element in &self.elements {
if let Element::Net(ref net) = *element {
v.push(net)
}
}
v
}
pub fn change_net_name(&mut self, old_name: &str, new_name: &str) {
let update = |name: &mut NetName| {
name.rename(old_name, new_name);
Ok(())
};
self.update_net_names(update).unwrap();
}
pub fn update_net_names<F>(&mut self, update: F) -> Result<(), KicadError>
where
F: Fn(&mut NetName) -> Result<(), KicadError>,
{
for element in &mut self.elements {
match *element {
Element::Net(ref mut net) => {
update(&mut net.name)?;
}
Element::NetClass(ref mut nc) => for name in &mut nc.nets {
update(name)?;
},
Element::Module(ref mut module) => for m_e in &mut module.elements {
if let footprint::Element::Pad(ref mut pad) = *m_e {
if let Some(ref mut net) = pad.net {
update(&mut net.name)?;
}
}
},
Element::Zone(ref mut zone) => {
update(&mut zone.net_name)?;
}
_ => (),
}
}
Ok(())
}
pub fn netclasses(&self) -> Vec<&NetClass> {
let mut v = vec![];
for element in &self.elements {
if let Element::NetClass(ref net_class) = *element {
v.push(net_class)
}
}
v
}
pub fn get_module(&self, reference: &str) -> Option<&footprint::Module> {
for x in &self.elements[..] {
match *x {
Element::Module(ref m) => if m.is_reference_with_name(reference) {
return Some(m);
},
Element::Via(_) |
Element::Segment(_) |
Element::Net(_) |
Element::NetClass(_) |
Element::GrText(_) |
Element::GrLine(_) |
Element::GrArc(_) |
Element::GrCircle(_) |
Element::Dimension(_) |
Element::Zone(_) |
Element::Other(_) => (),
}
}
None
}
pub fn get_modules(&self) -> Vec<&footprint::Module> {
let mut v = vec![];
for e in &self.elements {
if let Element::Module(ref m) = *e {
v.push(m);
}
}
v
}
pub fn modify_module<F>(&mut self, reference: &str, fun: F) -> Result<(), KicadError>
where
F: Fn(&mut footprint::Module) -> (),
{
for x in &mut self.elements {
match *x {
Element::Module(ref mut m) => if m.is_reference_with_name(reference) {
return Ok(fun(m));
},
Element::Via(_) |
Element::Segment(_) |
Element::Net(_) |
Element::NetClass(_) |
Element::GrText(_) |
Element::GrLine(_) |
Element::GrArc(_) |
Element::GrCircle(_) |
Element::Dimension(_) |
Element::Zone(_) |
Element::Other(_) => (),
}
}
str_error(format!("did not find module with reference {}", reference))
}
pub fn add_net(&mut self, num: i64, name: &'static str) {
self.elements.push(Element::Net(Net {
num: num,
name: name.into(),
}));
}
pub fn add_netclass(
&mut self,
name: &'static str,
desc: &'static str,
clearance: f64,
trace_width: f64,
via_dia: f64,
via_drill: f64,
uvia_dia: f64,
uvia_drill: f64,
diff_pair_gap: Option<f64>,
diff_pair_width: Option<f64>,
nets: Vec<String>,
) {
self.elements.push(Element::NetClass(NetClass {
name: String::from(name),
desc: String::from(desc),
clearance: clearance,
trace_width: trace_width,
via_dia: via_dia,
via_drill: via_drill,
uvia_dia: uvia_dia,
uvia_drill: uvia_drill,
diff_pair_gap: diff_pair_gap,
diff_pair_width: diff_pair_width,
nets: nets.into_iter().map(|x| x.into()).collect(),
}));
}
}
impl Default for Host {
fn default() -> Host {
Host {
tool: String::from("pcbnew"),
build: String::from("custom"),
}
}
}
impl Default for General {
fn default() -> General {
General {
links: 0,
no_connects: 0,
area: Area::default(),
thickness: 1.6,
drawings: 0,
tracks: 0,
zones: 0,
modules: 0,
nets: 0,
}
}
}
impl NetClass {
pub fn equal_no_net(&self, other: &NetClass) -> bool {
let mut s1 = self.clone();
s1.nets = vec![];
let mut s2 = other.clone();
s2.nets = vec![];
s1 == s2
}
pub fn has_net(&self, name: &'static str) -> bool {
for net in &self.nets {
if &net.0[..] == name {
return true;
}
}
false
}
}
impl Setup {
pub fn get(&self, s: &str) -> Option<&String> {
for element in &self.elements {
if element.name[..] == s[..] {
return Some(&element.value1);
}
}
None
}
pub fn update_element(&mut self, name: &'static str, value: String) {
for element in &mut self.elements {
if &element.name[..] == name {
element.value1 = value;
return;
}
}
self.elements.push(SetupElement {
name: String::from(name),
value1: value,
value2: None,
})
}
}
impl Adjust for Element {
fn adjust(&mut self, x: f64, y: f64) {
match *self {
Element::Module(ref mut e) => e.adjust(x, y),
Element::GrText(ref mut e) => e.adjust(x, y),
Element::GrLine(ref mut e) => e.adjust(x, y),
Element::GrArc(ref mut e) => e.adjust(x, y),
Element::GrCircle(ref mut e) => e.adjust(x, y),
Element::Dimension(ref mut e) => e.adjust(x, y),
Element::Segment(ref mut e) => e.adjust(x, y),
Element::Via(ref mut e) => e.adjust(x, y),
Element::Zone(ref mut e) => e.adjust(x, y),
Element::Net(_) | Element::NetClass(_) | Element::Other(_) => (),
}
}
}
impl BoundingBox for Element {
fn bounding_box(&self) -> Bound {
match *self {
Element::Module(ref e) => e.bounding_box(),
Element::GrText(ref e) => e.bounding_box(),
Element::GrLine(ref e) => e.bounding_box(),
Element::GrArc(ref e) => e.bounding_box(),
Element::GrCircle(ref e) => e.bounding_box(),
Element::Dimension(ref e) => e.bounding_box(),
Element::Segment(ref e) => e.bounding_box(),
Element::Via(ref e) => e.bounding_box(),
Element::Zone(ref e) => e.bounding_box(),
Element::Net(_) | Element::NetClass(_) | Element::Other(_) => Bound::default(),
}
}
}
impl NetName {
pub fn is_unnamed_net(&self) -> Option<(String, String)> {
if self.0.starts_with("Net-(") {
let v: Vec<&str> = self.0.split(|c| c == '-' || c == '(' || c == ')').collect();
if v.len() != 5 {
None
} else {
Some((v[2].into(), v[3].into()))
}
} else {
None
}
}
pub fn set_unnamed_net(&mut self, new_name: &str) -> Result<(), KicadError> {
if let Some((_, pad)) = self.is_unnamed_net() {
self.0 = format!("Net-({}-{})", new_name, pad);
Ok(())
} else {
Err(format!("net is not an unnamed net: {}", self.0).into())
}
}
}
impl fmt::Display for NetName {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
write!(f, "{}", self.0)
}
}
#[test]
fn test_is_unnamed_net() {
let n = Net {
num: 1,
name: "HELLO".into(),
};
assert_eq!(n.name.is_unnamed_net(), None);
let n = Net {
num: 1,
name: "Net-(L1-Pad1)".into(),
};
assert_eq!(n.name.is_unnamed_net(), Some(("L1".into(), "Pad1".into())));
}
#[test]
fn test_unnamed_rename() {
let mut n = Net {
num: 1,
name: "Net-(L1-Pad1)".into(),
};
n.name.set_unnamed_net("L101").unwrap();
assert_eq!(
n.name.is_unnamed_net(),
Some(("L101".into(), "Pad1".into()))
);
}