use std::f64::consts::{FRAC_1_SQRT_2, SQRT_2};
use crate::scene::{Point, Segment};
use super::{Border, Geometry, Input};
pub fn ellipse_path(c: Point, rx: f64, ry: f64) -> Vec<Segment> {
let k = 0.55228475;
let p = |x: f64, y: f64| c.plus(Point::new(x, y));
vec![
Segment::MoveTo(p(rx, 0.0)),
Segment::CurveTo(p(rx, k * ry), p(k * rx, ry), p(0.0, ry)),
Segment::CurveTo(p(-k * rx, ry), p(-rx, k * ry), p(-rx, 0.0)),
Segment::CurveTo(p(-rx, -k * ry), p(-k * rx, -ry), p(0.0, -ry)),
Segment::CurveTo(p(k * rx, -ry), p(rx, -k * ry), p(rx, 0.0)),
Segment::Close,
]
}
pub fn coordinate(input: &mut Input) -> Geometry {
let c = input.center_point();
let mut g = Geometry::new(c, Border::Point);
for a in ["center", "north", "north west", "north east", "west", "east", "mid", "mid west", "mid east", "base", "base west", "base east", "south", "south west", "south east"] {
g.anchor_at(a, c);
}
g.parts.clear();
g
}
pub fn box_anchors(g: &mut Geometry, sw: Point, ne: Point, ex: f64) {
let c = sw.lerp(ne, 0.5);
let m = 0.5 * ex;
for (name, p) in [
("center", c),
("mid", Point::new(c.x, m)),
("base", Point::new(c.x, 0.0)),
("north", Point::new(c.x, ne.y)),
("south", Point::new(c.x, sw.y)),
("west", Point::new(sw.x, c.y)),
("mid west", Point::new(sw.x, m)),
("base west", Point::new(sw.x, 0.0)),
("north west", Point::new(sw.x, ne.y)),
("south west", sw),
("east", Point::new(ne.x, c.y)),
("mid east", Point::new(ne.x, m)),
("base east", Point::new(ne.x, 0.0)),
("north east", ne),
("south east", Point::new(ne.x, sw.y)),
] {
g.anchor_at(name, p);
}
}
pub fn rectangle_corners(input: &Input) -> (Point, Point) {
let t = input.text();
let w = (t.wd + 2.0 * input.inner_xsep).max(input.minimum_width);
let h = (t.ht + t.dp + 2.0 * input.inner_ysep).max(input.minimum_height);
let cy = (t.ht - t.dp) / 2.0;
let ne = Point::new(t.wd / 2.0 + w / 2.0 + input.outer_xsep, cy + h / 2.0 + input.outer_ysep);
let sw = Point::new(t.wd / 2.0 - w / 2.0 - input.outer_xsep, cy - h / 2.0 - input.outer_ysep);
(sw, ne)
}
pub fn rectangle(input: &mut Input) -> Geometry {
let (sw, ne) = rectangle_corners(input);
let c = sw.lerp(ne, 0.5);
let mut g = Geometry::new(c, Border::Rectangle(ne.minus(sw).scale(0.5)));
box_anchors(&mut g, sw, ne, input.ex);
let o = Point::new(input.outer_xsep, input.outer_ysep);
let (a, b) = (sw.plus(o), ne.minus(o));
g.background(vec![Segment::MoveTo(a), Segment::LineTo(Point::new(b.x, a.y)), Segment::LineTo(b), Segment::LineTo(Point::new(a.x, b.y)), Segment::Close]);
g.roundable = true;
g
}
fn round_anchors(g: &mut Geometry, c: Point, r: Point, ex: f64) {
let m = 0.5 * ex;
let k = FRAC_1_SQRT_2;
for (name, p) in [
("center", c),
("mid", Point::new(c.x, m)),
("base", Point::new(c.x, 0.0)),
("north", Point::new(c.x, c.y + r.y)),
("south", Point::new(c.x, c.y - r.y)),
("west", Point::new(c.x - r.x, c.y)),
("east", Point::new(c.x + r.x, c.y)),
("mid west", Point::new(c.x - r.x, m)),
("mid east", Point::new(c.x + r.x, m)),
("base west", Point::new(c.x - r.x, 0.0)),
("base east", Point::new(c.x + r.x, 0.0)),
("north west", Point::new(c.x - k * r.x, c.y + k * r.y)),
("south west", Point::new(c.x - k * r.x, c.y - k * r.y)),
("north east", Point::new(c.x + k * r.x, c.y + k * r.y)),
("south east", Point::new(c.x + k * r.x, c.y - k * r.y)),
] {
g.anchor_at(name, p);
}
}
pub fn circle(input: &mut Input) -> Geometry {
let c = input.center_point();
let (xa, ya) = input.half_with_inner();
let r = xa.hypot(ya).max(input.minimum_width / 2.0).max(input.minimum_height / 2.0) + input.outer_sep();
let mut g = Geometry::new(c, Border::Ellipse(Point::new(r, r)));
round_anchors(&mut g, c, Point::new(r, r), input.ex);
let rr = r - input.outer_sep();
g.background(ellipse_path(c, rr, rr));
g
}
pub fn ellipse(input: &mut Input) -> Geometry {
let c = input.center_point();
let (xa, ya) = input.half_with_inner();
let rx = (SQRT_2 * xa).max(input.minimum_width / 2.0) + input.outer_xsep;
let ry = (SQRT_2 * ya).max(input.minimum_height / 2.0) + input.outer_ysep;
let mut g = Geometry::new(c, Border::Ellipse(Point::new(rx, ry)));
round_anchors(&mut g, c, Point::new(rx, ry), input.ex);
g.background(ellipse_path(c, rx - input.outer_xsep, ry - input.outer_ysep));
g
}
pub fn diamond(input: &mut Input) -> Geometry {
let c = input.center_point();
let (xa, ya) = input.half_with_inner();
let aspect = if input.aspect.abs() > 1e-9 { input.aspect } else { 1.0 };
let x = (xa + aspect * ya).max(input.minimum_width / 2.0) + input.outer_xsep;
let y = (xa / aspect + ya).max(input.minimum_height / 2.0) + input.outer_ysep;
let p = |dx: f64, dy: f64| c.plus(Point::new(dx, dy));
let outline = vec![p(x, 0.0), p(0.0, y), p(-x, 0.0), p(0.0, -y)];
let mut g = Geometry::new(c, Border::Outline(outline));
for (name, q) in [
("center", c),
("mid", Point::new(c.x, 0.5 * input.ex)),
("base", Point::new(c.x, 0.0)),
("north", p(0.0, y)),
("south", p(0.0, -y)),
("west", p(-x, 0.0)),
("east", p(x, 0.0)),
("north west", p(-x / 2.0, y / 2.0)),
("south west", p(-x / 2.0, -y / 2.0)),
("north east", p(x / 2.0, y / 2.0)),
("south east", p(x / 2.0, -y / 2.0)),
] {
g.anchor_at(name, q);
}
let xc = x - SQRT_2 * input.outer_xsep;
let yc = y - SQRT_2 * input.outer_ysep;
g.background(vec![Segment::MoveTo(p(xc, 0.0)), Segment::LineTo(p(0.0, yc)), Segment::LineTo(p(-xc, 0.0)), Segment::LineTo(p(0.0, -yc)), Segment::Close]);
g.roundable = true;
g
}