use crate::js::math::max;
use crate::scene::text::Label;
use crate::scene::{Painting, Part, Path, Point, Rect};
pub(crate) const PAD_X: f64 = 16.0;
const PAD_Y: f64 = 10.0;
const GLYPH: f64 = 34.0;
const GLYPH_GAP: f64 = 4.0;
const STACK: f64 = 5.0;
const CAP: f64 = 6.0;
pub(crate) fn actor_kind(kind: &str) -> &str {
match kind {
"participant" | "actor" | "boundary" | "control" | "entity" | "database"
| "collections" | "queue" => kind,
_ => "participant",
}
}
pub(crate) fn is_figure(kind: &str) -> bool {
matches!(
kind,
"actor" | "boundary" | "control" | "entity" | "database"
)
}
pub(crate) fn actor_size(
kind: &str,
label: &Label,
min_w: f64,
min_h: f64,
padding_x: f64,
) -> (f64, f64) {
if is_figure(kind) {
let h = if label.height > 0.0 {
GLYPH + (GLYPH_GAP + label.height)
} else {
GLYPH
};
return (max(40.0, label.width + 8.0), h);
}
let extra = match kind {
"collections" => STACK,
"queue" => 2.0 * CAP,
_ => 0.0,
};
let w = max(min_w, label.width + 2.0 * padding_x) + extra;
let h =
max(min_h, label.height + 2.0 * PAD_Y) + if kind == "collections" { STACK } else { 0.0 };
(w, h)
}
pub(crate) fn circle(cx: f64, cy: f64, r: f64, paint: Painting) -> Part {
let mut p = Path::new();
p.ellipse(cx, cy, r, r);
Part::path(p, paint)
}
fn line(p: &mut Path) -> Part {
Part::path(std::mem::take(p), Painting::StrokeOnly)
}
fn figure(kind: &str, cx: f64, top: f64) -> Vec<Part> {
let y = |dy: f64| top + dy;
let mut p = Path::new();
match kind {
"actor" => vec![
circle(cx, y(7.0), 6.0, Painting::FillAndStroke),
line(
p.move_to(cx, y(13.0))
.line_to(cx, y(24.0))
.move_to(cx - 10.0, y(17.0))
.line_to(cx + 10.0, y(17.0))
.move_to(cx - 8.0, y(34.0))
.line_to(cx, y(24.0))
.line_to(cx + 8.0, y(34.0)),
),
],
"boundary" => vec![
line(
p.move_to(cx - 19.0, y(6.0))
.line_to(cx - 19.0, y(28.0))
.move_to(cx - 19.0, y(17.0))
.line_to(cx - 8.0, y(17.0)),
),
circle(cx + 3.0, y(17.0), 11.0, Painting::FillAndStroke),
],
"control" => vec![
circle(cx, y(21.0), 11.0, Painting::FillAndStroke),
line(
p.move_to(cx + 5.0, y(5.0))
.line_to(cx - 1.0, y(10.0))
.line_to(cx + 5.0, y(15.0)),
),
],
"entity" => vec![
circle(cx, y(15.0), 11.0, Painting::FillAndStroke),
line(p.move_to(cx - 11.0, y(30.0)).line_to(cx + 11.0, y(30.0))),
],
_ => {
let (l, r) = (cx - 14.0, cx + 14.0);
p.move_to(l, y(7.0))
.line_to(l, y(27.0))
.arc_to(14.0, 5.0, 0.0, false, false, r, y(27.0))
.line_to(r, y(7.0))
.arc_to(14.0, 5.0, 0.0, false, false, l, y(7.0))
.close();
let body = Part::path(std::mem::take(&mut p), Painting::FillAndStroke);
p.move_to(l, y(7.0))
.arc_to(14.0, 5.0, 0.0, false, false, r, y(7.0));
vec![body, line(&mut p)]
}
}
}
pub(crate) fn draw_actor(
kind: &str,
cx: f64,
top: f64,
w: f64,
h: f64,
label: &Label,
) -> (Vec<Part>, Point) {
if is_figure(kind) {
let at = Point::new(cx, top + GLYPH + GLYPH_GAP + label.height / 2.0);
return (figure(kind, cx, top), at);
}
let left = cx - w / 2.0;
match kind {
"collections" => {
let (bw, bh) = (w - STACK, h - STACK);
(
vec![
Part::rect(
Rect::new(left + STACK, top, bw, bh),
true,
Painting::FillAndStroke,
),
Part::rect(
Rect::new(left, top + STACK, bw, bh),
true,
Painting::FillAndStroke,
),
],
Point::new(left + bw / 2.0, top + STACK + bh / 2.0),
)
}
"queue" => {
let (l, r, b, ry) = (left + CAP, left + w - CAP, top + h, h / 2.0);
let mut body = Path::new();
body.move_to(l, top)
.line_to(r, top)
.arc_to(CAP, ry, 0.0, false, true, r, b)
.line_to(l, b)
.arc_to(CAP, ry, 0.0, false, true, l, top)
.close();
let mut end = Path::new();
end.move_to(r, top).arc_to(CAP, ry, 0.0, false, false, r, b);
(
vec![
Part::path(body, Painting::FillAndStroke),
Part::path(end, Painting::StrokeOnly),
],
Point::new(cx - CAP / 2.0, top + h / 2.0),
)
}
_ => (
vec![Part::rect(
Rect::new(left, top, w, h),
true,
Painting::FillAndStroke,
)],
Point::new(cx, top + h / 2.0),
),
}
}