use super::ir::Bbox;
use super::values::as_pair;
use crate::error::Error;
use crate::resolve::{AttrMap, ResolvedValue};
use crate::span::Span;
#[derive(Clone, Copy)]
pub struct Pin {
pub fx: f64,
pub fy: f64,
}
#[derive(Clone, Copy, PartialEq)]
pub enum Role {
Flow,
Pinned,
}
impl Pin {
pub fn target(self, parent: Bbox, child: Bbox) -> (f64, f64) {
let px = (parent.min_x + parent.max_x) / 2.0 + self.fx * parent.w();
let py = (parent.min_y + parent.max_y) / 2.0 + self.fy * parent.h();
let cbx = (child.min_x + child.max_x) / 2.0;
let cby = (child.min_y + child.max_y) / 2.0;
(
px - cbx - self.fx * child.w(),
py - cby - self.fy * child.h(),
)
}
}
pub fn read_pin(attrs: &AttrMap, span: Span) -> Result<Option<Pin>, Error> {
let Some(v) = attrs.get("pin") else {
return Ok(None);
};
let bad = || {
Error::at(
span,
"'pin' expects none, center, an edge (top/bottom/left/right), or a corner (e.g. 'top right')",
)
};
match v {
ResolvedValue::Ident(s) => Ok(match s.as_str() {
"none" => None,
"center" => Some(Pin { fx: 0.0, fy: 0.0 }),
"top" => Some(Pin { fx: 0.0, fy: -0.5 }),
"bottom" => Some(Pin { fx: 0.0, fy: 0.5 }),
"left" => Some(Pin { fx: -0.5, fy: 0.0 }),
"right" => Some(Pin { fx: 0.5, fy: 0.0 }),
_ => return Err(bad()),
}),
ResolvedValue::Tuple(parts) if parts.len() == 2 => {
let fy = match ident(&parts[0]) {
Some("top") => -0.5,
Some("bottom") => 0.5,
_ => return Err(bad()),
};
let fx = match ident(&parts[1]) {
Some("left") => -0.5,
Some("right") => 0.5,
_ => return Err(bad()),
};
Ok(Some(Pin { fx, fy }))
}
_ => Err(bad()),
}
}
pub fn is_pinned(attrs: &AttrMap) -> bool {
match attrs.get("pin") {
Some(ResolvedValue::Ident(s)) => s != "none",
Some(_) => true,
None => false,
}
}
pub fn child_role(attrs: &AttrMap, span: Span) -> Result<Role, Error> {
Ok(match read_pin(attrs, span)? {
None => Role::Flow,
Some(_) => Role::Pinned,
})
}
pub fn translate(attrs: &AttrMap, span: Span) -> Result<Option<(f64, f64)>, Error> {
match attrs.get("translate") {
Some(v) => Ok(Some(as_pair(v, span)?)),
None => Ok(None),
}
}
fn ident(v: &ResolvedValue) -> Option<&str> {
match v {
ResolvedValue::Ident(s) => Some(s.as_str()),
_ => None,
}
}