From 4bdd5b5beec1d3c3bd97a717ad46a1cdde52c54f Mon Sep 17 00:00:00 2001 From: aiongg Date: Mon, 26 Oct 2026 20:46:09 +0000 Subject: [PATCH] refactor(ui-egui): split icons.rs into icons/{geometry,draw}.rs --- crates/ui-egui/src/icons.rs | 2100 +------------------------- crates/ui-egui/src/icons/draw.rs | 1341 ++++++++++++++++ crates/ui-egui/src/icons/geometry.rs | 763 ++++++++++ 3 files changed, 2112 insertions(+), 2092 deletions(-) create mode 100644 crates/ui-egui/src/icons/draw.rs create mode 100644 crates/ui-egui/src/icons/geometry.rs diff --git a/crates/ui-egui/src/icons.rs b/crates/ui-egui/src/icons.rs index 54509141..c3aa288b 100644 --- a/crates/ui-egui/src/icons.rs +++ b/crates/ui-egui/src/icons.rs @@ -57,6 +57,14 @@ macro_rules! icons { }; } +mod draw; +mod geometry; + +use draw::draw; +use geometry::Pen; +#[cfg(test)] +use geometry::{star_pts, triangulate}; + icons! { // Clipboard & slides Paste => "paste", @@ -345,2098 +353,6 @@ pub fn paint(painter: &egui::Painter, rect: egui::Rect, icon: Icon, ink: egui::C draw(&pen, icon); } -// --------------------------------------------------------------------------------------------- -// Drawing toolkit -// --------------------------------------------------------------------------------------------- - -/// A logical colour, resolved against the theme ink and the disabled flag. -#[derive(Clone, Copy)] -enum C { - Ink(f32), - Acc(Color32, f32), - White, - None, -} - -impl C { - fn a(self, f: f32) -> C { - match self { - C::Ink(a) => C::Ink(a * f), - C::Acc(c, a) => C::Acc(c, a * f), - other => other, - } - } -} - -const K: C = C::Ink(1.0); -const B: C = C::Acc(BLUE, 1.0); -const O: C = C::Acc(ORANGE, 1.0); -const G: C = C::Acc(GREEN, 1.0); -const PU: C = C::Acc(PURPLE, 1.0); -const R: C = C::Acc(RED, 1.0); -const Y: C = C::Acc(GOLD, 1.0); -const D: C = C::Acc(DARK, 1.0); -const W: C = C::White; -const N: C = C::None; - -/// Light tint of an accent, for fills behind outlines. -fn t(c: C) -> C { - c.a(0.25) -} - -type P2 = (f32, f32); - -#[derive(Clone, Copy)] -enum Glyph { - Plus, - Cross, - Check, -} - -struct Pen<'a> { - p: &'a Painter, - o: Pos2, - /// Points per design unit. - k: f32, - /// Base stroke width in points. - sw: f32, - ink: Color32, - dis: bool, -} - -fn rad(deg: f32) -> f32 { - deg * PI / 180.0 -} - -fn arc_pts(c: P2, rx: f32, ry: f32, a0: f32, a1: f32) -> Vec { - let n = (((a1 - a0).abs() / 9.0).ceil() as usize).max(4); - (0..=n) - .map(|i| { - let a = rad(a0 + (a1 - a0) * i as f32 / n as f32); - (c.0 + rx * a.cos(), c.1 + ry * a.sin()) - }) - .collect() -} - -fn cubic_pts(p0: P2, p1: P2, p2: P2, p3: P2, n: usize) -> Vec { - (0..=n) - .map(|i| { - let t = i as f32 / n as f32; - let u = 1.0 - t; - let (a, b, c, d) = (u * u * u, 3.0 * u * u * t, 3.0 * u * t * t, t * t * t); - (a * p0.0 + b * p1.0 + c * p2.0 + d * p3.0, a * p0.1 + b * p1.1 + c * p2.1 + d * p3.1) - }) - .collect() -} - -fn quad_pts(p0: P2, p1: P2, p2: P2, n: usize) -> Vec { - (0..=n) - .map(|i| { - let t = i as f32 / n as f32; - let u = 1.0 - t; - (u * u * p0.0 + 2.0 * u * t * p1.0 + t * t * p2.0, u * u * p0.1 + 2.0 * u * t * p1.1 + t * t * p2.1) - }) - .collect() -} - -/// Rounds the corners of a polyline by `r` design units. -fn round_pts(pts: &[P2], r: f32, closed: bool) -> Vec { - let n = pts.len(); - if n < 3 || r <= 0.0 { - return pts.to_vec(); - } - let mut out = Vec::with_capacity(n * 6); - for i in 0..n { - let cur = pts[i]; - let interior = closed || (i > 0 && i + 1 < n); - if !interior { - out.push(cur); - continue; - } - let prev = pts[(i + n - 1) % n]; - let next = pts[(i + 1) % n]; - let d1 = ((prev.0 - cur.0).powi(2) + (prev.1 - cur.1).powi(2)).sqrt(); - let d2 = ((next.0 - cur.0).powi(2) + (next.1 - cur.1).powi(2)).sqrt(); - if d1 < 1e-4 || d2 < 1e-4 { - out.push(cur); - continue; - } - let rr = r.min(d1 * 0.5).min(d2 * 0.5); - let a = (cur.0 + (prev.0 - cur.0) / d1 * rr, cur.1 + (prev.1 - cur.1) / d1 * rr); - let b = (cur.0 + (next.0 - cur.0) / d2 * rr, cur.1 + (next.1 - cur.1) / d2 * rr); - out.extend(quad_pts(a, cur, b, 4)); - } - out -} - -fn rrect_pts(x0: f32, y0: f32, x1: f32, y1: f32, r: f32) -> Vec { - round_pts(&[(x0, y0), (x1, y0), (x1, y1), (x0, y1)], r, true) -} - -/// Star polygon with `n` points. -fn star_pts(c: P2, ro: f32, ri: f32, n: usize, rot: f32) -> Vec { - (0..n * 2) - .map(|i| { - let r = if i % 2 == 0 { ro } else { ri }; - let a = rad(rot - 90.0 + i as f32 * 180.0 / n as f32); - (c.0 + r * a.cos(), c.1 + r * a.sin()) - }) - .collect() -} - -/// Transforms local points (u along `ang`, v across) to design coordinates around `c`. -fn tf(c: P2, ang: f32, pts: &[P2]) -> Vec { - let (s, co) = rad(ang).sin_cos(); - pts.iter().map(|&(u, v)| (c.0 + u * co - v * s, c.1 + u * s + v * co)).collect() -} - -fn point_in_tri(p: Pos2, a: Pos2, b: Pos2, c: Pos2) -> bool { - let d1 = (p.x - b.x) * (a.y - b.y) - (a.x - b.x) * (p.y - b.y); - let d2 = (p.x - c.x) * (b.y - c.y) - (b.x - c.x) * (p.y - c.y); - let d3 = (p.x - a.x) * (c.y - a.y) - (c.x - a.x) * (p.y - a.y); - let neg = d1 < 0.0 || d2 < 0.0 || d3 < 0.0; - let pos = d1 > 0.0 || d2 > 0.0 || d3 > 0.0; - !(neg && pos) -} - -/// Ear-clipping triangulation of a simple polygon. -fn triangulate(pts: &[Pos2]) -> Vec<[u32; 3]> { - let n = pts.len(); - let mut tris = Vec::new(); - if n < 3 { - return tris; - } - let area: f32 = (0..n) - .map(|i| { - let a = pts[i]; - let b = pts[(i + 1) % n]; - a.x * b.y - b.x * a.y - }) - .sum(); - let mut idx: Vec = (0..n).collect(); - if area < 0.0 { - idx.reverse(); - } - while idx.len() > 3 { - let m = idx.len(); - let mut clipped = false; - for i in 0..m { - let (ia, ib, ic) = (idx[(i + m - 1) % m], idx[i], idx[(i + 1) % m]); - let (a, b, c) = (pts[ia], pts[ib], pts[ic]); - let cross = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x); - if cross <= 1e-7 { - continue; - } - let blocked = idx.iter().any(|&j| j != ia && j != ib && j != ic && point_in_tri(pts[j], a, b, c)); - if blocked { - continue; - } - tris.push([ia as u32, ib as u32, ic as u32]); - idx.remove(i); - clipped = true; - break; - } - if !clipped { - // Degenerate input: drop a vertex and carry on rather than loop forever. - idx.remove(0); - } - } - if let [a, b, c] = idx[..] { - tris.push([a as u32, b as u32, c as u32]); - } - tris -} - -impl Pen<'_> { - fn pt(&self, (x, y): P2) -> Pos2 { - pos2(self.o.x + x * self.k, self.o.y + y * self.k) - } - - fn v(&self, pts: &[P2]) -> Vec { - pts.iter().map(|&p| self.pt(p)).collect() - } - - fn col(&self, c: C) -> Color32 { - let dim = if self.dis { 0.35 } else { 1.0 }; - match c { - C::Ink(a) => self.ink.gamma_multiply(a * dim), - C::Acc(col, a) => { - if self.dis { - self.ink.gamma_multiply(a * dim) - } else { - col.gamma_multiply(a) - } - } - C::White => { - if self.dis { - // Glyphs on solid fills stay legible as a darker mark. - self.ink.gamma_multiply(0.6) - } else { - Color32::WHITE - } - } - C::None => Color32::TRANSPARENT, - } - } - - fn stroke(&self, c: C, w: f32) -> Stroke { - Stroke::new(self.sw * w, self.col(c)) - } - - // --- lines --- - - fn line_w(&self, pts: &[P2], c: C, w: f32) { - if pts.len() < 2 { - return; - } - let col = self.col(c); - if col == Color32::TRANSPARENT { - return; - } - let v = self.v(pts); - if let [a, b] = v[..] { - // Single segments get round caps: draw them as one convex capsule. - let d = b - a; - let len = d.length(); - let r = self.sw * w * 0.5; - if len < 1e-3 { - self.p.circle_filled(a, r, col); - return; - } - let ang = d.y.atan2(d.x); - let mut cap = Vec::with_capacity(20); - for (c, start) in [(b, ang - PI * 0.5), (a, ang + PI * 0.5)] { - for i in 0..=8 { - let t = start + PI * i as f32 / 8.0; - cap.push(c + vec2(t.cos(), t.sin()) * r); - } - } - self.p.add(PathShape::convex_polygon(cap, col, Stroke::NONE)); - return; - } - self.p.add(PathShape::line(v, Stroke::new(self.sw * w, col))); - } - - fn line(&self, pts: &[P2], c: C) { - self.line_w(pts, c, 1.0); - } - - fn seg(&self, a: P2, b: P2, c: C) { - self.line_w(&[a, b], c, 1.0); - } - - fn seg_w(&self, a: P2, b: P2, c: C, w: f32) { - self.line_w(&[a, b], c, w); - } - - fn rline(&self, pts: &[P2], r: f32, c: C) { - self.line(&round_pts(pts, r, false), c); - } - - fn rline_w(&self, pts: &[P2], r: f32, c: C, w: f32) { - self.line_w(&round_pts(pts, r, false), c, w); - } - - fn dashed(&self, pts: &[P2], c: C, dash: f32, gap: f32) { - let v = self.v(pts); - self.p.extend(Shape::dashed_line(&v, self.stroke(c, 1.0), dash * self.k, gap * self.k)); - } - - fn dashed_w(&self, pts: &[P2], c: C, dash: f32, gap: f32, w: f32) { - let v = self.v(pts); - self.p.extend(Shape::dashed_line(&v, self.stroke(c, w), dash * self.k, gap * self.k)); - } - - fn dotted(&self, pts: &[P2], c: C, spacing: f32, r: f32) { - let v = self.v(pts); - self.p.extend(Shape::dotted_line(&v, self.col(c), spacing * self.k, r * self.sw)); - } - - // --- filled shapes --- - - /// Any simple polygon: filled via triangulation, stroked as a closed path. - fn shape_w(&self, pts: &[P2], fill: C, stroke: C, w: f32) { - if pts.len() < 3 { - return; - } - let v = self.v(pts); - let fc = self.col(fill); - if fc != Color32::TRANSPARENT { - let mut mesh = Mesh::default(); - for &p in &v { - mesh.colored_vertex(p, fc); - } - for [a, b, c] in triangulate(&v) { - mesh.add_triangle(a, b, c); - } - self.p.add(Shape::mesh(mesh)); - // A hairline in the fill colour anti-aliases the mesh edge. - if matches!(stroke, C::None) { - self.p.add(PathShape::closed_line(v.clone(), Stroke::new(0.6_f32.min(self.sw * 0.5), fc))); - } - } - let sc = self.col(stroke); - if sc != Color32::TRANSPARENT { - self.p.add(PathShape::closed_line(v, Stroke::new(self.sw * w, sc))); - } - } - - fn shape(&self, pts: &[P2], fill: C, stroke: C) { - self.shape_w(pts, fill, stroke, 1.0); - } - - fn rshape(&self, pts: &[P2], r: f32, fill: C, stroke: C) { - self.shape(&round_pts(pts, r, true), fill, stroke); - } - - /// Convex polygon (anti-aliased fill). - fn poly(&self, pts: &[P2], fill: C, stroke: C) { - self.poly_w(pts, fill, stroke, 1.0); - } - - fn poly_w(&self, pts: &[P2], fill: C, stroke: C, w: f32) { - let v = self.v(pts); - self.p.add(PathShape::convex_polygon(v, self.col(fill), self.stroke(stroke, w))); - } - - fn rect_w(&self, x0: f32, y0: f32, x1: f32, y1: f32, r: f32, fill: C, stroke: C, w: f32) { - let rect = Rect::from_min_max(self.pt((x0, y0)), self.pt((x1, y1))); - self.p.rect(rect, r * self.k, self.col(fill), self.stroke(stroke, w), StrokeKind::Middle); - } - - fn rect(&self, x0: f32, y0: f32, x1: f32, y1: f32, r: f32, fill: C, stroke: C) { - self.rect_w(x0, y0, x1, y1, r, fill, stroke, 1.0); - } - - fn circle_w(&self, c: P2, r: f32, fill: C, stroke: C, w: f32) { - self.p.circle(self.pt(c), r * self.k, self.col(fill), self.stroke(stroke, w)); - } - - fn circle(&self, c: P2, r: f32, fill: C, stroke: C) { - self.circle_w(c, r, fill, stroke, 1.0); - } - - fn dot(&self, c: P2, r: f32, fill: C) { - self.p.circle_filled(self.pt(c), r * self.k, self.col(fill)); - } - - fn ellipse(&self, c: P2, rx: f32, ry: f32, fill: C, stroke: C) { - let mut pts = arc_pts(c, rx, ry, 0.0, 360.0); - pts.pop(); - self.poly(&pts, fill, stroke); - } - - // --- arcs and arrows --- - - fn arc(&self, c: P2, r: f32, a0: f32, a1: f32, col: C) { - self.line(&arc_pts(c, r, r, a0, a1), col); - } - - /// Filled arrow head with its tip at `tip`, pointing along `dir` degrees. - fn head(&self, tip: P2, dir: f32, size: f32, c: C) { - let (s, co) = rad(dir).sin_cos(); - let hw = size * 0.62; - let back = (tip.0 - co * size, tip.1 - s * size); - self.poly(&[tip, (back.0 - s * hw, back.1 + co * hw), (back.0 + s * hw, back.1 - co * hw)], c, N); - } - - fn head_size(&self) -> f32 { - (2.2 * self.sw / self.k).max(3.6) - } - - fn arrow(&self, a: P2, b: P2, c: C) { - let dir = (b.1 - a.1).atan2(b.0 - a.0); - let hs = self.head_size(); - let end = (b.0 - dir.cos() * hs * 0.7, b.1 - dir.sin() * hs * 0.7); - self.seg(a, end, c); - self.head(b, dir.to_degrees(), hs, c); - } - - fn arrow2(&self, a: P2, b: P2, c: C) { - let dir = (b.1 - a.1).atan2(b.0 - a.0); - let hs = self.head_size(); - let s = (a.0 + dir.cos() * hs * 0.7, a.1 + dir.sin() * hs * 0.7); - let e = (b.0 - dir.cos() * hs * 0.7, b.1 - dir.sin() * hs * 0.7); - self.seg(s, e, c); - self.head(b, dir.to_degrees(), hs, c); - self.head(a, dir.to_degrees() + 180.0, hs, c); - } - - /// Polyline ending in an arrow head. - fn path_arrow(&self, pts: &[P2], c: C) { - let n = pts.len(); - if n < 2 { - return; - } - let a = pts[n - 2]; - let b = pts[n - 1]; - let dir = (b.1 - a.1).atan2(b.0 - a.0); - let hs = self.head_size(); - let mut body = pts.to_vec(); - body[n - 1] = (b.0 - dir.cos() * hs * 0.7, b.1 - dir.sin() * hs * 0.7); - self.line(&body, c); - self.head(b, dir.to_degrees(), hs, c); - } - - /// Circular arc from `a0` to `a1` degrees with an arrow head at the `a1` end. - fn arc_arrow(&self, c: P2, r: f32, a0: f32, a1: f32, col: C) { - let hs = self.head_size(); - let sign = if a1 >= a0 { 1.0 } else { -1.0 }; - let back = (hs * 0.6 / r).to_degrees() * sign; - self.line(&arc_pts(c, r, r, a0, a1 - back), col); - let tip_a = rad(a1); - let tip = (c.0 + r * tip_a.cos(), c.1 + r * tip_a.sin()); - // Tangent direction of travel, nudged inward to sit on the arc. - let dir = a1 + 90.0 * sign - (hs * 0.5 / r).to_degrees() * sign; - self.head(tip, dir, hs, col); - } - - fn curve(&self, p0: P2, p1: P2, p2: P2, p3: P2, c: C) { - self.line(&cubic_pts(p0, p1, p2, p3, 24), c); - } - - fn curve_w(&self, p0: P2, p1: P2, p2: P2, p3: P2, c: C, w: f32) { - self.line_w(&cubic_pts(p0, p1, p2, p3, 24), c, w); - } - - // --- text --- - - fn text(&self, s: &str, at: P2, size: f32, c: C) { - self.p.text(self.pt(at), Align2::CENTER_CENTER, s, FontId::proportional(size * self.k), self.col(c)); - } - - fn text_bold(&self, s: &str, at: P2, size: f32, c: C) { - let d = 0.32 * self.sw / self.k; - let reps: &[f32] = if self.dis { &[0.0] } else { &[-d, 0.0, d] }; - for &dx in reps { - self.text(s, (at.0 + dx, at.1), size, c); - } - } - - fn text_rot(&self, s: &str, at: P2, size: f32, c: C, angle_deg: f32) { - let col = self.col(c); - let galley = self.p.layout_no_wrap(s.to_owned(), FontId::proportional(size * self.k), col); - let rot = egui::emath::Rot2::from_angle(rad(angle_deg)); - let pos = self.pt(at) - rot * (galley.size() * 0.5); - self.p.add(TextShape::new(pos, galley, col).with_angle(rad(angle_deg))); - } - - // --- composite parts --- - - /// Round badge with a white glyph (outline + ink glyph when disabled). - fn badge(&self, c: P2, r: f32, fill: C, glyph: Glyph) { - let gc = if self.dis { - self.circle(c, r, N, fill); - K - } else { - self.circle(c, r, fill, N); - W - }; - let g = r * 0.5; - let w = 0.95; - match glyph { - Glyph::Plus => { - self.seg_w((c.0 - g, c.1), (c.0 + g, c.1), gc, w); - self.seg_w((c.0, c.1 - g), (c.0, c.1 + g), gc, w); - } - Glyph::Cross => { - let g = g * 0.85; - self.seg_w((c.0 - g, c.1 - g), (c.0 + g, c.1 + g), gc, w); - self.seg_w((c.0 + g, c.1 - g), (c.0 - g, c.1 + g), gc, w); - } - Glyph::Check => { - self.line_w(&[(c.0 - g, c.1 + 0.1), (c.0 - g * 0.25, c.1 + g * 0.75), (c.0 + g, c.1 - g * 0.6)], gc, w); - } - } - } - - /// Landscape slide frame. - fn frame(&self, x0: f32, y0: f32, x1: f32, y1: f32) { - self.rect(x0, y0, x1, y1, 1.5, N, K); - } - - /// Document page with a folded top-right corner. - fn page(&self, x0: f32, y0: f32, x1: f32, y1: f32, fill: C, stroke: C) { - let f = ((x1 - x0) * 0.32).min(5.0); - self.rshape(&[(x0, y0), (x1 - f, y0), (x1, y0 + f), (x1, y1), (x0, y1)], 1.2, fill, stroke); - self.line(&[(x1 - f, y0), (x1 - f, y0 + f), (x1, y0 + f)], stroke); - } - - /// Picture frame with sun and hills. - fn picture(&self, x0: f32, y0: f32, x1: f32, y1: f32) { - let (w, h) = (x1 - x0, y1 - y0); - let m = w.min(h); - self.rect(x0, y0, x1, y1, 1.5, N, K); - self.dot((x1 - w * 0.27, y0 + h * 0.3), m * 0.12, Y); - let base = y1 - h * 0.14; - self.poly(&[(x0 + w * 0.12, base), (x0 + w * 0.38, y0 + h * 0.36), (x0 + w * 0.64, base)], G, N); - self.poly(&[(x0 + w * 0.48, base), (x0 + w * 0.68, y0 + h * 0.56), (x1 - w * 0.12, base)], G.a(0.7), N); - } - - fn magnifier(&self, c: P2, r: f32, lens_fill: C, col: C) { - self.circle(c, r, lens_fill, col); - let d = r * 0.707; - self.seg_w((c.0 + d + 0.6, c.1 + d + 0.6), (c.0 + r * 1.55, c.1 + r * 1.55), col, 1.5); - } - - fn bubble_pts(x0: f32, y0: f32, x1: f32, y1: f32) -> Vec { - let r = 2.5; - let mut pts = arc_pts((x0 + r, y0 + r), r, r, 180.0, 270.0); - pts.extend(arc_pts((x1 - r, y0 + r), r, r, 270.0, 360.0)); - pts.extend(arc_pts((x1 - r, y1 - r), r, r, 0.0, 90.0)); - let tw = ((x1 - x0) * 0.22).clamp(2.5, 4.5); - pts.push((x0 + r + 0.8 + tw, y1)); - pts.push((x0 + r - 0.2, y1 + 3.8)); - pts.push((x0 + r + 0.8, y1)); - pts.extend(arc_pts((x0 + r, y1 - r), r, r, 90.0, 180.0)); - pts - } - - fn bubble(&self, x0: f32, y0: f32, x1: f32, y1: f32, fill: C, stroke: C) { - self.shape(&Self::bubble_pts(x0, y0, x1, y1), fill, stroke); - } - - fn person(&self, cx: f32, top: f32, s: f32, fill: C) { - self.dot((cx, top + 2.6 * s), 2.6 * s, fill); - let base = top + 12.0 * s; - self.poly(&arc_pts((cx, base), 5.2 * s, 5.0 * s, 180.0, 360.0), fill, N); - } - - fn star(&self, c: P2, ro: f32, fill: C, stroke: C) { - self.shape(&star_pts(c, ro, ro * 0.47, 5, 0.0), fill, stroke); - } - - fn sparkle(&self, c: P2, r: f32, fill: C) { - self.shape(&star_pts(c, r, r * 0.3, 4, 0.0), fill, N); - } - - fn gear(&self, c: P2, ro: f32, ri: f32, teeth: usize, fill: C, stroke: C) { - let step = 360.0 / teeth as f32; - let mut pts = Vec::with_capacity(teeth * 4); - for i in 0..teeth { - let base = i as f32 * step; - for (a, r) in [(-0.3, ri), (-0.17, ro), (0.17, ro), (0.3, ri)] { - let ang = rad(base + a * step); - pts.push((c.0 + r * ang.cos(), c.1 + r * ang.sin())); - } - } - self.shape(&pts, fill, stroke); - } - - fn speaker(&self, x: f32, cy: f32, fill: C, stroke: C) { - self.rshape( - &[(x, cy - 3.0), (x + 3.8, cy - 3.0), (x + 8.5, cy - 7.5), (x + 8.5, cy + 7.5), (x + 3.8, cy + 3.0), (x, cy + 3.0)], - 0.8, - fill, - stroke, - ); - } - - /// Table grid; `header` fills the first row. - fn table(&self, x0: f32, y0: f32, x1: f32, y1: f32, rows: usize, cols: usize, header: C) { - let rh = (y1 - y0) / rows as f32; - let cw = (x1 - x0) / cols as f32; - if !matches!(header, C::None) { - self.rect(x0, y0, x1, y0 + rh, 1.2, header, N); - } - for i in 1..rows { - let y = y0 + rh * i as f32; - self.seg_w((x0, y), (x1, y), K, 0.8); - } - for j in 1..cols { - let x = x0 + cw * j as f32; - self.seg_w((x, y0), (x, y1), K, 0.8); - } - self.rect(x0, y0, x1, y1, 1.2, N, K); - } - - /// Bar chart in the box. - fn bars(&self, x0: f32, y0: f32, x1: f32, y1: f32) { - let w = (x1 - x0) / 3.0; - let h = y1 - y0; - for (i, (frac, c)) in [(0.5, B), (1.0, O), (0.72, G)].into_iter().enumerate() { - let bx = x0 + w * i as f32; - self.rect(bx + w * 0.16, y1 - h * frac, bx + w * 0.84, y1, 0.6, c, N); - } - self.seg((x0 - 0.5, y1 + 0.2), (x1 + 0.5, y1 + 0.2), K); - } - - /// A writing tool lying along `ang` degrees with its point at `tip`. - fn tool(&self, tip: P2, ang: f32, len: f32, hw: f32, cone: f32, body: C, lead: C, cap_len: f32, cap: C) { - let m = |pts: &[P2]| tf(tip, ang, pts); - self.poly(&m(&[(0.0, 0.0), (cone, -hw), (cone, hw)]), N, K); - self.poly(&m(&[(0.0, 0.0), (cone * 0.42, -hw * 0.42), (cone * 0.42, hw * 0.42)]), lead, N); - self.poly(&m(&[(cone, -hw), (len - cap_len, -hw), (len - cap_len, hw), (cone, hw)]), body, K); - if cap_len > 0.0 { - self.poly(&m(&[(len - cap_len, -hw), (len, -hw), (len, hw), (len - cap_len, hw)]), cap, K); - } - } - - /// Paint brush lying along `ang` with bristle tip at `tip`. - fn brush(&self, tip: P2, ang: f32, len: f32, bristle: C) { - let m = |pts: &[P2]| tf(tip, ang, pts); - self.shape(&m(&[(0.0, -0.4), (2.5, -2.2), (5.5, -2.2), (5.5, 2.2), (2.5, 2.2), (0.0, 0.4)]), bristle, K); - self.poly(&m(&[(5.5, -2.0), (7.8, -2.0), (7.8, 2.0), (5.5, 2.0)]), K, K); - self.line(&m(&[(7.8, -1.1), (len, -1.3), (len, 1.3), (7.8, 1.1)]), K); - } - - /// Small path-drawn digit 1–3 in a `3 × h` box. - fn digit(&self, d: u8, x: f32, y: f32, h: f32, c: C) { - let w = 0.8; - match d { - 1 => self.line_w(&[(x + 0.4, y + 1.2), (x + 2.0, y), (x + 2.0, y + h)], c, w), - 2 => self.line_w(&[(x, y + 1.0), (x + 0.7, y), (x + 2.4, y), (x + 3.1, y + 1.1), (x + 2.9, y + 2.2), (x, y + h), (x + 3.2, y + h)], c, w), - _ => self.line_w( - &[(x, y), (x + 3.0, y), (x + 1.3, y + h * 0.42), (x + 2.6, y + h * 0.5), (x + 3.1, y + h * 0.7), (x + 2.5, y + h), (x, y + h)], - c, - w, - ), - } - } - - /// Path-drawn capital A. - fn letter_a(&self, x0: f32, top: f32, x1: f32, bottom: f32, c: C, w: f32) { - let mx = (x0 + x1) * 0.5; - self.line_w(&[(x0, bottom), (mx, top), (x1, bottom)], c, w); - let f = 0.62; - let y = top + (bottom - top) * f; - let dx = (x1 - x0) * 0.5 * f; - self.seg_w((mx - dx, y), (mx + dx, y), c, w); - } - - /// Hand-drawn scribble in the box. - fn scribble(&self, x0: f32, y0: f32, x1: f32, y1: f32, c: C) { - let (w, h) = (x1 - x0, y1 - y0); - let p = |u: f32, v: f32| (x0 + w * u, y0 + h * v); - // A loopy cursive stroke (prolate cycloid), normalised into the box. - let raw: Vec = (0..=48) - .map(|i| { - let th = i as f32 / 48.0 * 4.0 * PI + 0.6; - (th - 1.9 * th.sin(), -1.9 * th.cos()) - }) - .collect(); - let (mut lx, mut hx, mut ly, mut hy) = (f32::MAX, f32::MIN, f32::MAX, f32::MIN); - for &(x, y) in &raw { - lx = lx.min(x); - hx = hx.max(x); - ly = ly.min(y); - hy = hy.max(y); - } - let sx = (hx - lx).max(1e-3); - let sy = (hy - ly).max(1e-3); - let pts: Vec = raw.iter().map(|&(x, y)| p((x - lx) / sx, (y - ly) / sy)).collect(); - self.line(&pts, c); - } - - /// Isometric "layer" diamond. - fn layer(&self, cx: f32, cy: f32, hw: f32, hh: f32, fill: C, stroke: C) { - self.rshape(&[(cx, cy - hh), (cx + hw, cy), (cx, cy + hh), (cx - hw, cy)], 0.8, fill, stroke); - } - - /// Open book. - fn book(&self, fill: C, col: C) { - let left = { - let mut p = quad_pts((12.0, 7.0), (7.5, 4.0), (2.0, 5.0), 8); - p.extend(quad_pts((2.0, 18.5), (7.5, 17.5), (12.0, 20.5), 8)); - p - }; - let right = { - let mut p = quad_pts((12.0, 7.0), (16.5, 4.0), (22.0, 5.0), 8); - p.extend(quad_pts((22.0, 18.5), (16.5, 17.5), (12.0, 20.5), 8)); - p - }; - self.shape(&left, fill, col); - self.shape(&right, fill, col); - } - - fn monitor(&self, x0: f32, y0: f32, x1: f32, y1: f32) { - self.rect(x0, y0, x1, y1, 1.5, N, K); - let cx = (x0 + x1) * 0.5; - self.seg((cx, y1), (cx, y1 + 3.2), K); - self.seg((cx - 4.0, y1 + 3.6), (cx + 4.0, y1 + 3.6), K); - } - - fn clock_badge(&self, c: P2, r: f32, fill: C) { - let hc = if self.dis { - self.circle(c, r, N, fill); - K - } else { - self.circle(c, r, fill, N); - W - }; - self.line_w(&[(c.0, c.1 - r * 0.6), c, (c.0 + r * 0.5, c.1 + r * 0.2)], hc, 0.9); - } - - fn gear_badge(&self, c: P2, r: f32, fill: C) { - self.gear(c, r, r * 0.74, 8, fill, N); - if self.dis { - self.gear(c, r, r * 0.74, 8, N, fill); - } - self.dot(c, r * 0.32, if self.dis { N } else { W }); - if self.dis { - self.circle(c, r * 0.32, N, fill); - } - } - - fn lock(&self, x0: f32, y0: f32, x1: f32, y1: f32) { - let cx = (x0 + x1) * 0.5; - let r = (x1 - x0) * 0.3; - let mut sh = vec![(cx - r, y0)]; - sh.extend(arc_pts((cx, y0 - r * 0.7), r, r, 180.0, 360.0)); - sh.push((cx + r, y0)); - self.line(&sh, K); - self.rect(x0, y0, x1, y1, 1.2, Y, K); - let ky = (y0 + y1) * 0.5; - self.dot((cx, ky - 0.4), 1.1, D); - self.seg_w((cx, ky), (cx, ky + 2.0), D, 0.9); - } - - fn cursor(&self, x: f32, y: f32, s: f32) { - let p = |u: f32, v: f32| (x + u * s, y + v * s); - let pts = [p(0.0, 0.0), p(0.0, 15.0), p(3.8, 11.6), p(6.4, 17.0), p(8.6, 16.0), p(6.1, 10.8), p(11.0, 10.8)]; - self.shape(&pts, K, K); - } -} - -// --------------------------------------------------------------------------------------------- -// The icons -// --------------------------------------------------------------------------------------------- - -#[expect(clippy::too_many_lines)] -fn draw(p: &Pen<'_>, icon: Icon) { - use Icon as I; - match icon { - // ----- Clipboard & slides ----- - I::Paste => { - p.rline(&[(10.5, 21.5), (3.5, 21.5), (3.5, 4.5), (17.0, 4.5), (17.0, 9.0)], 2.0, K); - p.rect(7.0, 2.5, 13.5, 6.5, 1.2, Y, K); - p.rect(10.5, 10.0, 21.0, 22.0, 1.2, t(B), B); - p.seg((13.0, 14.0), (18.5, 14.0), B); - p.seg((13.0, 18.0), (17.0, 18.0), B); - } - I::Cut => { - p.seg((8.6, 14.6), (16.5, 2.5), K); - p.seg((15.4, 14.6), (7.5, 2.5), K); - p.circle((6.8, 17.6), 3.3, N, O); - p.circle((17.2, 17.6), 3.3, N, O); - p.dot((12.0, 9.4), 1.0, K); - } - I::Copy => { - p.rline(&[(8.0, 16.0), (3.0, 16.0), (3.0, 2.5), (14.0, 2.5), (14.0, 7.0)], 1.5, K); - p.rect(8.0, 7.5, 20.5, 21.5, 1.5, t(B), B); - p.seg((11.0, 12.5), (17.5, 12.5), B); - p.seg((11.0, 16.5), (16.0, 16.5), B); - } - I::FormatPainter => { - p.rect(2.5, 3.0, 17.0, 9.0, 1.5, Y, K); - p.rline(&[(17.0, 6.0), (20.5, 6.0), (20.5, 11.5), (11.0, 11.5), (11.0, 14.0)], 1.2, K); - p.rect(9.0, 14.0, 13.0, 21.5, 1.0, K, K); - } - I::NewSlide => { - p.frame(2.0, 4.0, 19.5, 16.5); - p.seg((5.0, 8.0), (13.0, 8.0), B); - p.seg((5.0, 12.0), (10.0, 12.0), K.a(0.5)); - p.badge((18.0, 17.5), 5.0, G, Glyph::Plus); - } - I::Layout => { - p.frame(2.0, 3.5, 22.0, 20.5); - p.rect(5.0, 6.5, 19.0, 9.0, 0.8, B, N); - p.rect(5.0, 11.5, 11.0, 17.5, 0.8, t(G), G); - p.rect(13.0, 11.5, 19.0, 17.5, 0.8, t(O), O); - } - I::ResetSlide => { - p.frame(2.0, 3.5, 22.0, 20.5); - p.arc_arrow((12.0, 12.0), 4.6, -20.0, -290.0, B); - } - I::Section => { - p.rshape(&[(2.5, 2.5), (13.0, 2.5), (16.5, 5.75), (13.0, 9.0), (2.5, 9.0)], 0.6, PU, N); - p.rline(&[(5.0, 9.0), (5.0, 16.0), (7.5, 16.0)], 1.0, K); - p.rect(7.5, 12.0, 21.5, 20.5, 1.2, N, K); - p.seg((10.0, 15.5), (16.0, 15.5), K.a(0.5)); - } - I::DuplicateSlide => { - p.rline(&[(6.5, 15.5), (2.5, 15.5), (2.5, 3.5), (16.5, 3.5), (16.5, 8.0)], 1.5, K); - p.rect(6.5, 8.0, 21.5, 20.5, 1.5, t(B), B); - p.seg((14.0, 11.5), (14.0, 17.0), B); - p.seg((11.25, 14.25), (16.75, 14.25), B); - } - I::DeleteSlide => { - p.frame(2.0, 4.0, 19.5, 16.5); - p.seg((5.0, 8.0), (13.0, 8.0), B); - p.seg((5.0, 12.0), (10.0, 12.0), K.a(0.5)); - p.badge((18.0, 17.5), 5.0, R, Glyph::Cross); - } - I::HideSlide => { - p.rect(2.0, 4.0, 22.0, 20.0, 1.5, N, K.a(0.55)); - p.seg((5.0, 8.0), (13.0, 8.0), K.a(0.55)); - p.seg((3.0, 21.0), (21.0, 3.0), R); - } - I::Slide => { - p.frame(2.0, 4.5, 22.0, 19.5); - p.seg((5.0, 8.5), (15.0, 8.5), B); - p.rect(5.0, 11.5, 19.0, 16.5, 0.8, t(B), N); - } - - // ----- Font ----- - I::FontGrow => { - p.letter_a(2.5, 4.0, 15.0, 20.5, K, 1.15); - p.poly(&[(16.0, 10.5), (22.5, 10.5), (19.25, 5.0)], B, N); - } - I::FontShrink => { - p.letter_a(3.0, 8.0, 13.5, 20.5, K, 1.0); - p.poly(&[(16.0, 5.0), (22.5, 5.0), (19.25, 10.5)], B, N); - } - I::ClearFormatting => { - p.letter_a(2.5, 3.0, 14.0, 17.5, K, 1.1); - let m = |pts: &[P2]| tf((17.0, 16.5), -45.0, pts); - p.poly(&m(&[(-5.5, -2.8), (0.0, -2.8), (0.0, 2.8), (-5.5, 2.8)]), R, R); - p.poly(&m(&[(0.0, -2.8), (5.5, -2.8), (5.5, 2.8), (0.0, 2.8)]), N, K); - } - I::Bold => p.text_bold("B", (12.0, 12.0), 22.0, K), - I::Italic => { - p.seg_w((10.5, 4.0), (18.5, 4.0), K, 1.1); - p.seg_w((5.5, 20.0), (13.5, 20.0), K, 1.1); - p.seg_w((14.5, 4.0), (9.5, 20.0), K, 1.1); - } - I::Underline => { - let mut pts = vec![(6.5, 3.0)]; - pts.extend(arc_pts((12.0, 11.5), 5.5, 5.5, 180.0, 0.0)); - pts.push((17.5, 3.0)); - p.line_w(&pts, K, 1.1); - p.seg_w((4.5, 21.0), (19.5, 21.0), B, 1.2); - } - I::Strikethrough => { - p.text("ab", (12.0, 11.0), 19.0, K); - p.seg_w((2.5, 13.0), (21.5, 13.0), R, 1.1); - } - I::Superscript => { - p.seg((3.0, 9.0), (13.0, 21.0), K); - p.seg((13.0, 9.0), (3.0, 21.0), K); - p.digit(2, 16.0, 2.5, 6.5, B); - } - I::Subscript => { - p.seg((3.0, 3.5), (13.0, 15.5), K); - p.seg((13.0, 3.5), (3.0, 15.5), K); - p.digit(2, 16.0, 15.0, 6.5, B); - } - I::CharSpacing => { - p.letter_a(2.0, 3.0, 10.5, 15.0, K, 1.0); - p.line(&[(13.5, 3.0), (17.75, 15.0), (22.0, 3.0)], K); - p.arrow2((2.5, 20.0), (21.5, 20.0), B); - } - I::ChangeCase => { - p.text("A", (8.0, 11.0), 19.0, K); - p.text("a", (17.5, 12.5), 16.0, B); - p.seg((3.0, 21.0), (21.0, 21.0), K.a(0.4)); - } - I::Highlight => { - let m = |pts: &[P2]| tf((8.0, 15.0), -45.0, pts); - p.poly(&m(&[(0.0, -1.2), (3.0, -3.0), (3.0, 3.0), (0.0, 1.4)]), Y, K); - p.poly(&m(&[(3.0, -3.0), (13.5, -3.0), (13.5, 3.0), (3.0, 3.0)]), N, K); - p.rect(2.5, 19.5, 21.5, 22.5, 0.8, Y, N); - } - I::FontColor => { - p.letter_a(4.5, 2.5, 19.5, 17.0, K, 1.1); - p.rect(2.5, 19.5, 21.5, 22.5, 0.8, R, N); - } - I::TextShadow => { - p.text("S", (14.0, 13.8), 22.0, K.a(0.3)); - p.text_bold("S", (11.0, 11.0), 22.0, K); - } - - // ----- Paragraph ----- - I::Bullets => { - for y in [5.5, 12.0, 18.5] { - p.dot((4.5, y), 1.8, B); - p.seg((9.0, y), (21.0, y), K); - } - } - I::Numbering => { - for (i, y) in [5.5_f32, 12.0, 18.5].into_iter().enumerate() { - p.digit(i as u8 + 1, 2.8, y - 2.6, 5.2, B); - p.seg((9.5, y), (21.0, y), K); - } - } - I::DecreaseIndent | I::IncreaseIndent => { - p.seg((3.0, 4.0), (21.0, 4.0), K); - p.seg((11.0, 9.5), (21.0, 9.5), K); - p.seg((11.0, 14.5), (21.0, 14.5), K); - p.seg((3.0, 20.0), (21.0, 20.0), K); - if icon == I::DecreaseIndent { - p.poly(&[(8.0, 8.0), (8.0, 16.0), (3.0, 12.0)], B, N); - } else { - p.poly(&[(3.0, 8.0), (3.0, 16.0), (8.0, 12.0)], B, N); - } - } - I::LineSpacing => { - for y in [5.0, 12.0, 19.0] { - p.seg((11.0, y), (21.0, y), K); - } - p.arrow2((5.0, 2.5), (5.0, 21.5), B); - } - I::Columns => { - for y in [4.5, 9.5, 14.5, 19.5] { - p.seg((2.5, y), (10.5, y), K); - p.seg((13.5, y), (21.5, y), K); - } - } - I::AlignLeft | I::AlignCenter | I::AlignRight | I::Justify => { - let rows: [(f32, f32); 4] = match icon { - I::AlignLeft => [(3.0, 21.0), (3.0, 15.0), (3.0, 21.0), (3.0, 13.0)], - I::AlignCenter => [(3.0, 21.0), (7.0, 17.0), (3.0, 21.0), (8.0, 16.0)], - I::AlignRight => [(3.0, 21.0), (9.0, 21.0), (3.0, 21.0), (11.0, 21.0)], - _ => [(3.0, 21.0), (3.0, 21.0), (3.0, 21.0), (3.0, 14.0)], - }; - for ((a, b), y) in rows.into_iter().zip([4.5, 9.5, 14.5, 19.5]) { - p.seg((a, y), (b, y), K); - } - } - I::TextDirection => { - // A capital A lying on its side. - p.line_w(&[(17.0, 5.5), (3.0, 12.0), (17.0, 18.5)], K, 1.1); - p.seg_w((11.7, 8.3), (11.7, 15.7), K, 1.1); - p.arrow((21.0, 3.5), (21.0, 20.5), B); - } - I::AlignText => { - p.rect(2.5, 3.0, 15.0, 21.0, 1.2, N, K); - p.seg((5.5, 10.0), (12.0, 10.0), K); - p.seg((5.5, 14.0), (10.5, 14.0), K); - p.arrow((19.5, 2.5), (19.5, 9.5), B); - p.arrow((19.5, 21.5), (19.5, 14.5), B); - } - I::ConvertSmartArt => { - for (y, x1) in [(4.0, 11.0), (8.0, 9.0), (12.0, 11.0)] { - p.seg((2.5, y), (x1, y), K); - } - p.path_arrow(&[(14.0, 6.0), (19.0, 6.0), (19.0, 12.5)], B); - p.rect(2.5, 15.5, 7.5, 21.5, 1.0, B, N); - p.rect(9.5, 15.5, 14.5, 21.5, 1.0, O, N); - p.rect(16.5, 15.5, 21.5, 21.5, 1.0, G, N); - } - - // ----- Drawing ----- - I::Shapes => { - p.rect(11.0, 2.5, 21.5, 13.0, 1.0, t(G), G); - p.rshape(&[(15.25, 11.0), (21.5, 21.5), (9.0, 21.5)], 0.8, t(O), O); - p.circle((8.0, 9.0), 5.5, t(B), B); - } - I::Arrange => { - p.rect(2.5, 2.5, 11.5, 11.5, 1.0, N, K); - p.rect(7.5, 7.5, 16.5, 16.5, 1.0, t(B), B); - p.rect(12.5, 12.5, 21.5, 21.5, 1.0, B, B); - } - I::QuickStyles => { - p.rect(3.0, 3.0, 11.0, 11.0, 1.5, B, N); - p.rect(13.0, 3.0, 21.0, 11.0, 1.5, N, O); - p.rect(3.0, 13.0, 11.0, 21.0, 1.5, t(G), G); - p.rect(13.0, 13.0, 21.0, 21.0, 1.5, PU, N); - } - I::ShapeFill => { - let m = |pts: &[P2]| tf((10.0, 10.0), 45.0, pts); - p.poly(&m(&[(-5.0, 0.0), (5.0, 0.0), (5.0, 5.0), (-5.0, 5.0)]), B, N); - p.rshape(&m(&[(-5.0, -5.0), (5.0, -5.0), (5.0, 5.0), (-5.0, 5.0)]), 0.8, N, K); - p.line(&quad_pts((5.2, 6.4), (5.5, 1.0), (10.0, 3.0), 10), K); - p.poly(&[(18.5, 8.5), (20.6, 12.6), (16.4, 12.6)], B, N); - p.dot((18.5, 13.4), 2.1, B); - p.rect(2.5, 19.5, 21.5, 22.5, 0.8, B, N); - } - I::ShapeOutline => { - p.tool((4.5, 15.5), -45.0, 17.0, 2.4, 4.2, N, K, 0.0, N); - p.rect(2.5, 19.5, 21.5, 22.5, 0.8, O, N); - } - I::ShapeEffects => { - p.rect(6.0, 8.0, 18.5, 20.5, 2.0, K.a(0.25), N); - p.rect(3.0, 5.0, 15.5, 17.5, 2.0, B, N); - p.sparkle((19.0, 5.0), 4.0, Y); - } - I::TextFill => { - p.letter_a(4.5, 2.5, 19.5, 17.0, B, 1.5); - p.rect(2.5, 19.5, 21.5, 22.5, 0.8, B, N); - } - I::TextOutline => { - let outer = [(3.5, 17.0), (9.6, 2.5), (14.4, 2.5), (20.5, 17.0), (16.6, 17.0), (15.2, 13.2), (8.8, 13.2), (7.4, 17.0)]; - p.shape_w(&outer, N, K, 0.8); - p.poly_w(&[(10.0, 10.0), (12.0, 5.0), (14.0, 10.0)], N, K, 0.8); - p.rect(2.5, 19.5, 21.5, 22.5, 0.8, O, N); - } - I::TextEffects => { - p.letter_a(3.0, 5.0, 17.0, 21.0, PU, 1.3); - p.sparkle((19.0, 5.0), 4.5, Y); - } - I::EditShape => { - let a = (4.0, 19.0); - let b = (11.0, 4.5); - let c = (20.0, 17.5); - let mut pts = vec![a, b]; - pts.extend(cubic_pts(b, (17.0, 4.0), (23.0, 11.0), c, 16)); - pts.extend(cubic_pts(c, (15.0, 22.0), (9.0, 17.0), a, 16)); - p.shape(&pts, t(B), K); - for q in [a, b, c] { - p.rect(q.0 - 1.6, q.1 - 1.6, q.0 + 1.6, q.1 + 1.6, 0.3, B, N); - } - } - I::MergeShapes => { - let mut lens = arc_pts((9.0, 12.0), 6.5, 6.5, -62.5, 62.5); - lens.extend(arc_pts((15.0, 12.0), 6.5, 6.5, 117.5, 242.5)); - p.poly(&lens, PU, N); - p.circle((9.0, 12.0), 6.5, N, K); - p.circle((15.0, 12.0), 6.5, N, K); - } - - // ----- Insert ----- - I::Table => p.table(2.5, 4.0, 21.5, 20.0, 3, 3, B), - I::Picture => p.picture(2.0, 4.0, 22.0, 20.0), - I::Screenshot => { - for (cx, cy, sx, sy) in [(2.5, 3.5, 1.0, 1.0), (21.5, 3.5, -1.0, 1.0), (2.5, 20.5, 1.0, -1.0), (21.5, 20.5, -1.0, -1.0)] { - p.rline(&[(cx, cy + 4.0 * sy), (cx, cy), (cx + 4.0 * sx, cy)], 1.0, K); - } - p.rect(6.5, 7.5, 17.5, 16.5, 1.0, t(B), B); - p.seg((6.5, 10.0), (17.5, 10.0), B); - } - I::Icons => { - p.person(6.75, 2.5, 0.75, B); - p.star((17.0, 7.0), 5.0, Y, N); - let heart: Vec = (0..40) - .map(|i| { - let tt = i as f32 / 40.0 * 2.0 * PI; - let x = 16.0 * tt.sin().powi(3); - let y = 13.0 * tt.cos() - 5.0 * (2.0 * tt).cos() - 2.0 * (3.0 * tt).cos() - (4.0 * tt).cos(); - (7.0 + x * 0.29, 17.0 - y * 0.29) - }) - .collect(); - p.shape(&heart, R, N); - p.rshape(&[(17.0, 12.5), (21.5, 17.0), (17.0, 21.5), (12.5, 17.0)], 0.8, G, N); - } - I::Models3d => { - let (top, ur, lr, bot, ll, ul, mid) = ((12.0, 2.5), (20.5, 7.2), (20.5, 16.8), (12.0, 21.5), (3.5, 16.8), (3.5, 7.2), (12.0, 11.9)); - p.poly(&[top, ur, mid, ul], t(B), N); - p.poly(&[ul, mid, bot, ll], B.a(0.55), N); - p.poly(&[mid, ur, lr, bot], B, N); - p.shape_w(&[top, ur, lr, bot, ll, ul], N, K, 1.0); - p.line(&[ul, mid, ur], K); - p.seg(mid, bot, K); - } - I::SmartArt => { - p.rect(8.5, 2.5, 15.5, 8.5, 1.0, G, N); - p.seg((12.0, 8.5), (12.0, 11.5), K); - p.rline(&[(5.5, 14.5), (5.5, 11.5), (18.5, 11.5), (18.5, 14.5)], 1.0, K); - p.rect(2.0, 14.5, 9.0, 20.5, 1.0, B, N); - p.rect(15.0, 14.5, 22.0, 20.5, 1.0, O, N); - } - I::Chart => p.bars(2.5, 4.0, 21.5, 20.5), - I::Zoom => { - p.rline(&[(10.5, 15.0), (2.0, 15.0), (2.0, 3.0), (18.0, 3.0), (18.0, 9.5)], 1.5, K); - p.seg((5.0, 6.5), (11.0, 6.5), K.a(0.5)); - p.magnifier((14.5, 14.5), 4.5, t(B), B); - } - I::Link => { - let link = |c: P2, col: C| { - let local = rrect_pts(-5.5, -2.8, 5.5, 2.8, 2.8); - p.shape(&tf(c, -45.0, &local), N, col); - }; - link((8.5, 15.5), K); - link((15.5, 8.5), B); - } - I::Action => { - p.rect(2.5, 3.5, 18.0, 12.5, 2.0, t(B), B); - p.cursor(11.5, 8.5, 0.82); - } - I::Comment => { - p.bubble(2.5, 3.5, 21.5, 16.5, t(Y), K); - p.seg((6.5, 8.0), (17.5, 8.0), K); - p.seg((6.5, 12.0), (14.0, 12.0), K); - } - I::TextBox => { - p.rect(3.5, 4.5, 20.5, 19.5, 0.5, N, K); - for (x, y) in [(3.5, 4.5), (20.5, 4.5), (3.5, 19.5), (20.5, 19.5)] { - p.rect(x - 1.5, y - 1.5, x + 1.5, y + 1.5, 0.3, B, N); - } - p.seg((7.5, 9.5), (16.5, 9.5), K); - p.seg((7.5, 14.5), (13.5, 14.5), K); - } - I::HeaderFooter => { - p.rect(4.0, 2.5, 20.0, 21.5, 1.2, N, K); - p.rect(6.5, 5.0, 17.5, 7.5, 0.6, B, N); - p.rect(6.5, 16.5, 17.5, 19.0, 0.6, B, N); - p.seg_w((7.0, 10.5), (17.0, 10.5), K.a(0.45), 0.8); - p.seg_w((7.0, 13.2), (14.5, 13.2), K.a(0.45), 0.8); - } - I::WordArt => { - p.text_rot("A", (13.5, 13.5), 22.0, PU, -10.0); - p.text_rot("A", (11.5, 11.5), 22.0, O, -10.0); - p.text_rot("A", (11.3, 11.5), 22.0, O, -10.0); - } - I::DateTime => { - p.rect(3.0, 5.0, 21.0, 21.0, 1.6, N, K); - p.rect(3.0, 5.0, 21.0, 9.5, 1.6, R, N); - p.rect(3.0, 5.0, 21.0, 21.0, 1.6, N, K); - p.seg((8.0, 2.8), (8.0, 6.5), K); - p.seg((16.0, 2.8), (16.0, 6.5), K); - for (i, (x, y)) in [(8.0, 13.5), (12.0, 13.5), (16.0, 13.5), (8.0, 17.5), (12.0, 17.5), (16.0, 17.5)].into_iter().enumerate() { - p.dot((x, y), 1.1, if i == 4 { B } else { K }); - } - } - I::SlideNumber => { - p.frame(2.0, 4.0, 22.0, 20.0); - p.seg((10.7, 7.5), (9.7, 16.5), B); - p.seg((15.0, 7.5), (14.0, 16.5), B); - p.seg((7.5, 10.5), (17.0, 10.5), B); - p.seg((7.0, 13.6), (16.5, 13.6), B); - } - I::Object => { - p.page(3.0, 2.0, 16.0, 19.0, N, K); - p.rect(11.5, 11.5, 21.5, 21.5, 1.6, B, N); - p.rect(14.2, 14.2, 18.8, 18.8, 0.6, N, W); - } - I::Equation => { - p.line_w(&[(18.5, 6.5), (18.0, 4.0), (6.0, 4.0), (12.5, 12.0), (6.0, 20.0), (18.0, 20.0), (18.5, 17.5)], B, 1.15); - } - I::Symbol => p.text("Ω", (12.0, 12.0), 22.0, K), - I::Video => { - p.rect(2.0, 6.0, 15.5, 18.0, 2.0, t(B), K); - p.rshape(&[(16.5, 10.5), (22.0, 7.0), (22.0, 17.0), (16.5, 13.5)], 0.6, N, K); - p.dot((6.0, 10.0), 1.2, R); - } - I::Audio => { - p.speaker(2.5, 12.0, t(PU), K); - p.arc((11.0, 12.0), 4.5, -45.0, 45.0, PU); - p.arc((11.0, 12.0), 8.5, -45.0, 45.0, PU); - } - I::ScreenRecording => { - p.monitor(2.0, 3.0, 22.0, 16.5); - p.circle((12.0, 9.75), 3.0, R, N); - } - I::Cameo => { - p.frame(2.0, 4.0, 22.0, 20.0); - p.circle((12.0, 12.0), 5.6, t(B), B); - p.dot((12.0, 10.4), 1.9, B); - p.poly(&arc_pts((12.0, 16.0), 3.4, 2.6, 180.0, 360.0), B, N); - } - - // ----- Draw tab ----- - I::Pen => { - p.tool((3.5, 20.5), -45.0, 23.0, 2.2, 5.0, B, K, 3.5, K); - } - I::Pencil => { - p.tool((3.5, 20.5), -45.0, 22.0, 2.6, 5.0, Y, K, 3.5, R); - } - I::HighlighterPen => { - let m = |pts: &[P2]| tf((6.0, 15.0), -45.0, pts); - p.poly(&m(&[(0.0, -1.0), (3.0, -3.2), (3.0, 3.2), (0.0, 2.2)]), Y, K); - p.poly(&m(&[(3.0, -3.6), (14.5, -3.6), (14.5, 3.6), (3.0, 3.6)]), t(Y), K); - p.rect(2.0, 19.5, 13.0, 22.5, 0.8, Y, N); - } - I::Eraser => { - let m = |pts: &[P2]| tf((12.5, 11.0), -45.0, pts); - p.poly(&m(&[(-8.0, -4.0), (-1.0, -4.0), (-1.0, 4.0), (-8.0, 4.0)]), R, N); - p.rshape(&m(&[(-8.0, -4.0), (8.0, -4.0), (8.0, 4.0), (-8.0, 4.0)]), 1.0, N, K); - p.seg((12.0, 21.0), (21.0, 21.0), K); - } - I::LassoSelect => { - let mut e = arc_pts((13.0, 9.0), 8.5, 6.0, 120.0, 480.0); - e.truncate(e.len().saturating_sub(1)); - p.dashed(&e, B, 2.6, 1.7); - p.curve((8.8, 14.2), (5.5, 16.5), (10.0, 19.0), (5.5, 22.0), K); - p.dot((8.8, 14.2), 1.4, K); - } - I::InkToShape => { - p.scribble(1.5, 2.0, 12.5, 11.0, K); - p.circle((16.5, 16.5), 5.0, t(B), B); - } - I::InkToText => { - p.scribble(1.5, 2.0, 12.5, 11.0, K); - p.letter_a(12.0, 9.0, 21.5, 21.5, B, 1.2); - } - I::InkToMath => { - p.scribble(1.5, 2.0, 12.5, 11.0, K); - p.line_w(&[(21.0, 11.0), (12.5, 11.0), (17.0, 16.25), (12.5, 21.5), (21.0, 21.5)], B, 1.1); - } - I::Ruler => { - let m = |pts: &[P2]| tf((12.0, 12.0), -45.0, pts); - p.rshape(&m(&[(-11.5, -4.0), (11.5, -4.0), (11.5, 4.0), (-11.5, 4.0)]), 0.8, Y.a(0.85), K); - for (i, u) in [-8.0_f32, -5.0, -2.0, 1.0, 4.0, 7.0].into_iter().enumerate() { - let l = if i % 2 == 0 { 3.2 } else { 1.8 }; - p.line_w(&m(&[(u, -4.0), (u, -4.0 + l)]), D, 0.8); - } - } - - // ----- Design ----- - I::Themes => { - p.rect(9.5, 2.5, 21.5, 14.5, 2.0, O, N); - p.rect(2.5, 8.5, 15.0, 21.5, 2.0, B, N); - p.text("Aa", (8.75, 15.0), 10.0, W); - } - I::Variants => { - p.rect(8.0, 2.5, 21.5, 11.0, 1.2, t(PU), PU); - p.rect(5.0, 7.0, 18.5, 15.5, 1.2, t(G), G); - p.rect(2.5, 12.0, 16.0, 21.0, 1.2, B, B); - } - I::Colors => { - for (i, c) in [R, Y, G, B].into_iter().enumerate() { - let x = 3.0 + i as f32 * 4.5; - p.rect(x, 4.0, x + 4.5, 20.0, 0.0, c, N); - } - p.rect(3.0, 4.0, 21.0, 20.0, 0.0, N, K); - } - I::Fonts => { - p.text("A", (8.0, 11.0), 20.0, K); - p.text("g", (17.0, 10.0), 18.0, B); - } - I::BackgroundStyles => { - p.poly(&[(2.7, 4.7), (21.3, 4.7), (2.7, 19.3)], t(B), N); - p.poly(&[(21.3, 4.7), (21.3, 19.3), (2.7, 19.3)], PU.a(0.7), N); - p.frame(2.0, 4.0, 22.0, 20.0); - } - I::SlideSize => { - p.rline(&[(17.0, 12.0), (17.0, 19.0), (2.5, 19.0), (2.5, 7.0), (10.0, 7.0)], 1.5, K); - p.arrow((9.0, 13.0), (21.5, 2.5), B); - } - I::FormatBackground => { - p.rline(&[(10.0, 17.0), (2.0, 17.0), (2.0, 4.0), (20.0, 4.0), (20.0, 9.0)], 1.5, K); - p.rect(4.5, 6.5, 13.5, 12.5, 0.6, t(PU), N); - p.brush((12.0, 20.5), -45.0, 13.0, Y); - } - I::DesignIdeas => { - p.circle((12.0, 9.5), 6.0, Y.a(0.5), K); - p.seg((9.6, 15.7), (9.6, 17.0), K); - p.seg((14.4, 15.7), (14.4, 17.0), K); - p.seg((9.5, 18.0), (14.5, 18.0), K); - p.seg((10.5, 21.0), (13.5, 21.0), K); - for a in [-160.0_f32, -125.0, -55.0, -20.0] { - let (s, c) = rad(a).sin_cos(); - p.seg((12.0 + c * 8.0, 9.5 + s * 8.0), (12.0 + c * 10.0, 9.5 + s * 10.0), Y); - } - } - - // ----- Transitions / animations ----- - I::Preview => { - p.circle((12.0, 12.0), 9.5, N, K); - p.rshape(&[(9.5, 7.0), (17.0, 12.0), (9.5, 17.0)], 0.8, G, N); - } - I::EffectOptions => { - p.rect(2.5, 8.5, 15.5, 21.5, 1.5, t(B), K); - p.arrow((9.0, 15.0), (21.5, 2.5), B); - } - I::Sound => { - let head = |c: P2| { - let pts = tf(c, -22.0, &arc_pts((0.0, 0.0), 3.0, 2.1, 0.0, 351.0)); - p.poly(&pts, PU, N); - }; - head((6.3, 18.0)); - head((16.3, 15.5)); - p.seg((9.0, 17.0), (9.0, 5.0), PU); - p.seg((19.0, 14.5), (19.0, 2.5), PU); - p.poly(&[(9.0, 4.5), (19.0, 2.0), (19.0, 5.5), (9.0, 8.0)], PU, N); - } - I::Duration => { - p.circle((12.0, 13.5), 8.0, N, K); - p.rect(10.0, 2.0, 14.0, 4.0, 0.6, K, N); - p.seg((12.0, 4.0), (12.0, 5.5), K); - p.seg((12.0, 13.5), (15.8, 9.0), B); - p.dot((12.0, 13.5), 1.3, B); - } - I::ApplyToAll => { - p.rline(&[(6.0, 7.5), (6.0, 3.0), (21.5, 3.0), (21.5, 13.5), (17.5, 13.5)], 1.5, K); - p.rect(2.5, 7.5, 17.5, 18.0, 1.5, N, K); - p.badge((18.0, 18.0), 4.8, G, Glyph::Check); - } - I::AnimEntrance => { - p.star((13.5, 12.5), 8.5, G, N); - p.seg((1.5, 9.0), (4.5, 9.0), K); - p.seg((0.8, 13.5), (4.0, 13.5), K); - } - I::AnimEmphasis => { - p.star((12.0, 12.5), 9.5, Y, N); - } - I::AnimExit => { - p.star((10.5, 12.5), 8.5, R, N); - p.seg((19.5, 9.0), (22.5, 9.0), K); - p.seg((20.0, 13.5), (23.2, 13.5), K); - } - I::AnimPath => { - let pts = cubic_pts((4.5, 19.5), (3.0, 6.0), (20.0, 19.0), (19.5, 5.0), 60); - p.dotted(&pts[6..pts.len().saturating_sub(4)], K, 2.4, 0.55); - p.head((19.5, 4.0), -90.0, 4.0, K); - p.circle((4.5, 19.5), 2.4, G, N); - } - I::AnimationPane => { - p.rect(2.5, 3.0, 21.5, 21.0, 1.5, N, K); - p.seg((11.0, 3.0), (11.0, 21.0), K); - p.rect(4.5, 7.0, 8.5, 12.0, 0.5, t(B), N); - for (y, c) in [(7.5, G), (12.0, Y), (16.5, R)] { - p.seg_w((13.5, y), (19.0, y), c, 1.4); - } - } - I::Trigger => { - p.rshape(&[(14.5, 2.0), (5.0, 13.5), (11.5, 13.5), (9.5, 22.0), (19.0, 9.5), (12.5, 9.5)], 0.6, Y, K); - } - I::AnimationPainter => { - p.star((7.5, 7.5), 6.0, G, N); - p.brush((12.0, 12.0), 45.0, 13.0, Y); - } - I::AddAnimation => { - p.star((10.5, 11.0), 9.0, Y, N); - p.badge((18.5, 18.5), 4.8, G, Glyph::Plus); - } - - // ----- Slide show ----- - I::PlayFromStart => { - p.frame(2.0, 4.0, 22.0, 20.0); - p.rect(7.0, 8.0, 9.2, 16.0, 0.5, G, N); - p.rshape(&[(11.0, 8.0), (17.5, 12.0), (11.0, 16.0)], 0.6, G, N); - } - I::PlayFromCurrent => { - p.frame(2.0, 4.0, 22.0, 20.0); - p.rshape(&[(9.5, 8.0), (16.0, 12.0), (9.5, 16.0)], 0.6, G, N); - } - I::PresenterView => { - p.monitor(2.0, 3.0, 22.0, 16.5); - p.rect(4.5, 5.5, 13.0, 12.0, 0.6, t(B), B); - p.seg_w((15.0, 6.5), (19.5, 6.5), K, 0.8); - p.seg_w((15.0, 9.5), (19.5, 9.5), K, 0.8); - p.seg_w((15.0, 12.5), (18.0, 12.5), K, 0.8); - } - I::CustomShow => { - p.rect(2.5, 2.5, 12.5, 8.0, 1.0, N, K); - p.rect(2.5, 9.5, 12.5, 15.0, 1.0, O, O); - p.rect(2.5, 16.5, 12.5, 22.0, 1.0, N, K); - p.rshape(&[(15.5, 8.0), (21.5, 12.25), (15.5, 16.5)], 0.6, G, N); - } - I::SetUpShow => { - p.frame(2.0, 4.0, 19.5, 16.5); - p.seg((5.0, 8.0), (13.0, 8.0), K.a(0.5)); - p.gear_badge((17.5, 17.5), 5.5, B); - } - I::RehearseTimings => { - p.frame(2.0, 4.0, 19.5, 16.5); - p.seg((5.0, 8.0), (13.0, 8.0), K.a(0.5)); - p.clock_badge((17.5, 17.5), 5.2, B); - } - I::Record => { - p.circle((12.0, 12.0), 9.5, N, K); - p.circle((12.0, 12.0), 5.5, R, N); - } - I::Subtitles => { - p.rect(2.0, 4.5, 22.0, 19.5, 2.0, N, K); - p.seg((5.5, 12.0), (11.0, 12.0), B); - p.seg((13.5, 12.0), (18.5, 12.0), B); - p.seg((5.5, 16.0), (8.5, 16.0), B); - p.seg((11.0, 16.0), (18.5, 16.0), B); - } - I::Coach => { - p.person(9.0, 5.0, 1.2, t(B)); - p.circle((9.0, 8.1), 3.1, N, B); - p.line(&arc_pts((9.0, 19.4), 6.2, 6.0, 180.0, 360.0), B); - p.star((18.5, 6.0), 4.2, Y, N); - } - - // ----- Review ----- - I::Spelling => { - p.text("abc", (11.5, 7.0), 12.0, K); - p.line_w(&[(6.0, 16.0), (10.0, 20.0), (19.5, 11.0)], G, 1.3); - } - I::Thesaurus => { - p.rect(8.0, 2.5, 19.5, 21.5, 1.2, t(B), N); - p.rect(4.5, 2.5, 19.5, 21.5, 1.5, N, K); - p.seg((8.0, 2.5), (8.0, 21.5), K); - p.poly(&[(13.5, 2.5), (17.0, 2.5), (17.0, 9.0), (15.25, 7.5), (13.5, 9.0)], R, N); - } - I::Accessibility => { - p.circle((12.0, 12.0), 9.8, N, B); - p.dot((12.0, 6.5), 1.6, B); - p.seg((6.8, 9.6), (17.2, 9.6), B); - p.seg((12.0, 9.6), (12.0, 13.8), B); - p.line(&[(9.0, 18.5), (12.0, 13.8), (15.0, 18.5)], B); - } - I::Translate => { - p.rect(10.0, 9.0, 21.5, 20.5, 2.0, O, N); - p.rect(2.5, 3.5, 14.0, 15.0, 2.0, B, N); - p.letter_a(5.5, 5.8, 11.0, 12.8, W, 0.9); - p.seg_w((17.75, 10.8), (17.75, 11.9), W, 0.8); - p.seg_w((15.2, 12.9), (20.3, 12.9), W, 0.8); - p.seg_w((15.6, 14.4), (19.9, 18.9), W, 0.8); - p.seg_w((19.9, 14.4), (15.6, 18.9), W, 0.8); - } - I::Language => { - p.circle((12.0, 12.0), 9.5, N, B); - p.ellipse((12.0, 12.0), 4.2, 9.5, N, B); - p.seg_w((2.5, 12.0), (21.5, 12.0), B, 0.8); - p.seg_w((4.3, 7.0), (19.7, 7.0), B, 0.8); - p.seg_w((4.3, 17.0), (19.7, 17.0), B, 0.8); - } - I::NewComment => { - p.bubble(2.0, 3.0, 19.5, 15.0, N, K); - p.badge((18.0, 17.5), 5.0, G, Glyph::Plus); - } - I::DeleteComment => { - p.bubble(2.0, 3.0, 19.5, 15.0, N, K); - p.badge((18.0, 17.5), 5.0, R, Glyph::Cross); - } - I::PrevComment => { - p.bubble(2.5, 3.0, 21.5, 17.0, N, K); - p.arrow((16.5, 10.0), (7.0, 10.0), B); - } - I::NextComment => { - p.bubble(2.5, 3.0, 21.5, 17.0, N, K); - p.arrow((7.5, 10.0), (17.0, 10.0), B); - } - I::ShowComments => { - p.rline(&[(8.0, 7.5), (8.0, 2.5), (21.5, 2.5), (21.5, 13.0), (17.0, 13.0)], 2.0, K); - p.bubble(2.5, 7.5, 16.5, 17.5, t(Y), K); - p.seg((5.5, 11.0), (13.5, 11.0), K); - p.seg((5.5, 14.2), (11.0, 14.2), K); - } - I::Compare => { - p.rect(2.5, 2.5, 11.0, 15.5, 1.2, N, K); - p.rect(13.0, 8.5, 21.5, 21.5, 1.2, t(B), B); - p.arrow((13.0, 4.0), (21.5, 4.0), K); - p.arrow((11.0, 20.0), (2.5, 20.0), K); - } - I::ReadOnly => { - p.page(3.0, 2.0, 16.0, 20.5, N, K); - p.lock(13.0, 15.0, 21.5, 22.0); - } - - // ----- View ----- - I::ViewNormal => { - p.rect(2.0, 3.5, 22.0, 20.5, 1.5, N, K); - p.seg((8.0, 3.5), (8.0, 20.5), K); - for y in [6.0, 10.5, 15.0] { - p.rect(3.9, y, 6.2, y + 2.6, 0.3, K.a(0.55), N); - } - p.rect(10.0, 6.0, 20.0, 15.0, 0.6, t(B), B); - p.seg_w((10.0, 18.0), (17.0, 18.0), K.a(0.5), 0.8); - } - I::ViewOutline => { - p.rect(2.0, 3.5, 22.0, 20.5, 1.5, N, K); - p.seg((11.0, 3.5), (11.0, 20.5), K); - p.seg_w((4.0, 7.0), (9.0, 7.0), K, 0.8); - p.seg_w((5.5, 10.0), (9.0, 10.0), K, 0.8); - p.seg_w((5.5, 13.0), (9.0, 13.0), K, 0.8); - p.seg_w((4.0, 16.0), (9.0, 16.0), K, 0.8); - p.rect(13.0, 7.0, 20.0, 13.0, 0.6, t(B), B); - } - I::ViewSorter => { - for (i, (x0, x1)) in [(2.5, 11.0), (13.0, 21.5)].into_iter().enumerate() { - for (j, (y0, y1)) in [(4.0, 10.5), (13.5, 20.0)].into_iter().enumerate() { - if i == 0 && j == 0 { - p.rect(x0, y0, x1, y1, 0.8, t(B), B); - } else { - p.rect(x0, y0, x1, y1, 0.8, N, K); - } - } - } - } - I::ViewNotesPage => { - p.rect(5.0, 2.0, 19.0, 22.0, 1.2, N, K); - p.rect(7.5, 4.5, 16.5, 10.5, 0.6, t(B), B); - p.seg_w((7.5, 14.0), (16.5, 14.0), K, 0.8); - p.seg_w((7.5, 17.5), (14.0, 17.5), K, 0.8); - } - I::ViewReading => { - p.book(t(B), K); - p.seg((12.0, 7.0), (12.0, 20.5), K); - } - I::SlideMaster => { - p.rect(2.0, 2.5, 15.0, 10.5, 1.2, t(PU), PU); - p.seg((4.5, 5.5), (11.0, 5.5), PU); - p.rline(&[(5.0, 10.5), (5.0, 19.5), (9.0, 19.5)], 1.0, K); - p.seg((5.0, 14.0), (9.0, 14.0), K); - p.rect(9.0, 12.0, 17.0, 16.0, 0.8, N, K); - p.rect(9.0, 17.5, 17.0, 21.5, 0.8, N, K); - } - I::HandoutMaster => { - p.rect(4.0, 2.0, 20.0, 22.0, 1.2, N, K); - for (x, y) in [(6.5, 4.5), (12.75, 4.5), (6.5, 11.0), (12.75, 11.0)] { - p.rect(x, y, x + 4.75, y + 4.5, 0.4, t(PU), PU); - } - p.seg_w((6.5, 19.0), (17.5, 19.0), K, 0.8); - } - I::NotesMaster => { - p.rline(&[(8.0, 4.0), (8.0, 2.0), (21.0, 2.0), (21.0, 19.0), (16.0, 19.0)], 1.2, PU); - p.rect(3.0, 4.5, 16.0, 22.0, 1.2, N, K); - p.rect(5.5, 7.0, 13.5, 12.0, 0.5, t(PU), PU); - p.seg_w((5.5, 15.5), (13.5, 15.5), K, 0.8); - p.seg_w((5.5, 18.8), (11.5, 18.8), K, 0.8); - } - I::Gridlines => { - for v in [9.0, 15.0] { - p.seg_w((v, 3.0), (v, 21.0), K.a(0.6), 0.7); - p.seg_w((3.0, v), (21.0, v), K.a(0.6), 0.7); - } - p.rect(3.0, 3.0, 21.0, 21.0, 1.2, N, K); - } - I::Guides => { - p.rect(3.0, 3.0, 21.0, 21.0, 1.2, N, K); - p.dashed(&[(12.0, 1.5), (12.0, 22.5)], B, 2.2, 1.6); - p.dashed(&[(1.5, 12.0), (22.5, 12.0)], B, 2.2, 1.6); - } - I::Notes => { - p.rect(4.0, 4.0, 20.0, 21.5, 1.5, t(Y), K); - for x in [8.0, 12.0, 16.0] { - p.seg((x, 2.3), (x, 5.8), K); - } - p.seg_w((7.5, 10.5), (16.5, 10.5), K, 0.85); - p.seg_w((7.5, 14.0), (16.5, 14.0), K, 0.85); - p.seg_w((7.5, 17.5), (13.5, 17.5), K, 0.85); - } - I::ZoomGlass => p.magnifier((10.0, 10.0), 7.0, t(B), K), - I::FitToWindow => { - for (cx, cy, sx, sy) in [(2.5, 2.5, 1.0, 1.0), (21.5, 2.5, -1.0, 1.0), (2.5, 21.5, 1.0, -1.0), (21.5, 21.5, -1.0, -1.0)] { - p.rline(&[(cx, cy + 5.0 * sy), (cx, cy), (cx + 5.0 * sx, cy)], 0.8, K); - } - p.rect(7.0, 8.0, 17.0, 16.0, 1.0, t(B), B); - } - I::Grayscale => { - p.rect(3.0, 3.0, 9.0, 21.0, 0.0, K.a(0.85), N); - p.rect(9.0, 3.0, 15.0, 21.0, 0.0, K.a(0.45), N); - p.rect(15.0, 3.0, 21.0, 21.0, 0.0, K.a(0.15), N); - p.rect(3.0, 3.0, 21.0, 21.0, 1.2, N, K); - } - I::NewWindow => { - p.rect(2.0, 3.5, 20.0, 17.0, 1.5, N, K); - p.seg((2.0, 7.0), (20.0, 7.0), K); - p.dot((4.5, 5.25), 0.6, K); - p.badge((18.5, 18.0), 4.8, G, Glyph::Plus); - } - I::Macros => { - p.page(3.0, 2.0, 17.0, 21.0, N, K); - p.line(&[(8.5, 9.0), (6.0, 11.5), (8.5, 14.0)], B); - p.line(&[(11.5, 9.0), (14.0, 11.5), (11.5, 14.0)], B); - p.gear_badge((17.5, 17.5), 5.2, O); - } - - // ----- Arrange ----- - I::BringForward => { - p.layer(10.0, 15.5, 8.0, 4.0, N, K); - p.layer(10.0, 8.5, 8.0, 4.0, B, B); - p.arrow((21.0, 13.0), (21.0, 3.0), K); - } - I::SendBackward => { - p.layer(10.0, 15.5, 8.0, 4.0, B, B); - p.layer(10.0, 8.5, 8.0, 4.0, N, K); - p.arrow((21.0, 11.0), (21.0, 21.0), K); - } - I::BringToFront => { - p.layer(10.0, 18.5, 7.5, 3.2, N, K); - p.layer(10.0, 12.0, 7.5, 3.2, N, K); - p.layer(10.0, 5.5, 7.5, 3.2, B, B); - p.arrow((20.5, 14.0), (20.5, 4.5), K); - p.seg((18.0, 2.5), (23.0, 2.5), K); - } - I::SendToBack => { - p.layer(10.0, 18.5, 7.5, 3.2, B, B); - p.layer(10.0, 12.0, 7.5, 3.2, N, K); - p.layer(10.0, 5.5, 7.5, 3.2, N, K); - p.arrow((20.5, 10.0), (20.5, 19.5), K); - p.seg((18.0, 21.5), (23.0, 21.5), K); - } - I::Group => { - p.dashed(&[(2.5, 2.5), (21.5, 2.5), (21.5, 21.5), (2.5, 21.5), (2.5, 2.5)], B, 2.0, 1.6); - p.rect(5.5, 5.5, 13.0, 13.0, 0.8, t(G), G); - p.circle((15.5, 15.5), 4.3, t(O), O); - } - I::Ungroup => { - p.dashed_w(&[(2.0, 2.0), (12.0, 2.0), (12.0, 12.0), (2.0, 12.0), (2.0, 2.0)], B, 1.6, 1.3, 0.8); - p.rect(4.5, 4.5, 9.5, 9.5, 0.6, t(G), G); - p.dashed_w(&[(12.0, 12.0), (22.0, 12.0), (22.0, 22.0), (12.0, 22.0), (12.0, 12.0)], B, 1.6, 1.3, 0.8); - p.circle((17.0, 17.0), 2.9, t(O), O); - } - I::AlignObjects => { - p.dashed(&[(2.0, 12.0), (22.0, 12.0)], K, 2.0, 1.5); - p.rect(3.5, 4.0, 8.0, 20.0, 0.8, B, N); - p.rect(10.0, 7.5, 14.0, 16.5, 0.8, O, N); - p.rect(16.0, 5.5, 20.5, 18.5, 0.8, G, N); - } - I::AlignObjLeft => { - p.rect(3.5, 5.0, 19.5, 10.5, 0.8, t(B), B); - p.rect(3.5, 13.5, 13.5, 19.0, 0.8, t(B), B); - p.seg((3.5, 2.5), (3.5, 21.5), K); - } - I::AlignObjCenter => { - p.rect(4.0, 5.0, 20.0, 10.5, 0.8, t(B), B); - p.rect(7.0, 13.5, 17.0, 19.0, 0.8, t(B), B); - p.seg((12.0, 2.5), (12.0, 21.5), K); - } - I::AlignObjRight => { - p.rect(4.5, 5.0, 20.5, 10.5, 0.8, t(B), B); - p.rect(10.5, 13.5, 20.5, 19.0, 0.8, t(B), B); - p.seg((20.5, 2.5), (20.5, 21.5), K); - } - I::AlignObjTop => { - p.rect(5.0, 3.5, 10.5, 19.5, 0.8, t(B), B); - p.rect(13.5, 3.5, 19.0, 13.5, 0.8, t(B), B); - p.seg((2.5, 3.5), (21.5, 3.5), K); - } - I::AlignObjMiddle => { - p.rect(5.0, 4.0, 10.5, 20.0, 0.8, t(B), B); - p.rect(13.5, 7.0, 19.0, 17.0, 0.8, t(B), B); - p.seg((2.5, 12.0), (21.5, 12.0), K); - } - I::AlignObjBottom => { - p.rect(5.0, 4.5, 10.5, 20.5, 0.8, t(B), B); - p.rect(13.5, 10.5, 19.0, 20.5, 0.8, t(B), B); - p.seg((2.5, 20.5), (21.5, 20.5), K); - } - I::DistributeH => { - p.seg((2.5, 3.0), (2.5, 21.0), K); - p.seg((21.5, 3.0), (21.5, 21.0), K); - p.rect(4.9, 7.0, 8.2, 17.0, 0.6, t(B), B); - p.rect(10.35, 5.0, 13.65, 19.0, 0.6, t(B), B); - p.rect(15.8, 8.5, 19.1, 15.5, 0.6, t(B), B); - } - I::DistributeV => { - p.seg((3.0, 2.5), (21.0, 2.5), K); - p.seg((3.0, 21.5), (21.0, 21.5), K); - p.rect(7.0, 4.9, 17.0, 8.2, 0.6, t(B), B); - p.rect(5.0, 10.35, 19.0, 13.65, 0.6, t(B), B); - p.rect(8.5, 15.8, 15.5, 19.1, 0.6, t(B), B); - } - I::RotateLeft => { - p.poly(&[(3.0, 21.5), (3.0, 13.0), (12.0, 21.5)], t(B), B); - p.arc_arrow((13.0, 12.0), 7.5, 30.0, -160.0, K); - } - I::RotateRight => { - p.poly(&[(21.0, 21.5), (21.0, 13.0), (12.0, 21.5)], t(B), B); - p.arc_arrow((11.0, 12.0), 7.5, 150.0, 340.0, K); - } - I::FlipH => { - p.dashed(&[(12.0, 2.0), (12.0, 22.0)], K, 1.8, 1.4); - p.poly(&[(9.5, 5.0), (9.5, 19.0), (2.5, 19.0)], N, K); - p.poly(&[(14.5, 5.0), (21.5, 19.0), (14.5, 19.0)], B, B); - } - I::FlipV => { - p.dashed(&[(2.0, 12.0), (22.0, 12.0)], K, 1.8, 1.4); - p.poly(&[(5.0, 9.5), (19.0, 9.5), (19.0, 2.5)], N, K); - p.poly(&[(5.0, 14.5), (19.0, 21.5), (19.0, 14.5)], B, B); - } - I::SelectionPane => { - p.rect(3.0, 2.5, 21.0, 21.5, 1.5, N, K); - p.rect(4.6, 4.6, 19.4, 9.4, 0.8, t(B), N); - for (y, c) in [(7.0, B), (12.0, O), (17.0, G)] { - p.rect(6.0, y - 1.3, 8.6, y + 1.3, 0.4, c, N); - p.seg_w((10.5, y), (17.5, y), K, 0.85); - } - } - I::Rotate => { - p.rect(9.0, 9.0, 15.0, 15.0, 0.8, t(B), B); - p.arc_arrow((12.0, 12.0), 8.5, -70.0, 220.0, K); - } - - // ----- Picture ----- - I::Crop => { - p.rline_w(&[(6.5, 2.0), (6.5, 17.5), (22.0, 17.5)], 0.6, K, 1.25); - p.rline_w(&[(2.0, 6.5), (17.5, 6.5), (17.5, 22.0)], 0.6, B, 1.25); - } - I::Corrections => { - p.poly(&arc_pts((12.0, 12.0), 5.2, 5.2, -90.0, 90.0), Y, N); - p.circle((12.0, 12.0), 5.2, N, K); - for i in 0..8 { - let (s, c) = rad(i as f32 * 45.0).sin_cos(); - p.seg((12.0 + c * 7.6, 12.0 + s * 7.6), (12.0 + c * 9.8, 12.0 + s * 9.8), Y); - } - } - I::ColorAdjust => { - p.circle((12.0, 8.0), 5.6, R.a(0.6), N); - p.circle((8.3, 14.6), 5.6, G.a(0.6), N); - p.circle((15.7, 14.6), 5.6, B.a(0.6), N); - } - I::ArtisticEffects => { - p.rect(2.0, 4.0, 22.0, 20.0, 1.5, N, K); - p.curve_w((5.5, 16.0), (8.0, 4.5), (14.0, 21.0), (18.5, 8.0), PU, 2.0); - } - I::Transparency => { - let cs = 13.0 / 4.0; - for i in 0..4 { - for j in 0..4 { - if (i + j) % 2 == 0 { - let (x, y) = (3.0 + i as f32 * cs, 3.0 + j as f32 * cs); - p.rect(x, y, x + cs, y + cs, 0.0, K.a(0.3), N); - } - } - } - p.rect(3.0, 3.0, 16.0, 16.0, 0.6, N, K.a(0.6)); - p.rect(9.0, 9.0, 21.0, 21.0, 1.2, B.a(0.55), B); - } - I::CompressPictures => { - p.picture(6.5, 6.0, 17.5, 18.0); - p.arrow((0.8, 12.0), (5.0, 12.0), K); - p.arrow((23.2, 12.0), (19.0, 12.0), K); - } - I::ChangePicture => { - p.picture(2.0, 3.0, 18.0, 15.5); - let c = (17.5, 17.5); - if p.dis { - p.circle(c, 5.0, N, G); - } else { - p.circle(c, 5.0, G, N); - } - let wc = if p.dis { K } else { W }; - p.arc_arrow(c, 2.8, 200.0, 330.0, wc); - p.arc_arrow(c, 2.8, 20.0, 150.0, wc); - } - I::ResetPicture => { - p.picture(2.0, 3.0, 18.0, 15.5); - p.arc_arrow((17.0, 17.5), 4.0, -10.0, -260.0, B); - } - I::RemoveBackground => { - p.rect(2.0, 4.0, 22.0, 20.0, 1.5, PU.a(0.55), N); - p.dot((12.0, 10.0), 3.0, B); - p.poly(&arc_pts((12.0, 19.3), 6.0, 5.5, 180.0, 360.0), B, N); - p.rect(2.0, 4.0, 22.0, 20.0, 1.5, N, K); - } - I::AltText => { - p.picture(2.0, 2.0, 15.5, 12.5); - p.seg((2.5, 16.5), (21.5, 16.5), K); - p.seg((2.5, 20.5), (15.0, 20.5), K); - } - - // ----- Table ----- - I::InsertAbove | I::InsertBelow | I::InsertLeft | I::InsertRight => { - let (grid, add): ((f32, f32, f32, f32, usize, usize), (f32, f32, f32, f32)) = match icon { - I::InsertAbove => ((3.0, 10.0, 21.0, 21.0, 2, 3), (3.0, 2.5, 21.0, 7.5)), - I::InsertBelow => ((3.0, 3.0, 21.0, 14.0, 2, 3), (3.0, 16.5, 21.0, 21.5)), - I::InsertLeft => ((10.0, 3.0, 21.0, 21.0, 3, 2), (2.5, 3.0, 7.5, 21.0)), - _ => ((3.0, 3.0, 14.0, 21.0, 3, 2), (16.5, 3.0, 21.5, 21.0)), - }; - p.table(grid.0, grid.1, grid.2, grid.3, grid.4, grid.5, N); - p.rect(add.0, add.1, add.2, add.3, 1.0, t(G), G); - let c = ((add.0 + add.2) * 0.5, (add.1 + add.3) * 0.5); - p.seg_w((c.0 - 1.6, c.1), (c.0 + 1.6, c.1), G, 0.9); - p.seg_w((c.0, c.1 - 1.6), (c.0, c.1 + 1.6), G, 0.9); - } - I::DeleteTable => { - p.table(2.5, 3.0, 19.5, 17.0, 3, 3, B); - p.badge((18.0, 18.0), 5.0, R, Glyph::Cross); - } - I::MergeCells => { - p.rect(2.5, 6.0, 21.5, 18.0, 1.2, N, K); - p.dashed(&[(12.0, 6.0), (12.0, 18.0)], K.a(0.6), 1.5, 1.2); - p.arrow((4.5, 12.0), (10.2, 12.0), B); - p.arrow((19.5, 12.0), (13.8, 12.0), B); - } - I::SplitCells => { - p.rect(2.5, 6.0, 21.5, 18.0, 1.2, N, K); - p.seg((12.0, 6.0), (12.0, 18.0), K); - p.arrow((10.0, 12.0), (4.5, 12.0), B); - p.arrow((14.0, 12.0), (19.5, 12.0), B); - } - I::Borders => { - p.dotted(&[(12.0, 5.0), (12.0, 19.0)], K, 2.2, 0.45); - p.dotted(&[(5.0, 12.0), (19.0, 12.0)], K, 2.2, 0.45); - p.rect(3.0, 3.0, 21.0, 21.0, 0.6, N, B); - } - I::Shading => { - p.rect(3.0, 3.0, 12.0, 12.0, 0.0, Y.a(0.8), N); - p.rect(12.0, 12.0, 21.0, 21.0, 0.0, Y.a(0.8), N); - p.table(3.0, 3.0, 21.0, 21.0, 2, 2, N); - } - - // ----- Chart ----- - I::ChartElements => { - p.bars(2.5, 3.5, 16.0, 17.5); - p.badge((18.5, 18.5), 4.8, G, Glyph::Plus); - } - I::ChartStyles => { - p.bars(2.5, 6.0, 15.5, 20.5); - p.brush((14.0, 10.0), -45.0, 12.0, PU); - } - I::SwitchRowCol => { - p.table(2.5, 2.5, 11.0, 11.0, 2, 2, B); - p.rect(13.0, 13.0, 17.25, 21.5, 0.6, O, N); - p.table(13.0, 13.0, 21.5, 21.5, 2, 2, N); - p.line(&quad_pts((14.0, 4.0), (20.0, 4.0), (20.0, 9.5), 10), K); - p.head((20.0, 11.0), 90.0, 3.6, K); - p.line(&quad_pts((10.0, 20.0), (4.0, 20.0), (4.0, 14.5), 10), K); - p.head((4.0, 13.0), -90.0, 3.6, K); - } - I::EditData => { - p.table(2.5, 3.0, 18.0, 16.0, 3, 3, B); - p.tool((11.0, 21.5), -45.0, 14.0, 2.2, 3.8, Y, K, 2.5, R); - } - I::ChangeChartType => { - p.circle((8.0, 8.0), 5.8, t(B), B); - let mut wedge = vec![(8.0, 8.0)]; - wedge.extend(arc_pts((8.0, 8.0), 5.8, 5.8, -90.0, 0.0)); - p.poly(&wedge, O, N); - p.rect(13.0, 15.0, 15.8, 21.0, 0.4, G, N); - p.rect(17.5, 11.0, 20.3, 21.0, 0.4, B, N); - p.seg((11.5, 21.5), (22.0, 21.5), K); - } - - // ----- Media ----- - I::Play => p.rshape(&[(6.5, 4.0), (19.5, 12.0), (6.5, 20.0)], 1.0, G, N), - I::Pause => { - p.rect(6.0, 4.5, 10.0, 19.5, 1.0, K, N); - p.rect(14.0, 4.5, 18.0, 19.5, 1.0, K, N); - } - I::Stop => p.rect(5.0, 5.0, 19.0, 19.0, 2.0, K, N), - I::TrimMedia => { - p.rect(2.0, 8.0, 22.0, 16.0, 1.2, N, K); - p.rect(7.0, 8.0, 17.0, 16.0, 0.0, t(Y), N); - p.rect(5.8, 5.0, 8.2, 19.0, 0.8, Y, N); - p.rect(15.8, 5.0, 18.2, 19.0, 0.8, Y, N); - } - I::Volume => { - p.speaker(2.5, 12.0, N, K); - p.arc((11.0, 12.0), 3.8, -45.0, 45.0, B); - p.arc((11.0, 12.0), 7.0, -45.0, 45.0, B); - p.arc((11.0, 12.0), 10.2, -40.0, 40.0, B); - } - I::Loop => { - p.path_arrow(&round_pts(&[(4.0, 13.0), (4.0, 7.0), (20.0, 7.0)], 2.5, false), G); - p.path_arrow(&round_pts(&[(20.0, 11.0), (20.0, 17.0), (4.0, 17.0)], 2.5, false), G); - } - I::Bookmark => { - p.rshape(&[(6.0, 2.5), (18.0, 2.5), (18.0, 21.5), (12.0, 16.5), (6.0, 21.5)], 0.8, R, N); - } - - // ----- Window chrome ----- - I::Home => { - p.rect(10.0, 14.5, 14.0, 21.0, 0.8, B, N); - p.line(&[(2.5, 11.5), (12.0, 3.0), (21.5, 11.5)], K); - p.rline(&[(5.0, 9.5), (5.0, 21.0), (19.0, 21.0), (19.0, 9.5)], 1.2, K); - } - I::Save => { - p.rshape(&[(3.0, 3.0), (17.5, 3.0), (21.0, 6.5), (21.0, 21.0), (3.0, 21.0)], 1.5, t(B), B); - p.rect(7.0, 3.0, 15.0, 8.5, 0.6, N, B); - p.seg((12.5, 4.5), (12.5, 7.0), B); - p.rect(6.5, 13.0, 17.5, 21.0, 0.8, N, B); - } - I::Undo | I::Redo => { - let mut pts = vec![(6.0, 8.5), (14.0, 8.5)]; - pts.extend(arc_pts((14.0, 14.0), 5.5, 5.5, -90.0, 90.0)); - pts.push((8.5, 19.5)); - let hs = p.head_size(); - let pts: Vec = if icon == I::Undo { pts } else { pts.into_iter().map(|(x, y)| (24.0 - x, y)).collect() }; - p.line(&pts, K); - if icon == I::Undo { - p.head((3.0, 8.5), 180.0, hs, K); - } else { - p.head((21.0, 8.5), 0.0, hs, K); - } - } - I::More => { - for x in [5.0, 12.0, 19.0] { - p.dot((x, 12.0), 1.9, K); - } - } - I::Search => p.magnifier((10.0, 10.0), 6.5, N, K), - I::Share => { - p.seg((6.0, 12.0), (18.0, 5.5), K); - p.seg((6.0, 12.0), (18.0, 18.5), K); - p.dot((18.0, 5.5), 3.0, B); - p.dot((6.0, 12.0), 3.0, B); - p.dot((18.0, 18.5), 3.0, B); - } - I::CommentsBubble => p.bubble(2.5, 3.5, 21.5, 17.0, N, K), - I::File => { - p.page(4.5, 2.0, 19.5, 22.0, N, K); - p.seg((8.0, 12.5), (16.0, 12.5), B); - p.seg((8.0, 16.5), (14.0, 16.5), B); - } - I::Folder => { - p.rshape(&[(2.5, 4.5), (9.0, 4.5), (11.0, 7.0), (21.5, 7.0), (21.5, 19.5), (2.5, 19.5)], 1.2, t(Y), K); - p.rshape(&[(2.5, 10.0), (21.5, 10.0), (21.5, 19.5), (2.5, 19.5)], 1.2, Y.a(0.6), K); - } - I::Export => { - p.rline(&[(15.0, 8.5), (15.0, 2.5), (3.0, 2.5), (3.0, 21.5), (15.0, 21.5), (15.0, 15.5)], 1.5, K); - p.arrow((8.5, 12.0), (22.0, 12.0), B); - } - I::Print => { - p.rect(6.5, 2.5, 17.5, 9.0, 0.8, N, K); - p.rline(&[(6.5, 17.5), (2.5, 17.5), (2.5, 9.0), (21.5, 9.0), (21.5, 17.5), (17.5, 17.5)], 1.8, K); - p.rect(6.5, 14.0, 17.5, 21.5, 0.8, N, K); - p.dot((18.5, 12.0), 1.1, G); - } - I::Settings => { - p.gear((12.0, 12.0), 9.8, 7.3, 8, N, K); - p.circle((12.0, 12.0), 3.0, N, K); - } - I::Help => { - p.circle((12.0, 12.0), 9.8, N, K); - let mut q = arc_pts((12.0, 9.4), 3.2, 3.0, 180.0, 400.0); - q.push((12.0, 13.0)); - q.push((12.0, 14.6)); - p.line(&q, B); - p.dot((12.0, 17.8), 1.25, B); - } - I::Info => { - p.circle((12.0, 12.0), 9.8, N, K); - p.dot((12.0, 7.3), 1.3, B); - p.seg((12.0, 10.8), (12.0, 17.5), B); - } - I::Warning => { - p.rshape(&[(12.0, 2.5), (22.5, 20.5), (1.5, 20.5)], 2.0, Y, N); - p.seg((12.0, 9.0), (12.0, 14.0), D); - p.dot((12.0, 17.2), 1.2, D); - } - // Selection pane toggles: monochrome outlines in the ink colour. - I::Lock => { - let mut sh = vec![(7.5, 10.5), (7.5, 8.0)]; - sh.extend(arc_pts((12.0, 8.0), 4.5, 4.5, 180.0, 360.0)); - sh.push((16.5, 10.5)); - p.line(&sh, K); - p.rect(4.5, 10.5, 19.5, 21.5, 1.8, N, K); - p.seg((12.0, 14.5), (12.0, 17.5), K); - } - I::Unlock => { - // The shackle swung open: its left leg out of the body, raised. - let mut sh = vec![(12.0, 10.5), (12.0, 6.0)]; - sh.extend(arc_pts((16.5, 6.0), 4.5, 4.5, 180.0, 360.0)); - sh.push((21.0, 8.5)); - p.line(&sh, K); - p.rect(3.0, 10.5, 16.5, 21.5, 1.8, N, K); - p.seg((9.75, 14.5), (9.75, 17.5), K); - } - I::Eye | I::EyeOff => { - let mut e = quad_pts((2.0, 12.0), (12.0, 4.0), (22.0, 12.0), 16); - e.extend(quad_pts((22.0, 12.0), (12.0, 20.0), (2.0, 12.0), 16).into_iter().skip(1)); - e.pop(); - p.shape(&e, N, K); - p.circle((12.0, 12.0), 3.2, N, K); - if icon == I::EyeOff { - p.seg_w((4.0, 21.0), (20.0, 3.0), K, 1.1); - } - } - I::Close => { - p.seg_w((5.5, 5.5), (18.5, 18.5), K, 1.1); - p.seg_w((18.5, 5.5), (5.5, 18.5), K, 1.1); - } - I::Check => p.line_w(&[(4.0, 12.5), (9.5, 18.0), (20.0, 6.5)], G, 1.3), - I::Plus => { - p.seg((12.0, 4.5), (12.0, 19.5), K); - p.seg((4.5, 12.0), (19.5, 12.0), K); - } - I::Minus => p.seg((4.5, 12.0), (19.5, 12.0), K), - I::ChevronDown => p.line(&[(6.0, 9.0), (12.0, 15.0), (18.0, 9.0)], K), - I::ChevronUp => p.line(&[(6.0, 15.0), (12.0, 9.0), (18.0, 15.0)], K), - I::ChevronLeft => p.line(&[(15.0, 6.0), (9.0, 12.0), (15.0, 18.0)], K), - I::ChevronRight => p.line(&[(9.0, 6.0), (15.0, 12.0), (9.0, 18.0)], K), - I::DragHandle => { - for x in [9.0, 15.0] { - for y in [6.0, 12.0, 18.0] { - p.dot((x, y), 1.6, K); - } - } - } - I::Star => p.star((12.0, 12.8), 10.0, t(Y), Y), - I::Image => { - p.rect(2.5, 4.0, 21.5, 20.0, 1.5, N, K); - p.circle((16.0, 9.0), 1.8, N, K); - p.line(&[(2.5, 17.5), (8.5, 11.0), (14.0, 16.5), (16.5, 14.0), (21.5, 18.5)], K); - } - I::Sparkle => { - p.sparkle((10.5, 13.0), 9.0, PU); - p.sparkle((19.0, 5.0), 3.8, Y); - } - I::Palette => { - let mut pts = arc_pts((12.0, 12.0), 9.8, 9.5, 70.0, 380.0); - pts.extend(cubic_pts((21.2, 15.25), (19.0, 15.0), (14.5, 14.0), (15.5, 21.0), 10)); - p.shape(&pts, N, K); - p.dot((7.5, 9.5), 1.7, R); - p.dot((11.5, 6.3), 1.7, Y); - p.dot((16.3, 7.8), 1.7, G); - p.dot((7.2, 14.8), 1.7, B); - } - I::Grid => { - for (x, y) in [(3.0, 3.0), (13.0, 3.0), (3.0, 13.0), (13.0, 13.0)] { - p.rect(x, y, x + 8.0, y + 8.0, 1.5, N, K); - } - } - I::List => { - for y in [5.5, 12.0, 18.5] { - p.dot((4.5, y), 1.5, K); - p.seg((9.0, y), (21.0, y), K); - } - } - I::Collapse => { - p.line(&[(7.0, 3.0), (12.0, 8.0), (17.0, 3.0)], K); - p.line(&[(7.0, 21.0), (12.0, 16.0), (17.0, 21.0)], K); - p.seg_w((5.0, 12.0), (19.0, 12.0), K.a(0.5), 0.8); - } - I::Expand => { - p.line(&[(7.0, 8.0), (12.0, 3.0), (17.0, 8.0)], K); - p.line(&[(7.0, 16.0), (12.0, 21.0), (17.0, 16.0)], K); - p.seg_w((5.0, 12.0), (19.0, 12.0), K.a(0.5), 0.8); - } - I::Pin => { - let m = |pts: &[P2]| tf((12.0, 12.0), 35.0, pts); - p.line(&m(&[(0.0, 2.0), (0.0, 10.5)]), K); - p.rshape(&m(&[(-6.0, 0.0), (6.0, 0.0), (6.0, 2.5), (-6.0, 2.5)]), 0.6, R, N); - p.poly(&m(&[(-3.0, -7.0), (3.0, -7.0), (3.6, 0.0), (-3.6, 0.0)]), R, N); - p.rshape(&m(&[(-4.2, -10.0), (4.2, -10.0), (4.2, -7.0), (-4.2, -7.0)]), 0.6, R, N); - } - - // ----- File tab (Backstage) navigation: outlines in the ink colour ----- - I::BackCircle => { - p.circle((12.0, 12.0), 9.8, N, K); - p.arrow((17.0, 12.0), (6.5, 12.0), K); - } - I::SaveAs => { - p.rline(&[(11.0, 20.5), (2.5, 20.5), (2.5, 3.0), (15.0, 3.0), (18.0, 6.0), (18.0, 10.5)], 1.2, K); - p.rect(6.0, 3.0, 13.0, 7.5, 0.6, N, K); - p.seg_w((13.5, 21.0), (21.5, 13.0), O, 1.4); - p.seg_w((12.5, 22.0), (13.5, 21.0), K, 1.0); - } - I::Account => { - p.circle((12.0, 8.0), 4.5, N, K); - p.line(&arc_pts((12.0, 22.0), 8.5, 7.5, 180.0, 360.0), K); - } - I::Recent => { - p.circle((12.0, 12.0), 9.8, N, K); - p.line(&[(12.0, 6.0), (12.0, 12.0), (16.0, 14.5)], K); - } - I::NewFile => { - p.rline(&[(14.0, 2.5), (5.0, 2.5), (5.0, 21.5), (19.0, 21.5), (19.0, 7.5), (14.0, 2.5), (14.0, 7.5), (19.0, 7.5)], 0.8, K); - } - I::OpenFolder => { - p.rline(&[(18.5, 9.0), (18.5, 6.5), (10.0, 6.5), (8.0, 4.0), (2.5, 4.0), (2.5, 19.5), (18.0, 19.5)], 1.0, K); - p.rline(&[(2.5, 19.5), (6.0, 10.0), (22.0, 10.0), (18.0, 19.5)], 1.0, K); - } - - // ----- Status bar (monochrome) ----- - I::NotesSmall => { - p.rect(3.0, 4.0, 21.0, 20.0, 1.5, N, K); - p.seg((3.0, 13.5), (21.0, 13.5), K); - p.seg((6.5, 16.8), (15.5, 16.8), K); - } - I::CommentsSmall => p.bubble(3.0, 4.0, 21.0, 16.5, N, K), - I::ViewNormalSmall => { - p.rect(2.5, 4.0, 21.5, 20.0, 1.5, N, K); - p.seg((8.5, 4.0), (8.5, 20.0), K); - } - I::ViewSorterSmall => { - for (x, y) in [(2.5, 4.5), (13.0, 4.5), (2.5, 13.5), (13.0, 13.5)] { - p.rect(x, y, x + 8.5, y + 6.0, 1.0, N, K); - } - } - I::ViewReadingSmall => { - p.book(N, K); - p.seg((12.0, 7.0), (12.0, 20.5), K); - } - I::ViewShowSmall => { - p.seg((2.0, 4.0), (22.0, 4.0), K); - p.rect(3.5, 4.0, 20.5, 16.0, 0.8, N, K); - p.seg((12.0, 16.0), (12.0, 19.0), K); - p.line(&[(8.5, 22.0), (12.0, 19.0), (15.5, 22.0)], K); - } - I::FitSmall => { - for (cx, cy, sx, sy) in [(3.0, 3.0, 1.0, 1.0), (21.0, 3.0, -1.0, 1.0), (3.0, 21.0, 1.0, -1.0), (21.0, 21.0, -1.0, -1.0)] { - p.rline(&[(cx, cy + 5.0 * sy), (cx, cy), (cx + 5.0 * sx, cy)], 0.8, K); - } - p.rect(8.0, 8.5, 16.0, 15.5, 0.8, N, K); - } - I::ZoomIn | I::ZoomOut => { - p.magnifier((10.0, 10.0), 7.0, N, K); - p.seg((7.0, 10.0), (13.0, 10.0), K); - if icon == I::ZoomIn { - p.seg((10.0, 7.0), (10.0, 13.0), K); - } - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/ui-egui/src/icons/draw.rs b/crates/ui-egui/src/icons/draw.rs new file mode 100644 index 00000000..19a0a584 --- /dev/null +++ b/crates/ui-egui/src/icons/draw.rs @@ -0,0 +1,1341 @@ +use super::geometry::{B, C, D, G, Glyph, K, N, O, P2, PU, Pen, R, W, Y, arc_pts, cubic_pts, quad_pts, rad, round_pts, rrect_pts, t, tf}; +use super::*; + +// --------------------------------------------------------------------------------------------- +// The icons +// --------------------------------------------------------------------------------------------- + +#[expect(clippy::too_many_lines)] +pub(super) fn draw(p: &Pen<'_>, icon: Icon) { + use Icon as I; + match icon { + // ----- Clipboard & slides ----- + I::Paste => { + p.rline(&[(10.5, 21.5), (3.5, 21.5), (3.5, 4.5), (17.0, 4.5), (17.0, 9.0)], 2.0, K); + p.rect(7.0, 2.5, 13.5, 6.5, 1.2, Y, K); + p.rect(10.5, 10.0, 21.0, 22.0, 1.2, t(B), B); + p.seg((13.0, 14.0), (18.5, 14.0), B); + p.seg((13.0, 18.0), (17.0, 18.0), B); + } + I::Cut => { + p.seg((8.6, 14.6), (16.5, 2.5), K); + p.seg((15.4, 14.6), (7.5, 2.5), K); + p.circle((6.8, 17.6), 3.3, N, O); + p.circle((17.2, 17.6), 3.3, N, O); + p.dot((12.0, 9.4), 1.0, K); + } + I::Copy => { + p.rline(&[(8.0, 16.0), (3.0, 16.0), (3.0, 2.5), (14.0, 2.5), (14.0, 7.0)], 1.5, K); + p.rect(8.0, 7.5, 20.5, 21.5, 1.5, t(B), B); + p.seg((11.0, 12.5), (17.5, 12.5), B); + p.seg((11.0, 16.5), (16.0, 16.5), B); + } + I::FormatPainter => { + p.rect(2.5, 3.0, 17.0, 9.0, 1.5, Y, K); + p.rline(&[(17.0, 6.0), (20.5, 6.0), (20.5, 11.5), (11.0, 11.5), (11.0, 14.0)], 1.2, K); + p.rect(9.0, 14.0, 13.0, 21.5, 1.0, K, K); + } + I::NewSlide => { + p.frame(2.0, 4.0, 19.5, 16.5); + p.seg((5.0, 8.0), (13.0, 8.0), B); + p.seg((5.0, 12.0), (10.0, 12.0), K.a(0.5)); + p.badge((18.0, 17.5), 5.0, G, Glyph::Plus); + } + I::Layout => { + p.frame(2.0, 3.5, 22.0, 20.5); + p.rect(5.0, 6.5, 19.0, 9.0, 0.8, B, N); + p.rect(5.0, 11.5, 11.0, 17.5, 0.8, t(G), G); + p.rect(13.0, 11.5, 19.0, 17.5, 0.8, t(O), O); + } + I::ResetSlide => { + p.frame(2.0, 3.5, 22.0, 20.5); + p.arc_arrow((12.0, 12.0), 4.6, -20.0, -290.0, B); + } + I::Section => { + p.rshape(&[(2.5, 2.5), (13.0, 2.5), (16.5, 5.75), (13.0, 9.0), (2.5, 9.0)], 0.6, PU, N); + p.rline(&[(5.0, 9.0), (5.0, 16.0), (7.5, 16.0)], 1.0, K); + p.rect(7.5, 12.0, 21.5, 20.5, 1.2, N, K); + p.seg((10.0, 15.5), (16.0, 15.5), K.a(0.5)); + } + I::DuplicateSlide => { + p.rline(&[(6.5, 15.5), (2.5, 15.5), (2.5, 3.5), (16.5, 3.5), (16.5, 8.0)], 1.5, K); + p.rect(6.5, 8.0, 21.5, 20.5, 1.5, t(B), B); + p.seg((14.0, 11.5), (14.0, 17.0), B); + p.seg((11.25, 14.25), (16.75, 14.25), B); + } + I::DeleteSlide => { + p.frame(2.0, 4.0, 19.5, 16.5); + p.seg((5.0, 8.0), (13.0, 8.0), B); + p.seg((5.0, 12.0), (10.0, 12.0), K.a(0.5)); + p.badge((18.0, 17.5), 5.0, R, Glyph::Cross); + } + I::HideSlide => { + p.rect(2.0, 4.0, 22.0, 20.0, 1.5, N, K.a(0.55)); + p.seg((5.0, 8.0), (13.0, 8.0), K.a(0.55)); + p.seg((3.0, 21.0), (21.0, 3.0), R); + } + I::Slide => { + p.frame(2.0, 4.5, 22.0, 19.5); + p.seg((5.0, 8.5), (15.0, 8.5), B); + p.rect(5.0, 11.5, 19.0, 16.5, 0.8, t(B), N); + } + + // ----- Font ----- + I::FontGrow => { + p.letter_a(2.5, 4.0, 15.0, 20.5, K, 1.15); + p.poly(&[(16.0, 10.5), (22.5, 10.5), (19.25, 5.0)], B, N); + } + I::FontShrink => { + p.letter_a(3.0, 8.0, 13.5, 20.5, K, 1.0); + p.poly(&[(16.0, 5.0), (22.5, 5.0), (19.25, 10.5)], B, N); + } + I::ClearFormatting => { + p.letter_a(2.5, 3.0, 14.0, 17.5, K, 1.1); + let m = |pts: &[P2]| tf((17.0, 16.5), -45.0, pts); + p.poly(&m(&[(-5.5, -2.8), (0.0, -2.8), (0.0, 2.8), (-5.5, 2.8)]), R, R); + p.poly(&m(&[(0.0, -2.8), (5.5, -2.8), (5.5, 2.8), (0.0, 2.8)]), N, K); + } + I::Bold => p.text_bold("B", (12.0, 12.0), 22.0, K), + I::Italic => { + p.seg_w((10.5, 4.0), (18.5, 4.0), K, 1.1); + p.seg_w((5.5, 20.0), (13.5, 20.0), K, 1.1); + p.seg_w((14.5, 4.0), (9.5, 20.0), K, 1.1); + } + I::Underline => { + let mut pts = vec![(6.5, 3.0)]; + pts.extend(arc_pts((12.0, 11.5), 5.5, 5.5, 180.0, 0.0)); + pts.push((17.5, 3.0)); + p.line_w(&pts, K, 1.1); + p.seg_w((4.5, 21.0), (19.5, 21.0), B, 1.2); + } + I::Strikethrough => { + p.text("ab", (12.0, 11.0), 19.0, K); + p.seg_w((2.5, 13.0), (21.5, 13.0), R, 1.1); + } + I::Superscript => { + p.seg((3.0, 9.0), (13.0, 21.0), K); + p.seg((13.0, 9.0), (3.0, 21.0), K); + p.digit(2, 16.0, 2.5, 6.5, B); + } + I::Subscript => { + p.seg((3.0, 3.5), (13.0, 15.5), K); + p.seg((13.0, 3.5), (3.0, 15.5), K); + p.digit(2, 16.0, 15.0, 6.5, B); + } + I::CharSpacing => { + p.letter_a(2.0, 3.0, 10.5, 15.0, K, 1.0); + p.line(&[(13.5, 3.0), (17.75, 15.0), (22.0, 3.0)], K); + p.arrow2((2.5, 20.0), (21.5, 20.0), B); + } + I::ChangeCase => { + p.text("A", (8.0, 11.0), 19.0, K); + p.text("a", (17.5, 12.5), 16.0, B); + p.seg((3.0, 21.0), (21.0, 21.0), K.a(0.4)); + } + I::Highlight => { + let m = |pts: &[P2]| tf((8.0, 15.0), -45.0, pts); + p.poly(&m(&[(0.0, -1.2), (3.0, -3.0), (3.0, 3.0), (0.0, 1.4)]), Y, K); + p.poly(&m(&[(3.0, -3.0), (13.5, -3.0), (13.5, 3.0), (3.0, 3.0)]), N, K); + p.rect(2.5, 19.5, 21.5, 22.5, 0.8, Y, N); + } + I::FontColor => { + p.letter_a(4.5, 2.5, 19.5, 17.0, K, 1.1); + p.rect(2.5, 19.5, 21.5, 22.5, 0.8, R, N); + } + I::TextShadow => { + p.text("S", (14.0, 13.8), 22.0, K.a(0.3)); + p.text_bold("S", (11.0, 11.0), 22.0, K); + } + + // ----- Paragraph ----- + I::Bullets => { + for y in [5.5, 12.0, 18.5] { + p.dot((4.5, y), 1.8, B); + p.seg((9.0, y), (21.0, y), K); + } + } + I::Numbering => { + for (i, y) in [5.5_f32, 12.0, 18.5].into_iter().enumerate() { + p.digit(i as u8 + 1, 2.8, y - 2.6, 5.2, B); + p.seg((9.5, y), (21.0, y), K); + } + } + I::DecreaseIndent | I::IncreaseIndent => { + p.seg((3.0, 4.0), (21.0, 4.0), K); + p.seg((11.0, 9.5), (21.0, 9.5), K); + p.seg((11.0, 14.5), (21.0, 14.5), K); + p.seg((3.0, 20.0), (21.0, 20.0), K); + if icon == I::DecreaseIndent { + p.poly(&[(8.0, 8.0), (8.0, 16.0), (3.0, 12.0)], B, N); + } else { + p.poly(&[(3.0, 8.0), (3.0, 16.0), (8.0, 12.0)], B, N); + } + } + I::LineSpacing => { + for y in [5.0, 12.0, 19.0] { + p.seg((11.0, y), (21.0, y), K); + } + p.arrow2((5.0, 2.5), (5.0, 21.5), B); + } + I::Columns => { + for y in [4.5, 9.5, 14.5, 19.5] { + p.seg((2.5, y), (10.5, y), K); + p.seg((13.5, y), (21.5, y), K); + } + } + I::AlignLeft | I::AlignCenter | I::AlignRight | I::Justify => { + let rows: [(f32, f32); 4] = match icon { + I::AlignLeft => [(3.0, 21.0), (3.0, 15.0), (3.0, 21.0), (3.0, 13.0)], + I::AlignCenter => [(3.0, 21.0), (7.0, 17.0), (3.0, 21.0), (8.0, 16.0)], + I::AlignRight => [(3.0, 21.0), (9.0, 21.0), (3.0, 21.0), (11.0, 21.0)], + _ => [(3.0, 21.0), (3.0, 21.0), (3.0, 21.0), (3.0, 14.0)], + }; + for ((a, b), y) in rows.into_iter().zip([4.5, 9.5, 14.5, 19.5]) { + p.seg((a, y), (b, y), K); + } + } + I::TextDirection => { + // A capital A lying on its side. + p.line_w(&[(17.0, 5.5), (3.0, 12.0), (17.0, 18.5)], K, 1.1); + p.seg_w((11.7, 8.3), (11.7, 15.7), K, 1.1); + p.arrow((21.0, 3.5), (21.0, 20.5), B); + } + I::AlignText => { + p.rect(2.5, 3.0, 15.0, 21.0, 1.2, N, K); + p.seg((5.5, 10.0), (12.0, 10.0), K); + p.seg((5.5, 14.0), (10.5, 14.0), K); + p.arrow((19.5, 2.5), (19.5, 9.5), B); + p.arrow((19.5, 21.5), (19.5, 14.5), B); + } + I::ConvertSmartArt => { + for (y, x1) in [(4.0, 11.0), (8.0, 9.0), (12.0, 11.0)] { + p.seg((2.5, y), (x1, y), K); + } + p.path_arrow(&[(14.0, 6.0), (19.0, 6.0), (19.0, 12.5)], B); + p.rect(2.5, 15.5, 7.5, 21.5, 1.0, B, N); + p.rect(9.5, 15.5, 14.5, 21.5, 1.0, O, N); + p.rect(16.5, 15.5, 21.5, 21.5, 1.0, G, N); + } + + // ----- Drawing ----- + I::Shapes => { + p.rect(11.0, 2.5, 21.5, 13.0, 1.0, t(G), G); + p.rshape(&[(15.25, 11.0), (21.5, 21.5), (9.0, 21.5)], 0.8, t(O), O); + p.circle((8.0, 9.0), 5.5, t(B), B); + } + I::Arrange => { + p.rect(2.5, 2.5, 11.5, 11.5, 1.0, N, K); + p.rect(7.5, 7.5, 16.5, 16.5, 1.0, t(B), B); + p.rect(12.5, 12.5, 21.5, 21.5, 1.0, B, B); + } + I::QuickStyles => { + p.rect(3.0, 3.0, 11.0, 11.0, 1.5, B, N); + p.rect(13.0, 3.0, 21.0, 11.0, 1.5, N, O); + p.rect(3.0, 13.0, 11.0, 21.0, 1.5, t(G), G); + p.rect(13.0, 13.0, 21.0, 21.0, 1.5, PU, N); + } + I::ShapeFill => { + let m = |pts: &[P2]| tf((10.0, 10.0), 45.0, pts); + p.poly(&m(&[(-5.0, 0.0), (5.0, 0.0), (5.0, 5.0), (-5.0, 5.0)]), B, N); + p.rshape(&m(&[(-5.0, -5.0), (5.0, -5.0), (5.0, 5.0), (-5.0, 5.0)]), 0.8, N, K); + p.line(&quad_pts((5.2, 6.4), (5.5, 1.0), (10.0, 3.0), 10), K); + p.poly(&[(18.5, 8.5), (20.6, 12.6), (16.4, 12.6)], B, N); + p.dot((18.5, 13.4), 2.1, B); + p.rect(2.5, 19.5, 21.5, 22.5, 0.8, B, N); + } + I::ShapeOutline => { + p.tool((4.5, 15.5), -45.0, 17.0, 2.4, 4.2, N, K, 0.0, N); + p.rect(2.5, 19.5, 21.5, 22.5, 0.8, O, N); + } + I::ShapeEffects => { + p.rect(6.0, 8.0, 18.5, 20.5, 2.0, K.a(0.25), N); + p.rect(3.0, 5.0, 15.5, 17.5, 2.0, B, N); + p.sparkle((19.0, 5.0), 4.0, Y); + } + I::TextFill => { + p.letter_a(4.5, 2.5, 19.5, 17.0, B, 1.5); + p.rect(2.5, 19.5, 21.5, 22.5, 0.8, B, N); + } + I::TextOutline => { + let outer = [(3.5, 17.0), (9.6, 2.5), (14.4, 2.5), (20.5, 17.0), (16.6, 17.0), (15.2, 13.2), (8.8, 13.2), (7.4, 17.0)]; + p.shape_w(&outer, N, K, 0.8); + p.poly_w(&[(10.0, 10.0), (12.0, 5.0), (14.0, 10.0)], N, K, 0.8); + p.rect(2.5, 19.5, 21.5, 22.5, 0.8, O, N); + } + I::TextEffects => { + p.letter_a(3.0, 5.0, 17.0, 21.0, PU, 1.3); + p.sparkle((19.0, 5.0), 4.5, Y); + } + I::EditShape => { + let a = (4.0, 19.0); + let b = (11.0, 4.5); + let c = (20.0, 17.5); + let mut pts = vec![a, b]; + pts.extend(cubic_pts(b, (17.0, 4.0), (23.0, 11.0), c, 16)); + pts.extend(cubic_pts(c, (15.0, 22.0), (9.0, 17.0), a, 16)); + p.shape(&pts, t(B), K); + for q in [a, b, c] { + p.rect(q.0 - 1.6, q.1 - 1.6, q.0 + 1.6, q.1 + 1.6, 0.3, B, N); + } + } + I::MergeShapes => { + let mut lens = arc_pts((9.0, 12.0), 6.5, 6.5, -62.5, 62.5); + lens.extend(arc_pts((15.0, 12.0), 6.5, 6.5, 117.5, 242.5)); + p.poly(&lens, PU, N); + p.circle((9.0, 12.0), 6.5, N, K); + p.circle((15.0, 12.0), 6.5, N, K); + } + + // ----- Insert ----- + I::Table => p.table(2.5, 4.0, 21.5, 20.0, 3, 3, B), + I::Picture => p.picture(2.0, 4.0, 22.0, 20.0), + I::Screenshot => { + for (cx, cy, sx, sy) in [(2.5, 3.5, 1.0, 1.0), (21.5, 3.5, -1.0, 1.0), (2.5, 20.5, 1.0, -1.0), (21.5, 20.5, -1.0, -1.0)] { + p.rline(&[(cx, cy + 4.0 * sy), (cx, cy), (cx + 4.0 * sx, cy)], 1.0, K); + } + p.rect(6.5, 7.5, 17.5, 16.5, 1.0, t(B), B); + p.seg((6.5, 10.0), (17.5, 10.0), B); + } + I::Icons => { + p.person(6.75, 2.5, 0.75, B); + p.star((17.0, 7.0), 5.0, Y, N); + let heart: Vec = (0..40) + .map(|i| { + let tt = i as f32 / 40.0 * 2.0 * PI; + let x = 16.0 * tt.sin().powi(3); + let y = 13.0 * tt.cos() - 5.0 * (2.0 * tt).cos() - 2.0 * (3.0 * tt).cos() - (4.0 * tt).cos(); + (7.0 + x * 0.29, 17.0 - y * 0.29) + }) + .collect(); + p.shape(&heart, R, N); + p.rshape(&[(17.0, 12.5), (21.5, 17.0), (17.0, 21.5), (12.5, 17.0)], 0.8, G, N); + } + I::Models3d => { + let (top, ur, lr, bot, ll, ul, mid) = ((12.0, 2.5), (20.5, 7.2), (20.5, 16.8), (12.0, 21.5), (3.5, 16.8), (3.5, 7.2), (12.0, 11.9)); + p.poly(&[top, ur, mid, ul], t(B), N); + p.poly(&[ul, mid, bot, ll], B.a(0.55), N); + p.poly(&[mid, ur, lr, bot], B, N); + p.shape_w(&[top, ur, lr, bot, ll, ul], N, K, 1.0); + p.line(&[ul, mid, ur], K); + p.seg(mid, bot, K); + } + I::SmartArt => { + p.rect(8.5, 2.5, 15.5, 8.5, 1.0, G, N); + p.seg((12.0, 8.5), (12.0, 11.5), K); + p.rline(&[(5.5, 14.5), (5.5, 11.5), (18.5, 11.5), (18.5, 14.5)], 1.0, K); + p.rect(2.0, 14.5, 9.0, 20.5, 1.0, B, N); + p.rect(15.0, 14.5, 22.0, 20.5, 1.0, O, N); + } + I::Chart => p.bars(2.5, 4.0, 21.5, 20.5), + I::Zoom => { + p.rline(&[(10.5, 15.0), (2.0, 15.0), (2.0, 3.0), (18.0, 3.0), (18.0, 9.5)], 1.5, K); + p.seg((5.0, 6.5), (11.0, 6.5), K.a(0.5)); + p.magnifier((14.5, 14.5), 4.5, t(B), B); + } + I::Link => { + let link = |c: P2, col: C| { + let local = rrect_pts(-5.5, -2.8, 5.5, 2.8, 2.8); + p.shape(&tf(c, -45.0, &local), N, col); + }; + link((8.5, 15.5), K); + link((15.5, 8.5), B); + } + I::Action => { + p.rect(2.5, 3.5, 18.0, 12.5, 2.0, t(B), B); + p.cursor(11.5, 8.5, 0.82); + } + I::Comment => { + p.bubble(2.5, 3.5, 21.5, 16.5, t(Y), K); + p.seg((6.5, 8.0), (17.5, 8.0), K); + p.seg((6.5, 12.0), (14.0, 12.0), K); + } + I::TextBox => { + p.rect(3.5, 4.5, 20.5, 19.5, 0.5, N, K); + for (x, y) in [(3.5, 4.5), (20.5, 4.5), (3.5, 19.5), (20.5, 19.5)] { + p.rect(x - 1.5, y - 1.5, x + 1.5, y + 1.5, 0.3, B, N); + } + p.seg((7.5, 9.5), (16.5, 9.5), K); + p.seg((7.5, 14.5), (13.5, 14.5), K); + } + I::HeaderFooter => { + p.rect(4.0, 2.5, 20.0, 21.5, 1.2, N, K); + p.rect(6.5, 5.0, 17.5, 7.5, 0.6, B, N); + p.rect(6.5, 16.5, 17.5, 19.0, 0.6, B, N); + p.seg_w((7.0, 10.5), (17.0, 10.5), K.a(0.45), 0.8); + p.seg_w((7.0, 13.2), (14.5, 13.2), K.a(0.45), 0.8); + } + I::WordArt => { + p.text_rot("A", (13.5, 13.5), 22.0, PU, -10.0); + p.text_rot("A", (11.5, 11.5), 22.0, O, -10.0); + p.text_rot("A", (11.3, 11.5), 22.0, O, -10.0); + } + I::DateTime => { + p.rect(3.0, 5.0, 21.0, 21.0, 1.6, N, K); + p.rect(3.0, 5.0, 21.0, 9.5, 1.6, R, N); + p.rect(3.0, 5.0, 21.0, 21.0, 1.6, N, K); + p.seg((8.0, 2.8), (8.0, 6.5), K); + p.seg((16.0, 2.8), (16.0, 6.5), K); + for (i, (x, y)) in [(8.0, 13.5), (12.0, 13.5), (16.0, 13.5), (8.0, 17.5), (12.0, 17.5), (16.0, 17.5)].into_iter().enumerate() { + p.dot((x, y), 1.1, if i == 4 { B } else { K }); + } + } + I::SlideNumber => { + p.frame(2.0, 4.0, 22.0, 20.0); + p.seg((10.7, 7.5), (9.7, 16.5), B); + p.seg((15.0, 7.5), (14.0, 16.5), B); + p.seg((7.5, 10.5), (17.0, 10.5), B); + p.seg((7.0, 13.6), (16.5, 13.6), B); + } + I::Object => { + p.page(3.0, 2.0, 16.0, 19.0, N, K); + p.rect(11.5, 11.5, 21.5, 21.5, 1.6, B, N); + p.rect(14.2, 14.2, 18.8, 18.8, 0.6, N, W); + } + I::Equation => { + p.line_w(&[(18.5, 6.5), (18.0, 4.0), (6.0, 4.0), (12.5, 12.0), (6.0, 20.0), (18.0, 20.0), (18.5, 17.5)], B, 1.15); + } + I::Symbol => p.text("Ω", (12.0, 12.0), 22.0, K), + I::Video => { + p.rect(2.0, 6.0, 15.5, 18.0, 2.0, t(B), K); + p.rshape(&[(16.5, 10.5), (22.0, 7.0), (22.0, 17.0), (16.5, 13.5)], 0.6, N, K); + p.dot((6.0, 10.0), 1.2, R); + } + I::Audio => { + p.speaker(2.5, 12.0, t(PU), K); + p.arc((11.0, 12.0), 4.5, -45.0, 45.0, PU); + p.arc((11.0, 12.0), 8.5, -45.0, 45.0, PU); + } + I::ScreenRecording => { + p.monitor(2.0, 3.0, 22.0, 16.5); + p.circle((12.0, 9.75), 3.0, R, N); + } + I::Cameo => { + p.frame(2.0, 4.0, 22.0, 20.0); + p.circle((12.0, 12.0), 5.6, t(B), B); + p.dot((12.0, 10.4), 1.9, B); + p.poly(&arc_pts((12.0, 16.0), 3.4, 2.6, 180.0, 360.0), B, N); + } + + // ----- Draw tab ----- + I::Pen => { + p.tool((3.5, 20.5), -45.0, 23.0, 2.2, 5.0, B, K, 3.5, K); + } + I::Pencil => { + p.tool((3.5, 20.5), -45.0, 22.0, 2.6, 5.0, Y, K, 3.5, R); + } + I::HighlighterPen => { + let m = |pts: &[P2]| tf((6.0, 15.0), -45.0, pts); + p.poly(&m(&[(0.0, -1.0), (3.0, -3.2), (3.0, 3.2), (0.0, 2.2)]), Y, K); + p.poly(&m(&[(3.0, -3.6), (14.5, -3.6), (14.5, 3.6), (3.0, 3.6)]), t(Y), K); + p.rect(2.0, 19.5, 13.0, 22.5, 0.8, Y, N); + } + I::Eraser => { + let m = |pts: &[P2]| tf((12.5, 11.0), -45.0, pts); + p.poly(&m(&[(-8.0, -4.0), (-1.0, -4.0), (-1.0, 4.0), (-8.0, 4.0)]), R, N); + p.rshape(&m(&[(-8.0, -4.0), (8.0, -4.0), (8.0, 4.0), (-8.0, 4.0)]), 1.0, N, K); + p.seg((12.0, 21.0), (21.0, 21.0), K); + } + I::LassoSelect => { + let mut e = arc_pts((13.0, 9.0), 8.5, 6.0, 120.0, 480.0); + e.truncate(e.len().saturating_sub(1)); + p.dashed(&e, B, 2.6, 1.7); + p.curve((8.8, 14.2), (5.5, 16.5), (10.0, 19.0), (5.5, 22.0), K); + p.dot((8.8, 14.2), 1.4, K); + } + I::InkToShape => { + p.scribble(1.5, 2.0, 12.5, 11.0, K); + p.circle((16.5, 16.5), 5.0, t(B), B); + } + I::InkToText => { + p.scribble(1.5, 2.0, 12.5, 11.0, K); + p.letter_a(12.0, 9.0, 21.5, 21.5, B, 1.2); + } + I::InkToMath => { + p.scribble(1.5, 2.0, 12.5, 11.0, K); + p.line_w(&[(21.0, 11.0), (12.5, 11.0), (17.0, 16.25), (12.5, 21.5), (21.0, 21.5)], B, 1.1); + } + I::Ruler => { + let m = |pts: &[P2]| tf((12.0, 12.0), -45.0, pts); + p.rshape(&m(&[(-11.5, -4.0), (11.5, -4.0), (11.5, 4.0), (-11.5, 4.0)]), 0.8, Y.a(0.85), K); + for (i, u) in [-8.0_f32, -5.0, -2.0, 1.0, 4.0, 7.0].into_iter().enumerate() { + let l = if i % 2 == 0 { 3.2 } else { 1.8 }; + p.line_w(&m(&[(u, -4.0), (u, -4.0 + l)]), D, 0.8); + } + } + + // ----- Design ----- + I::Themes => { + p.rect(9.5, 2.5, 21.5, 14.5, 2.0, O, N); + p.rect(2.5, 8.5, 15.0, 21.5, 2.0, B, N); + p.text("Aa", (8.75, 15.0), 10.0, W); + } + I::Variants => { + p.rect(8.0, 2.5, 21.5, 11.0, 1.2, t(PU), PU); + p.rect(5.0, 7.0, 18.5, 15.5, 1.2, t(G), G); + p.rect(2.5, 12.0, 16.0, 21.0, 1.2, B, B); + } + I::Colors => { + for (i, c) in [R, Y, G, B].into_iter().enumerate() { + let x = 3.0 + i as f32 * 4.5; + p.rect(x, 4.0, x + 4.5, 20.0, 0.0, c, N); + } + p.rect(3.0, 4.0, 21.0, 20.0, 0.0, N, K); + } + I::Fonts => { + p.text("A", (8.0, 11.0), 20.0, K); + p.text("g", (17.0, 10.0), 18.0, B); + } + I::BackgroundStyles => { + p.poly(&[(2.7, 4.7), (21.3, 4.7), (2.7, 19.3)], t(B), N); + p.poly(&[(21.3, 4.7), (21.3, 19.3), (2.7, 19.3)], PU.a(0.7), N); + p.frame(2.0, 4.0, 22.0, 20.0); + } + I::SlideSize => { + p.rline(&[(17.0, 12.0), (17.0, 19.0), (2.5, 19.0), (2.5, 7.0), (10.0, 7.0)], 1.5, K); + p.arrow((9.0, 13.0), (21.5, 2.5), B); + } + I::FormatBackground => { + p.rline(&[(10.0, 17.0), (2.0, 17.0), (2.0, 4.0), (20.0, 4.0), (20.0, 9.0)], 1.5, K); + p.rect(4.5, 6.5, 13.5, 12.5, 0.6, t(PU), N); + p.brush((12.0, 20.5), -45.0, 13.0, Y); + } + I::DesignIdeas => { + p.circle((12.0, 9.5), 6.0, Y.a(0.5), K); + p.seg((9.6, 15.7), (9.6, 17.0), K); + p.seg((14.4, 15.7), (14.4, 17.0), K); + p.seg((9.5, 18.0), (14.5, 18.0), K); + p.seg((10.5, 21.0), (13.5, 21.0), K); + for a in [-160.0_f32, -125.0, -55.0, -20.0] { + let (s, c) = rad(a).sin_cos(); + p.seg((12.0 + c * 8.0, 9.5 + s * 8.0), (12.0 + c * 10.0, 9.5 + s * 10.0), Y); + } + } + + // ----- Transitions / animations ----- + I::Preview => { + p.circle((12.0, 12.0), 9.5, N, K); + p.rshape(&[(9.5, 7.0), (17.0, 12.0), (9.5, 17.0)], 0.8, G, N); + } + I::EffectOptions => { + p.rect(2.5, 8.5, 15.5, 21.5, 1.5, t(B), K); + p.arrow((9.0, 15.0), (21.5, 2.5), B); + } + I::Sound => { + let head = |c: P2| { + let pts = tf(c, -22.0, &arc_pts((0.0, 0.0), 3.0, 2.1, 0.0, 351.0)); + p.poly(&pts, PU, N); + }; + head((6.3, 18.0)); + head((16.3, 15.5)); + p.seg((9.0, 17.0), (9.0, 5.0), PU); + p.seg((19.0, 14.5), (19.0, 2.5), PU); + p.poly(&[(9.0, 4.5), (19.0, 2.0), (19.0, 5.5), (9.0, 8.0)], PU, N); + } + I::Duration => { + p.circle((12.0, 13.5), 8.0, N, K); + p.rect(10.0, 2.0, 14.0, 4.0, 0.6, K, N); + p.seg((12.0, 4.0), (12.0, 5.5), K); + p.seg((12.0, 13.5), (15.8, 9.0), B); + p.dot((12.0, 13.5), 1.3, B); + } + I::ApplyToAll => { + p.rline(&[(6.0, 7.5), (6.0, 3.0), (21.5, 3.0), (21.5, 13.5), (17.5, 13.5)], 1.5, K); + p.rect(2.5, 7.5, 17.5, 18.0, 1.5, N, K); + p.badge((18.0, 18.0), 4.8, G, Glyph::Check); + } + I::AnimEntrance => { + p.star((13.5, 12.5), 8.5, G, N); + p.seg((1.5, 9.0), (4.5, 9.0), K); + p.seg((0.8, 13.5), (4.0, 13.5), K); + } + I::AnimEmphasis => { + p.star((12.0, 12.5), 9.5, Y, N); + } + I::AnimExit => { + p.star((10.5, 12.5), 8.5, R, N); + p.seg((19.5, 9.0), (22.5, 9.0), K); + p.seg((20.0, 13.5), (23.2, 13.5), K); + } + I::AnimPath => { + let pts = cubic_pts((4.5, 19.5), (3.0, 6.0), (20.0, 19.0), (19.5, 5.0), 60); + p.dotted(&pts[6..pts.len().saturating_sub(4)], K, 2.4, 0.55); + p.head((19.5, 4.0), -90.0, 4.0, K); + p.circle((4.5, 19.5), 2.4, G, N); + } + I::AnimationPane => { + p.rect(2.5, 3.0, 21.5, 21.0, 1.5, N, K); + p.seg((11.0, 3.0), (11.0, 21.0), K); + p.rect(4.5, 7.0, 8.5, 12.0, 0.5, t(B), N); + for (y, c) in [(7.5, G), (12.0, Y), (16.5, R)] { + p.seg_w((13.5, y), (19.0, y), c, 1.4); + } + } + I::Trigger => { + p.rshape(&[(14.5, 2.0), (5.0, 13.5), (11.5, 13.5), (9.5, 22.0), (19.0, 9.5), (12.5, 9.5)], 0.6, Y, K); + } + I::AnimationPainter => { + p.star((7.5, 7.5), 6.0, G, N); + p.brush((12.0, 12.0), 45.0, 13.0, Y); + } + I::AddAnimation => { + p.star((10.5, 11.0), 9.0, Y, N); + p.badge((18.5, 18.5), 4.8, G, Glyph::Plus); + } + + // ----- Slide show ----- + I::PlayFromStart => { + p.frame(2.0, 4.0, 22.0, 20.0); + p.rect(7.0, 8.0, 9.2, 16.0, 0.5, G, N); + p.rshape(&[(11.0, 8.0), (17.5, 12.0), (11.0, 16.0)], 0.6, G, N); + } + I::PlayFromCurrent => { + p.frame(2.0, 4.0, 22.0, 20.0); + p.rshape(&[(9.5, 8.0), (16.0, 12.0), (9.5, 16.0)], 0.6, G, N); + } + I::PresenterView => { + p.monitor(2.0, 3.0, 22.0, 16.5); + p.rect(4.5, 5.5, 13.0, 12.0, 0.6, t(B), B); + p.seg_w((15.0, 6.5), (19.5, 6.5), K, 0.8); + p.seg_w((15.0, 9.5), (19.5, 9.5), K, 0.8); + p.seg_w((15.0, 12.5), (18.0, 12.5), K, 0.8); + } + I::CustomShow => { + p.rect(2.5, 2.5, 12.5, 8.0, 1.0, N, K); + p.rect(2.5, 9.5, 12.5, 15.0, 1.0, O, O); + p.rect(2.5, 16.5, 12.5, 22.0, 1.0, N, K); + p.rshape(&[(15.5, 8.0), (21.5, 12.25), (15.5, 16.5)], 0.6, G, N); + } + I::SetUpShow => { + p.frame(2.0, 4.0, 19.5, 16.5); + p.seg((5.0, 8.0), (13.0, 8.0), K.a(0.5)); + p.gear_badge((17.5, 17.5), 5.5, B); + } + I::RehearseTimings => { + p.frame(2.0, 4.0, 19.5, 16.5); + p.seg((5.0, 8.0), (13.0, 8.0), K.a(0.5)); + p.clock_badge((17.5, 17.5), 5.2, B); + } + I::Record => { + p.circle((12.0, 12.0), 9.5, N, K); + p.circle((12.0, 12.0), 5.5, R, N); + } + I::Subtitles => { + p.rect(2.0, 4.5, 22.0, 19.5, 2.0, N, K); + p.seg((5.5, 12.0), (11.0, 12.0), B); + p.seg((13.5, 12.0), (18.5, 12.0), B); + p.seg((5.5, 16.0), (8.5, 16.0), B); + p.seg((11.0, 16.0), (18.5, 16.0), B); + } + I::Coach => { + p.person(9.0, 5.0, 1.2, t(B)); + p.circle((9.0, 8.1), 3.1, N, B); + p.line(&arc_pts((9.0, 19.4), 6.2, 6.0, 180.0, 360.0), B); + p.star((18.5, 6.0), 4.2, Y, N); + } + + // ----- Review ----- + I::Spelling => { + p.text("abc", (11.5, 7.0), 12.0, K); + p.line_w(&[(6.0, 16.0), (10.0, 20.0), (19.5, 11.0)], G, 1.3); + } + I::Thesaurus => { + p.rect(8.0, 2.5, 19.5, 21.5, 1.2, t(B), N); + p.rect(4.5, 2.5, 19.5, 21.5, 1.5, N, K); + p.seg((8.0, 2.5), (8.0, 21.5), K); + p.poly(&[(13.5, 2.5), (17.0, 2.5), (17.0, 9.0), (15.25, 7.5), (13.5, 9.0)], R, N); + } + I::Accessibility => { + p.circle((12.0, 12.0), 9.8, N, B); + p.dot((12.0, 6.5), 1.6, B); + p.seg((6.8, 9.6), (17.2, 9.6), B); + p.seg((12.0, 9.6), (12.0, 13.8), B); + p.line(&[(9.0, 18.5), (12.0, 13.8), (15.0, 18.5)], B); + } + I::Translate => { + p.rect(10.0, 9.0, 21.5, 20.5, 2.0, O, N); + p.rect(2.5, 3.5, 14.0, 15.0, 2.0, B, N); + p.letter_a(5.5, 5.8, 11.0, 12.8, W, 0.9); + p.seg_w((17.75, 10.8), (17.75, 11.9), W, 0.8); + p.seg_w((15.2, 12.9), (20.3, 12.9), W, 0.8); + p.seg_w((15.6, 14.4), (19.9, 18.9), W, 0.8); + p.seg_w((19.9, 14.4), (15.6, 18.9), W, 0.8); + } + I::Language => { + p.circle((12.0, 12.0), 9.5, N, B); + p.ellipse((12.0, 12.0), 4.2, 9.5, N, B); + p.seg_w((2.5, 12.0), (21.5, 12.0), B, 0.8); + p.seg_w((4.3, 7.0), (19.7, 7.0), B, 0.8); + p.seg_w((4.3, 17.0), (19.7, 17.0), B, 0.8); + } + I::NewComment => { + p.bubble(2.0, 3.0, 19.5, 15.0, N, K); + p.badge((18.0, 17.5), 5.0, G, Glyph::Plus); + } + I::DeleteComment => { + p.bubble(2.0, 3.0, 19.5, 15.0, N, K); + p.badge((18.0, 17.5), 5.0, R, Glyph::Cross); + } + I::PrevComment => { + p.bubble(2.5, 3.0, 21.5, 17.0, N, K); + p.arrow((16.5, 10.0), (7.0, 10.0), B); + } + I::NextComment => { + p.bubble(2.5, 3.0, 21.5, 17.0, N, K); + p.arrow((7.5, 10.0), (17.0, 10.0), B); + } + I::ShowComments => { + p.rline(&[(8.0, 7.5), (8.0, 2.5), (21.5, 2.5), (21.5, 13.0), (17.0, 13.0)], 2.0, K); + p.bubble(2.5, 7.5, 16.5, 17.5, t(Y), K); + p.seg((5.5, 11.0), (13.5, 11.0), K); + p.seg((5.5, 14.2), (11.0, 14.2), K); + } + I::Compare => { + p.rect(2.5, 2.5, 11.0, 15.5, 1.2, N, K); + p.rect(13.0, 8.5, 21.5, 21.5, 1.2, t(B), B); + p.arrow((13.0, 4.0), (21.5, 4.0), K); + p.arrow((11.0, 20.0), (2.5, 20.0), K); + } + I::ReadOnly => { + p.page(3.0, 2.0, 16.0, 20.5, N, K); + p.lock(13.0, 15.0, 21.5, 22.0); + } + + // ----- View ----- + I::ViewNormal => { + p.rect(2.0, 3.5, 22.0, 20.5, 1.5, N, K); + p.seg((8.0, 3.5), (8.0, 20.5), K); + for y in [6.0, 10.5, 15.0] { + p.rect(3.9, y, 6.2, y + 2.6, 0.3, K.a(0.55), N); + } + p.rect(10.0, 6.0, 20.0, 15.0, 0.6, t(B), B); + p.seg_w((10.0, 18.0), (17.0, 18.0), K.a(0.5), 0.8); + } + I::ViewOutline => { + p.rect(2.0, 3.5, 22.0, 20.5, 1.5, N, K); + p.seg((11.0, 3.5), (11.0, 20.5), K); + p.seg_w((4.0, 7.0), (9.0, 7.0), K, 0.8); + p.seg_w((5.5, 10.0), (9.0, 10.0), K, 0.8); + p.seg_w((5.5, 13.0), (9.0, 13.0), K, 0.8); + p.seg_w((4.0, 16.0), (9.0, 16.0), K, 0.8); + p.rect(13.0, 7.0, 20.0, 13.0, 0.6, t(B), B); + } + I::ViewSorter => { + for (i, (x0, x1)) in [(2.5, 11.0), (13.0, 21.5)].into_iter().enumerate() { + for (j, (y0, y1)) in [(4.0, 10.5), (13.5, 20.0)].into_iter().enumerate() { + if i == 0 && j == 0 { + p.rect(x0, y0, x1, y1, 0.8, t(B), B); + } else { + p.rect(x0, y0, x1, y1, 0.8, N, K); + } + } + } + } + I::ViewNotesPage => { + p.rect(5.0, 2.0, 19.0, 22.0, 1.2, N, K); + p.rect(7.5, 4.5, 16.5, 10.5, 0.6, t(B), B); + p.seg_w((7.5, 14.0), (16.5, 14.0), K, 0.8); + p.seg_w((7.5, 17.5), (14.0, 17.5), K, 0.8); + } + I::ViewReading => { + p.book(t(B), K); + p.seg((12.0, 7.0), (12.0, 20.5), K); + } + I::SlideMaster => { + p.rect(2.0, 2.5, 15.0, 10.5, 1.2, t(PU), PU); + p.seg((4.5, 5.5), (11.0, 5.5), PU); + p.rline(&[(5.0, 10.5), (5.0, 19.5), (9.0, 19.5)], 1.0, K); + p.seg((5.0, 14.0), (9.0, 14.0), K); + p.rect(9.0, 12.0, 17.0, 16.0, 0.8, N, K); + p.rect(9.0, 17.5, 17.0, 21.5, 0.8, N, K); + } + I::HandoutMaster => { + p.rect(4.0, 2.0, 20.0, 22.0, 1.2, N, K); + for (x, y) in [(6.5, 4.5), (12.75, 4.5), (6.5, 11.0), (12.75, 11.0)] { + p.rect(x, y, x + 4.75, y + 4.5, 0.4, t(PU), PU); + } + p.seg_w((6.5, 19.0), (17.5, 19.0), K, 0.8); + } + I::NotesMaster => { + p.rline(&[(8.0, 4.0), (8.0, 2.0), (21.0, 2.0), (21.0, 19.0), (16.0, 19.0)], 1.2, PU); + p.rect(3.0, 4.5, 16.0, 22.0, 1.2, N, K); + p.rect(5.5, 7.0, 13.5, 12.0, 0.5, t(PU), PU); + p.seg_w((5.5, 15.5), (13.5, 15.5), K, 0.8); + p.seg_w((5.5, 18.8), (11.5, 18.8), K, 0.8); + } + I::Gridlines => { + for v in [9.0, 15.0] { + p.seg_w((v, 3.0), (v, 21.0), K.a(0.6), 0.7); + p.seg_w((3.0, v), (21.0, v), K.a(0.6), 0.7); + } + p.rect(3.0, 3.0, 21.0, 21.0, 1.2, N, K); + } + I::Guides => { + p.rect(3.0, 3.0, 21.0, 21.0, 1.2, N, K); + p.dashed(&[(12.0, 1.5), (12.0, 22.5)], B, 2.2, 1.6); + p.dashed(&[(1.5, 12.0), (22.5, 12.0)], B, 2.2, 1.6); + } + I::Notes => { + p.rect(4.0, 4.0, 20.0, 21.5, 1.5, t(Y), K); + for x in [8.0, 12.0, 16.0] { + p.seg((x, 2.3), (x, 5.8), K); + } + p.seg_w((7.5, 10.5), (16.5, 10.5), K, 0.85); + p.seg_w((7.5, 14.0), (16.5, 14.0), K, 0.85); + p.seg_w((7.5, 17.5), (13.5, 17.5), K, 0.85); + } + I::ZoomGlass => p.magnifier((10.0, 10.0), 7.0, t(B), K), + I::FitToWindow => { + for (cx, cy, sx, sy) in [(2.5, 2.5, 1.0, 1.0), (21.5, 2.5, -1.0, 1.0), (2.5, 21.5, 1.0, -1.0), (21.5, 21.5, -1.0, -1.0)] { + p.rline(&[(cx, cy + 5.0 * sy), (cx, cy), (cx + 5.0 * sx, cy)], 0.8, K); + } + p.rect(7.0, 8.0, 17.0, 16.0, 1.0, t(B), B); + } + I::Grayscale => { + p.rect(3.0, 3.0, 9.0, 21.0, 0.0, K.a(0.85), N); + p.rect(9.0, 3.0, 15.0, 21.0, 0.0, K.a(0.45), N); + p.rect(15.0, 3.0, 21.0, 21.0, 0.0, K.a(0.15), N); + p.rect(3.0, 3.0, 21.0, 21.0, 1.2, N, K); + } + I::NewWindow => { + p.rect(2.0, 3.5, 20.0, 17.0, 1.5, N, K); + p.seg((2.0, 7.0), (20.0, 7.0), K); + p.dot((4.5, 5.25), 0.6, K); + p.badge((18.5, 18.0), 4.8, G, Glyph::Plus); + } + I::Macros => { + p.page(3.0, 2.0, 17.0, 21.0, N, K); + p.line(&[(8.5, 9.0), (6.0, 11.5), (8.5, 14.0)], B); + p.line(&[(11.5, 9.0), (14.0, 11.5), (11.5, 14.0)], B); + p.gear_badge((17.5, 17.5), 5.2, O); + } + + // ----- Arrange ----- + I::BringForward => { + p.layer(10.0, 15.5, 8.0, 4.0, N, K); + p.layer(10.0, 8.5, 8.0, 4.0, B, B); + p.arrow((21.0, 13.0), (21.0, 3.0), K); + } + I::SendBackward => { + p.layer(10.0, 15.5, 8.0, 4.0, B, B); + p.layer(10.0, 8.5, 8.0, 4.0, N, K); + p.arrow((21.0, 11.0), (21.0, 21.0), K); + } + I::BringToFront => { + p.layer(10.0, 18.5, 7.5, 3.2, N, K); + p.layer(10.0, 12.0, 7.5, 3.2, N, K); + p.layer(10.0, 5.5, 7.5, 3.2, B, B); + p.arrow((20.5, 14.0), (20.5, 4.5), K); + p.seg((18.0, 2.5), (23.0, 2.5), K); + } + I::SendToBack => { + p.layer(10.0, 18.5, 7.5, 3.2, B, B); + p.layer(10.0, 12.0, 7.5, 3.2, N, K); + p.layer(10.0, 5.5, 7.5, 3.2, N, K); + p.arrow((20.5, 10.0), (20.5, 19.5), K); + p.seg((18.0, 21.5), (23.0, 21.5), K); + } + I::Group => { + p.dashed(&[(2.5, 2.5), (21.5, 2.5), (21.5, 21.5), (2.5, 21.5), (2.5, 2.5)], B, 2.0, 1.6); + p.rect(5.5, 5.5, 13.0, 13.0, 0.8, t(G), G); + p.circle((15.5, 15.5), 4.3, t(O), O); + } + I::Ungroup => { + p.dashed_w(&[(2.0, 2.0), (12.0, 2.0), (12.0, 12.0), (2.0, 12.0), (2.0, 2.0)], B, 1.6, 1.3, 0.8); + p.rect(4.5, 4.5, 9.5, 9.5, 0.6, t(G), G); + p.dashed_w(&[(12.0, 12.0), (22.0, 12.0), (22.0, 22.0), (12.0, 22.0), (12.0, 12.0)], B, 1.6, 1.3, 0.8); + p.circle((17.0, 17.0), 2.9, t(O), O); + } + I::AlignObjects => { + p.dashed(&[(2.0, 12.0), (22.0, 12.0)], K, 2.0, 1.5); + p.rect(3.5, 4.0, 8.0, 20.0, 0.8, B, N); + p.rect(10.0, 7.5, 14.0, 16.5, 0.8, O, N); + p.rect(16.0, 5.5, 20.5, 18.5, 0.8, G, N); + } + I::AlignObjLeft => { + p.rect(3.5, 5.0, 19.5, 10.5, 0.8, t(B), B); + p.rect(3.5, 13.5, 13.5, 19.0, 0.8, t(B), B); + p.seg((3.5, 2.5), (3.5, 21.5), K); + } + I::AlignObjCenter => { + p.rect(4.0, 5.0, 20.0, 10.5, 0.8, t(B), B); + p.rect(7.0, 13.5, 17.0, 19.0, 0.8, t(B), B); + p.seg((12.0, 2.5), (12.0, 21.5), K); + } + I::AlignObjRight => { + p.rect(4.5, 5.0, 20.5, 10.5, 0.8, t(B), B); + p.rect(10.5, 13.5, 20.5, 19.0, 0.8, t(B), B); + p.seg((20.5, 2.5), (20.5, 21.5), K); + } + I::AlignObjTop => { + p.rect(5.0, 3.5, 10.5, 19.5, 0.8, t(B), B); + p.rect(13.5, 3.5, 19.0, 13.5, 0.8, t(B), B); + p.seg((2.5, 3.5), (21.5, 3.5), K); + } + I::AlignObjMiddle => { + p.rect(5.0, 4.0, 10.5, 20.0, 0.8, t(B), B); + p.rect(13.5, 7.0, 19.0, 17.0, 0.8, t(B), B); + p.seg((2.5, 12.0), (21.5, 12.0), K); + } + I::AlignObjBottom => { + p.rect(5.0, 4.5, 10.5, 20.5, 0.8, t(B), B); + p.rect(13.5, 10.5, 19.0, 20.5, 0.8, t(B), B); + p.seg((2.5, 20.5), (21.5, 20.5), K); + } + I::DistributeH => { + p.seg((2.5, 3.0), (2.5, 21.0), K); + p.seg((21.5, 3.0), (21.5, 21.0), K); + p.rect(4.9, 7.0, 8.2, 17.0, 0.6, t(B), B); + p.rect(10.35, 5.0, 13.65, 19.0, 0.6, t(B), B); + p.rect(15.8, 8.5, 19.1, 15.5, 0.6, t(B), B); + } + I::DistributeV => { + p.seg((3.0, 2.5), (21.0, 2.5), K); + p.seg((3.0, 21.5), (21.0, 21.5), K); + p.rect(7.0, 4.9, 17.0, 8.2, 0.6, t(B), B); + p.rect(5.0, 10.35, 19.0, 13.65, 0.6, t(B), B); + p.rect(8.5, 15.8, 15.5, 19.1, 0.6, t(B), B); + } + I::RotateLeft => { + p.poly(&[(3.0, 21.5), (3.0, 13.0), (12.0, 21.5)], t(B), B); + p.arc_arrow((13.0, 12.0), 7.5, 30.0, -160.0, K); + } + I::RotateRight => { + p.poly(&[(21.0, 21.5), (21.0, 13.0), (12.0, 21.5)], t(B), B); + p.arc_arrow((11.0, 12.0), 7.5, 150.0, 340.0, K); + } + I::FlipH => { + p.dashed(&[(12.0, 2.0), (12.0, 22.0)], K, 1.8, 1.4); + p.poly(&[(9.5, 5.0), (9.5, 19.0), (2.5, 19.0)], N, K); + p.poly(&[(14.5, 5.0), (21.5, 19.0), (14.5, 19.0)], B, B); + } + I::FlipV => { + p.dashed(&[(2.0, 12.0), (22.0, 12.0)], K, 1.8, 1.4); + p.poly(&[(5.0, 9.5), (19.0, 9.5), (19.0, 2.5)], N, K); + p.poly(&[(5.0, 14.5), (19.0, 21.5), (19.0, 14.5)], B, B); + } + I::SelectionPane => { + p.rect(3.0, 2.5, 21.0, 21.5, 1.5, N, K); + p.rect(4.6, 4.6, 19.4, 9.4, 0.8, t(B), N); + for (y, c) in [(7.0, B), (12.0, O), (17.0, G)] { + p.rect(6.0, y - 1.3, 8.6, y + 1.3, 0.4, c, N); + p.seg_w((10.5, y), (17.5, y), K, 0.85); + } + } + I::Rotate => { + p.rect(9.0, 9.0, 15.0, 15.0, 0.8, t(B), B); + p.arc_arrow((12.0, 12.0), 8.5, -70.0, 220.0, K); + } + + // ----- Picture ----- + I::Crop => { + p.rline_w(&[(6.5, 2.0), (6.5, 17.5), (22.0, 17.5)], 0.6, K, 1.25); + p.rline_w(&[(2.0, 6.5), (17.5, 6.5), (17.5, 22.0)], 0.6, B, 1.25); + } + I::Corrections => { + p.poly(&arc_pts((12.0, 12.0), 5.2, 5.2, -90.0, 90.0), Y, N); + p.circle((12.0, 12.0), 5.2, N, K); + for i in 0..8 { + let (s, c) = rad(i as f32 * 45.0).sin_cos(); + p.seg((12.0 + c * 7.6, 12.0 + s * 7.6), (12.0 + c * 9.8, 12.0 + s * 9.8), Y); + } + } + I::ColorAdjust => { + p.circle((12.0, 8.0), 5.6, R.a(0.6), N); + p.circle((8.3, 14.6), 5.6, G.a(0.6), N); + p.circle((15.7, 14.6), 5.6, B.a(0.6), N); + } + I::ArtisticEffects => { + p.rect(2.0, 4.0, 22.0, 20.0, 1.5, N, K); + p.curve_w((5.5, 16.0), (8.0, 4.5), (14.0, 21.0), (18.5, 8.0), PU, 2.0); + } + I::Transparency => { + let cs = 13.0 / 4.0; + for i in 0..4 { + for j in 0..4 { + if (i + j) % 2 == 0 { + let (x, y) = (3.0 + i as f32 * cs, 3.0 + j as f32 * cs); + p.rect(x, y, x + cs, y + cs, 0.0, K.a(0.3), N); + } + } + } + p.rect(3.0, 3.0, 16.0, 16.0, 0.6, N, K.a(0.6)); + p.rect(9.0, 9.0, 21.0, 21.0, 1.2, B.a(0.55), B); + } + I::CompressPictures => { + p.picture(6.5, 6.0, 17.5, 18.0); + p.arrow((0.8, 12.0), (5.0, 12.0), K); + p.arrow((23.2, 12.0), (19.0, 12.0), K); + } + I::ChangePicture => { + p.picture(2.0, 3.0, 18.0, 15.5); + let c = (17.5, 17.5); + if p.dis { + p.circle(c, 5.0, N, G); + } else { + p.circle(c, 5.0, G, N); + } + let wc = if p.dis { K } else { W }; + p.arc_arrow(c, 2.8, 200.0, 330.0, wc); + p.arc_arrow(c, 2.8, 20.0, 150.0, wc); + } + I::ResetPicture => { + p.picture(2.0, 3.0, 18.0, 15.5); + p.arc_arrow((17.0, 17.5), 4.0, -10.0, -260.0, B); + } + I::RemoveBackground => { + p.rect(2.0, 4.0, 22.0, 20.0, 1.5, PU.a(0.55), N); + p.dot((12.0, 10.0), 3.0, B); + p.poly(&arc_pts((12.0, 19.3), 6.0, 5.5, 180.0, 360.0), B, N); + p.rect(2.0, 4.0, 22.0, 20.0, 1.5, N, K); + } + I::AltText => { + p.picture(2.0, 2.0, 15.5, 12.5); + p.seg((2.5, 16.5), (21.5, 16.5), K); + p.seg((2.5, 20.5), (15.0, 20.5), K); + } + + // ----- Table ----- + I::InsertAbove | I::InsertBelow | I::InsertLeft | I::InsertRight => { + let (grid, add): ((f32, f32, f32, f32, usize, usize), (f32, f32, f32, f32)) = match icon { + I::InsertAbove => ((3.0, 10.0, 21.0, 21.0, 2, 3), (3.0, 2.5, 21.0, 7.5)), + I::InsertBelow => ((3.0, 3.0, 21.0, 14.0, 2, 3), (3.0, 16.5, 21.0, 21.5)), + I::InsertLeft => ((10.0, 3.0, 21.0, 21.0, 3, 2), (2.5, 3.0, 7.5, 21.0)), + _ => ((3.0, 3.0, 14.0, 21.0, 3, 2), (16.5, 3.0, 21.5, 21.0)), + }; + p.table(grid.0, grid.1, grid.2, grid.3, grid.4, grid.5, N); + p.rect(add.0, add.1, add.2, add.3, 1.0, t(G), G); + let c = ((add.0 + add.2) * 0.5, (add.1 + add.3) * 0.5); + p.seg_w((c.0 - 1.6, c.1), (c.0 + 1.6, c.1), G, 0.9); + p.seg_w((c.0, c.1 - 1.6), (c.0, c.1 + 1.6), G, 0.9); + } + I::DeleteTable => { + p.table(2.5, 3.0, 19.5, 17.0, 3, 3, B); + p.badge((18.0, 18.0), 5.0, R, Glyph::Cross); + } + I::MergeCells => { + p.rect(2.5, 6.0, 21.5, 18.0, 1.2, N, K); + p.dashed(&[(12.0, 6.0), (12.0, 18.0)], K.a(0.6), 1.5, 1.2); + p.arrow((4.5, 12.0), (10.2, 12.0), B); + p.arrow((19.5, 12.0), (13.8, 12.0), B); + } + I::SplitCells => { + p.rect(2.5, 6.0, 21.5, 18.0, 1.2, N, K); + p.seg((12.0, 6.0), (12.0, 18.0), K); + p.arrow((10.0, 12.0), (4.5, 12.0), B); + p.arrow((14.0, 12.0), (19.5, 12.0), B); + } + I::Borders => { + p.dotted(&[(12.0, 5.0), (12.0, 19.0)], K, 2.2, 0.45); + p.dotted(&[(5.0, 12.0), (19.0, 12.0)], K, 2.2, 0.45); + p.rect(3.0, 3.0, 21.0, 21.0, 0.6, N, B); + } + I::Shading => { + p.rect(3.0, 3.0, 12.0, 12.0, 0.0, Y.a(0.8), N); + p.rect(12.0, 12.0, 21.0, 21.0, 0.0, Y.a(0.8), N); + p.table(3.0, 3.0, 21.0, 21.0, 2, 2, N); + } + + // ----- Chart ----- + I::ChartElements => { + p.bars(2.5, 3.5, 16.0, 17.5); + p.badge((18.5, 18.5), 4.8, G, Glyph::Plus); + } + I::ChartStyles => { + p.bars(2.5, 6.0, 15.5, 20.5); + p.brush((14.0, 10.0), -45.0, 12.0, PU); + } + I::SwitchRowCol => { + p.table(2.5, 2.5, 11.0, 11.0, 2, 2, B); + p.rect(13.0, 13.0, 17.25, 21.5, 0.6, O, N); + p.table(13.0, 13.0, 21.5, 21.5, 2, 2, N); + p.line(&quad_pts((14.0, 4.0), (20.0, 4.0), (20.0, 9.5), 10), K); + p.head((20.0, 11.0), 90.0, 3.6, K); + p.line(&quad_pts((10.0, 20.0), (4.0, 20.0), (4.0, 14.5), 10), K); + p.head((4.0, 13.0), -90.0, 3.6, K); + } + I::EditData => { + p.table(2.5, 3.0, 18.0, 16.0, 3, 3, B); + p.tool((11.0, 21.5), -45.0, 14.0, 2.2, 3.8, Y, K, 2.5, R); + } + I::ChangeChartType => { + p.circle((8.0, 8.0), 5.8, t(B), B); + let mut wedge = vec![(8.0, 8.0)]; + wedge.extend(arc_pts((8.0, 8.0), 5.8, 5.8, -90.0, 0.0)); + p.poly(&wedge, O, N); + p.rect(13.0, 15.0, 15.8, 21.0, 0.4, G, N); + p.rect(17.5, 11.0, 20.3, 21.0, 0.4, B, N); + p.seg((11.5, 21.5), (22.0, 21.5), K); + } + + // ----- Media ----- + I::Play => p.rshape(&[(6.5, 4.0), (19.5, 12.0), (6.5, 20.0)], 1.0, G, N), + I::Pause => { + p.rect(6.0, 4.5, 10.0, 19.5, 1.0, K, N); + p.rect(14.0, 4.5, 18.0, 19.5, 1.0, K, N); + } + I::Stop => p.rect(5.0, 5.0, 19.0, 19.0, 2.0, K, N), + I::TrimMedia => { + p.rect(2.0, 8.0, 22.0, 16.0, 1.2, N, K); + p.rect(7.0, 8.0, 17.0, 16.0, 0.0, t(Y), N); + p.rect(5.8, 5.0, 8.2, 19.0, 0.8, Y, N); + p.rect(15.8, 5.0, 18.2, 19.0, 0.8, Y, N); + } + I::Volume => { + p.speaker(2.5, 12.0, N, K); + p.arc((11.0, 12.0), 3.8, -45.0, 45.0, B); + p.arc((11.0, 12.0), 7.0, -45.0, 45.0, B); + p.arc((11.0, 12.0), 10.2, -40.0, 40.0, B); + } + I::Loop => { + p.path_arrow(&round_pts(&[(4.0, 13.0), (4.0, 7.0), (20.0, 7.0)], 2.5, false), G); + p.path_arrow(&round_pts(&[(20.0, 11.0), (20.0, 17.0), (4.0, 17.0)], 2.5, false), G); + } + I::Bookmark => { + p.rshape(&[(6.0, 2.5), (18.0, 2.5), (18.0, 21.5), (12.0, 16.5), (6.0, 21.5)], 0.8, R, N); + } + + // ----- Window chrome ----- + I::Home => { + p.rect(10.0, 14.5, 14.0, 21.0, 0.8, B, N); + p.line(&[(2.5, 11.5), (12.0, 3.0), (21.5, 11.5)], K); + p.rline(&[(5.0, 9.5), (5.0, 21.0), (19.0, 21.0), (19.0, 9.5)], 1.2, K); + } + I::Save => { + p.rshape(&[(3.0, 3.0), (17.5, 3.0), (21.0, 6.5), (21.0, 21.0), (3.0, 21.0)], 1.5, t(B), B); + p.rect(7.0, 3.0, 15.0, 8.5, 0.6, N, B); + p.seg((12.5, 4.5), (12.5, 7.0), B); + p.rect(6.5, 13.0, 17.5, 21.0, 0.8, N, B); + } + I::Undo | I::Redo => { + let mut pts = vec![(6.0, 8.5), (14.0, 8.5)]; + pts.extend(arc_pts((14.0, 14.0), 5.5, 5.5, -90.0, 90.0)); + pts.push((8.5, 19.5)); + let hs = p.head_size(); + let pts: Vec = if icon == I::Undo { pts } else { pts.into_iter().map(|(x, y)| (24.0 - x, y)).collect() }; + p.line(&pts, K); + if icon == I::Undo { + p.head((3.0, 8.5), 180.0, hs, K); + } else { + p.head((21.0, 8.5), 0.0, hs, K); + } + } + I::More => { + for x in [5.0, 12.0, 19.0] { + p.dot((x, 12.0), 1.9, K); + } + } + I::Search => p.magnifier((10.0, 10.0), 6.5, N, K), + I::Share => { + p.seg((6.0, 12.0), (18.0, 5.5), K); + p.seg((6.0, 12.0), (18.0, 18.5), K); + p.dot((18.0, 5.5), 3.0, B); + p.dot((6.0, 12.0), 3.0, B); + p.dot((18.0, 18.5), 3.0, B); + } + I::CommentsBubble => p.bubble(2.5, 3.5, 21.5, 17.0, N, K), + I::File => { + p.page(4.5, 2.0, 19.5, 22.0, N, K); + p.seg((8.0, 12.5), (16.0, 12.5), B); + p.seg((8.0, 16.5), (14.0, 16.5), B); + } + I::Folder => { + p.rshape(&[(2.5, 4.5), (9.0, 4.5), (11.0, 7.0), (21.5, 7.0), (21.5, 19.5), (2.5, 19.5)], 1.2, t(Y), K); + p.rshape(&[(2.5, 10.0), (21.5, 10.0), (21.5, 19.5), (2.5, 19.5)], 1.2, Y.a(0.6), K); + } + I::Export => { + p.rline(&[(15.0, 8.5), (15.0, 2.5), (3.0, 2.5), (3.0, 21.5), (15.0, 21.5), (15.0, 15.5)], 1.5, K); + p.arrow((8.5, 12.0), (22.0, 12.0), B); + } + I::Print => { + p.rect(6.5, 2.5, 17.5, 9.0, 0.8, N, K); + p.rline(&[(6.5, 17.5), (2.5, 17.5), (2.5, 9.0), (21.5, 9.0), (21.5, 17.5), (17.5, 17.5)], 1.8, K); + p.rect(6.5, 14.0, 17.5, 21.5, 0.8, N, K); + p.dot((18.5, 12.0), 1.1, G); + } + I::Settings => { + p.gear((12.0, 12.0), 9.8, 7.3, 8, N, K); + p.circle((12.0, 12.0), 3.0, N, K); + } + I::Help => { + p.circle((12.0, 12.0), 9.8, N, K); + let mut q = arc_pts((12.0, 9.4), 3.2, 3.0, 180.0, 400.0); + q.push((12.0, 13.0)); + q.push((12.0, 14.6)); + p.line(&q, B); + p.dot((12.0, 17.8), 1.25, B); + } + I::Info => { + p.circle((12.0, 12.0), 9.8, N, K); + p.dot((12.0, 7.3), 1.3, B); + p.seg((12.0, 10.8), (12.0, 17.5), B); + } + I::Warning => { + p.rshape(&[(12.0, 2.5), (22.5, 20.5), (1.5, 20.5)], 2.0, Y, N); + p.seg((12.0, 9.0), (12.0, 14.0), D); + p.dot((12.0, 17.2), 1.2, D); + } + // Selection pane toggles: monochrome outlines in the ink colour. + I::Lock => { + let mut sh = vec![(7.5, 10.5), (7.5, 8.0)]; + sh.extend(arc_pts((12.0, 8.0), 4.5, 4.5, 180.0, 360.0)); + sh.push((16.5, 10.5)); + p.line(&sh, K); + p.rect(4.5, 10.5, 19.5, 21.5, 1.8, N, K); + p.seg((12.0, 14.5), (12.0, 17.5), K); + } + I::Unlock => { + // The shackle swung open: its left leg out of the body, raised. + let mut sh = vec![(12.0, 10.5), (12.0, 6.0)]; + sh.extend(arc_pts((16.5, 6.0), 4.5, 4.5, 180.0, 360.0)); + sh.push((21.0, 8.5)); + p.line(&sh, K); + p.rect(3.0, 10.5, 16.5, 21.5, 1.8, N, K); + p.seg((9.75, 14.5), (9.75, 17.5), K); + } + I::Eye | I::EyeOff => { + let mut e = quad_pts((2.0, 12.0), (12.0, 4.0), (22.0, 12.0), 16); + e.extend(quad_pts((22.0, 12.0), (12.0, 20.0), (2.0, 12.0), 16).into_iter().skip(1)); + e.pop(); + p.shape(&e, N, K); + p.circle((12.0, 12.0), 3.2, N, K); + if icon == I::EyeOff { + p.seg_w((4.0, 21.0), (20.0, 3.0), K, 1.1); + } + } + I::Close => { + p.seg_w((5.5, 5.5), (18.5, 18.5), K, 1.1); + p.seg_w((18.5, 5.5), (5.5, 18.5), K, 1.1); + } + I::Check => p.line_w(&[(4.0, 12.5), (9.5, 18.0), (20.0, 6.5)], G, 1.3), + I::Plus => { + p.seg((12.0, 4.5), (12.0, 19.5), K); + p.seg((4.5, 12.0), (19.5, 12.0), K); + } + I::Minus => p.seg((4.5, 12.0), (19.5, 12.0), K), + I::ChevronDown => p.line(&[(6.0, 9.0), (12.0, 15.0), (18.0, 9.0)], K), + I::ChevronUp => p.line(&[(6.0, 15.0), (12.0, 9.0), (18.0, 15.0)], K), + I::ChevronLeft => p.line(&[(15.0, 6.0), (9.0, 12.0), (15.0, 18.0)], K), + I::ChevronRight => p.line(&[(9.0, 6.0), (15.0, 12.0), (9.0, 18.0)], K), + I::DragHandle => { + for x in [9.0, 15.0] { + for y in [6.0, 12.0, 18.0] { + p.dot((x, y), 1.6, K); + } + } + } + I::Star => p.star((12.0, 12.8), 10.0, t(Y), Y), + I::Image => { + p.rect(2.5, 4.0, 21.5, 20.0, 1.5, N, K); + p.circle((16.0, 9.0), 1.8, N, K); + p.line(&[(2.5, 17.5), (8.5, 11.0), (14.0, 16.5), (16.5, 14.0), (21.5, 18.5)], K); + } + I::Sparkle => { + p.sparkle((10.5, 13.0), 9.0, PU); + p.sparkle((19.0, 5.0), 3.8, Y); + } + I::Palette => { + let mut pts = arc_pts((12.0, 12.0), 9.8, 9.5, 70.0, 380.0); + pts.extend(cubic_pts((21.2, 15.25), (19.0, 15.0), (14.5, 14.0), (15.5, 21.0), 10)); + p.shape(&pts, N, K); + p.dot((7.5, 9.5), 1.7, R); + p.dot((11.5, 6.3), 1.7, Y); + p.dot((16.3, 7.8), 1.7, G); + p.dot((7.2, 14.8), 1.7, B); + } + I::Grid => { + for (x, y) in [(3.0, 3.0), (13.0, 3.0), (3.0, 13.0), (13.0, 13.0)] { + p.rect(x, y, x + 8.0, y + 8.0, 1.5, N, K); + } + } + I::List => { + for y in [5.5, 12.0, 18.5] { + p.dot((4.5, y), 1.5, K); + p.seg((9.0, y), (21.0, y), K); + } + } + I::Collapse => { + p.line(&[(7.0, 3.0), (12.0, 8.0), (17.0, 3.0)], K); + p.line(&[(7.0, 21.0), (12.0, 16.0), (17.0, 21.0)], K); + p.seg_w((5.0, 12.0), (19.0, 12.0), K.a(0.5), 0.8); + } + I::Expand => { + p.line(&[(7.0, 8.0), (12.0, 3.0), (17.0, 8.0)], K); + p.line(&[(7.0, 16.0), (12.0, 21.0), (17.0, 16.0)], K); + p.seg_w((5.0, 12.0), (19.0, 12.0), K.a(0.5), 0.8); + } + I::Pin => { + let m = |pts: &[P2]| tf((12.0, 12.0), 35.0, pts); + p.line(&m(&[(0.0, 2.0), (0.0, 10.5)]), K); + p.rshape(&m(&[(-6.0, 0.0), (6.0, 0.0), (6.0, 2.5), (-6.0, 2.5)]), 0.6, R, N); + p.poly(&m(&[(-3.0, -7.0), (3.0, -7.0), (3.6, 0.0), (-3.6, 0.0)]), R, N); + p.rshape(&m(&[(-4.2, -10.0), (4.2, -10.0), (4.2, -7.0), (-4.2, -7.0)]), 0.6, R, N); + } + + // ----- File tab (Backstage) navigation: outlines in the ink colour ----- + I::BackCircle => { + p.circle((12.0, 12.0), 9.8, N, K); + p.arrow((17.0, 12.0), (6.5, 12.0), K); + } + I::SaveAs => { + p.rline(&[(11.0, 20.5), (2.5, 20.5), (2.5, 3.0), (15.0, 3.0), (18.0, 6.0), (18.0, 10.5)], 1.2, K); + p.rect(6.0, 3.0, 13.0, 7.5, 0.6, N, K); + p.seg_w((13.5, 21.0), (21.5, 13.0), O, 1.4); + p.seg_w((12.5, 22.0), (13.5, 21.0), K, 1.0); + } + I::Account => { + p.circle((12.0, 8.0), 4.5, N, K); + p.line(&arc_pts((12.0, 22.0), 8.5, 7.5, 180.0, 360.0), K); + } + I::Recent => { + p.circle((12.0, 12.0), 9.8, N, K); + p.line(&[(12.0, 6.0), (12.0, 12.0), (16.0, 14.5)], K); + } + I::NewFile => { + p.rline(&[(14.0, 2.5), (5.0, 2.5), (5.0, 21.5), (19.0, 21.5), (19.0, 7.5), (14.0, 2.5), (14.0, 7.5), (19.0, 7.5)], 0.8, K); + } + I::OpenFolder => { + p.rline(&[(18.5, 9.0), (18.5, 6.5), (10.0, 6.5), (8.0, 4.0), (2.5, 4.0), (2.5, 19.5), (18.0, 19.5)], 1.0, K); + p.rline(&[(2.5, 19.5), (6.0, 10.0), (22.0, 10.0), (18.0, 19.5)], 1.0, K); + } + + // ----- Status bar (monochrome) ----- + I::NotesSmall => { + p.rect(3.0, 4.0, 21.0, 20.0, 1.5, N, K); + p.seg((3.0, 13.5), (21.0, 13.5), K); + p.seg((6.5, 16.8), (15.5, 16.8), K); + } + I::CommentsSmall => p.bubble(3.0, 4.0, 21.0, 16.5, N, K), + I::ViewNormalSmall => { + p.rect(2.5, 4.0, 21.5, 20.0, 1.5, N, K); + p.seg((8.5, 4.0), (8.5, 20.0), K); + } + I::ViewSorterSmall => { + for (x, y) in [(2.5, 4.5), (13.0, 4.5), (2.5, 13.5), (13.0, 13.5)] { + p.rect(x, y, x + 8.5, y + 6.0, 1.0, N, K); + } + } + I::ViewReadingSmall => { + p.book(N, K); + p.seg((12.0, 7.0), (12.0, 20.5), K); + } + I::ViewShowSmall => { + p.seg((2.0, 4.0), (22.0, 4.0), K); + p.rect(3.5, 4.0, 20.5, 16.0, 0.8, N, K); + p.seg((12.0, 16.0), (12.0, 19.0), K); + p.line(&[(8.5, 22.0), (12.0, 19.0), (15.5, 22.0)], K); + } + I::FitSmall => { + for (cx, cy, sx, sy) in [(3.0, 3.0, 1.0, 1.0), (21.0, 3.0, -1.0, 1.0), (3.0, 21.0, 1.0, -1.0), (21.0, 21.0, -1.0, -1.0)] { + p.rline(&[(cx, cy + 5.0 * sy), (cx, cy), (cx + 5.0 * sx, cy)], 0.8, K); + } + p.rect(8.0, 8.5, 16.0, 15.5, 0.8, N, K); + } + I::ZoomIn | I::ZoomOut => { + p.magnifier((10.0, 10.0), 7.0, N, K); + p.seg((7.0, 10.0), (13.0, 10.0), K); + if icon == I::ZoomIn { + p.seg((10.0, 7.0), (10.0, 13.0), K); + } + } + } +} diff --git a/crates/ui-egui/src/icons/geometry.rs b/crates/ui-egui/src/icons/geometry.rs new file mode 100644 index 00000000..93ac96c0 --- /dev/null +++ b/crates/ui-egui/src/icons/geometry.rs @@ -0,0 +1,763 @@ +use super::*; + +// --------------------------------------------------------------------------------------------- +// Drawing toolkit +// --------------------------------------------------------------------------------------------- + +/// A logical colour, resolved against the theme ink and the disabled flag. +#[derive(Clone, Copy)] +pub(super) enum C { + Ink(f32), + Acc(Color32, f32), + White, + None, +} + +impl C { + pub(super) fn a(self, f: f32) -> C { + match self { + C::Ink(a) => C::Ink(a * f), + C::Acc(c, a) => C::Acc(c, a * f), + other => other, + } + } +} + +pub(super) const K: C = C::Ink(1.0); + +pub(super) const B: C = C::Acc(BLUE, 1.0); + +pub(super) const O: C = C::Acc(ORANGE, 1.0); + +pub(super) const G: C = C::Acc(GREEN, 1.0); + +pub(super) const PU: C = C::Acc(PURPLE, 1.0); + +pub(super) const R: C = C::Acc(RED, 1.0); + +pub(super) const Y: C = C::Acc(GOLD, 1.0); + +pub(super) const D: C = C::Acc(DARK, 1.0); + +pub(super) const W: C = C::White; + +pub(super) const N: C = C::None; + +/// Light tint of an accent, for fills behind outlines. +pub(super) fn t(c: C) -> C { + c.a(0.25) +} + +pub(super) type P2 = (f32, f32); + +#[derive(Clone, Copy)] +pub(super) enum Glyph { + Plus, + Cross, + Check, +} + +pub(super) struct Pen<'a> { + pub(super) p: &'a Painter, + pub(super) o: Pos2, + /// Points per design unit. + pub(super) k: f32, + /// Base stroke width in points. + pub(super) sw: f32, + pub(super) ink: Color32, + pub(super) dis: bool, +} + +pub(super) fn rad(deg: f32) -> f32 { + deg * PI / 180.0 +} + +pub(super) fn arc_pts(c: P2, rx: f32, ry: f32, a0: f32, a1: f32) -> Vec { + let n = (((a1 - a0).abs() / 9.0).ceil() as usize).max(4); + (0..=n) + .map(|i| { + let a = rad(a0 + (a1 - a0) * i as f32 / n as f32); + (c.0 + rx * a.cos(), c.1 + ry * a.sin()) + }) + .collect() +} + +pub(super) fn cubic_pts(p0: P2, p1: P2, p2: P2, p3: P2, n: usize) -> Vec { + (0..=n) + .map(|i| { + let t = i as f32 / n as f32; + let u = 1.0 - t; + let (a, b, c, d) = (u * u * u, 3.0 * u * u * t, 3.0 * u * t * t, t * t * t); + (a * p0.0 + b * p1.0 + c * p2.0 + d * p3.0, a * p0.1 + b * p1.1 + c * p2.1 + d * p3.1) + }) + .collect() +} + +pub(super) fn quad_pts(p0: P2, p1: P2, p2: P2, n: usize) -> Vec { + (0..=n) + .map(|i| { + let t = i as f32 / n as f32; + let u = 1.0 - t; + (u * u * p0.0 + 2.0 * u * t * p1.0 + t * t * p2.0, u * u * p0.1 + 2.0 * u * t * p1.1 + t * t * p2.1) + }) + .collect() +} + +/// Rounds the corners of a polyline by `r` design units. +pub(super) fn round_pts(pts: &[P2], r: f32, closed: bool) -> Vec { + let n = pts.len(); + if n < 3 || r <= 0.0 { + return pts.to_vec(); + } + let mut out = Vec::with_capacity(n * 6); + for i in 0..n { + let cur = pts[i]; + let interior = closed || (i > 0 && i + 1 < n); + if !interior { + out.push(cur); + continue; + } + let prev = pts[(i + n - 1) % n]; + let next = pts[(i + 1) % n]; + let d1 = ((prev.0 - cur.0).powi(2) + (prev.1 - cur.1).powi(2)).sqrt(); + let d2 = ((next.0 - cur.0).powi(2) + (next.1 - cur.1).powi(2)).sqrt(); + if d1 < 1e-4 || d2 < 1e-4 { + out.push(cur); + continue; + } + let rr = r.min(d1 * 0.5).min(d2 * 0.5); + let a = (cur.0 + (prev.0 - cur.0) / d1 * rr, cur.1 + (prev.1 - cur.1) / d1 * rr); + let b = (cur.0 + (next.0 - cur.0) / d2 * rr, cur.1 + (next.1 - cur.1) / d2 * rr); + out.extend(quad_pts(a, cur, b, 4)); + } + out +} + +pub(super) fn rrect_pts(x0: f32, y0: f32, x1: f32, y1: f32, r: f32) -> Vec { + round_pts(&[(x0, y0), (x1, y0), (x1, y1), (x0, y1)], r, true) +} + +/// Star polygon with `n` points. +pub(super) fn star_pts(c: P2, ro: f32, ri: f32, n: usize, rot: f32) -> Vec { + (0..n * 2) + .map(|i| { + let r = if i % 2 == 0 { ro } else { ri }; + let a = rad(rot - 90.0 + i as f32 * 180.0 / n as f32); + (c.0 + r * a.cos(), c.1 + r * a.sin()) + }) + .collect() +} + +/// Transforms local points (u along `ang`, v across) to design coordinates around `c`. +pub(super) fn tf(c: P2, ang: f32, pts: &[P2]) -> Vec { + let (s, co) = rad(ang).sin_cos(); + pts.iter().map(|&(u, v)| (c.0 + u * co - v * s, c.1 + u * s + v * co)).collect() +} + +fn point_in_tri(p: Pos2, a: Pos2, b: Pos2, c: Pos2) -> bool { + let d1 = (p.x - b.x) * (a.y - b.y) - (a.x - b.x) * (p.y - b.y); + let d2 = (p.x - c.x) * (b.y - c.y) - (b.x - c.x) * (p.y - c.y); + let d3 = (p.x - a.x) * (c.y - a.y) - (c.x - a.x) * (p.y - a.y); + let neg = d1 < 0.0 || d2 < 0.0 || d3 < 0.0; + let pos = d1 > 0.0 || d2 > 0.0 || d3 > 0.0; + !(neg && pos) +} + +/// Ear-clipping triangulation of a simple polygon. +pub(super) fn triangulate(pts: &[Pos2]) -> Vec<[u32; 3]> { + let n = pts.len(); + let mut tris = Vec::new(); + if n < 3 { + return tris; + } + let area: f32 = (0..n) + .map(|i| { + let a = pts[i]; + let b = pts[(i + 1) % n]; + a.x * b.y - b.x * a.y + }) + .sum(); + let mut idx: Vec = (0..n).collect(); + if area < 0.0 { + idx.reverse(); + } + while idx.len() > 3 { + let m = idx.len(); + let mut clipped = false; + for i in 0..m { + let (ia, ib, ic) = (idx[(i + m - 1) % m], idx[i], idx[(i + 1) % m]); + let (a, b, c) = (pts[ia], pts[ib], pts[ic]); + let cross = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x); + if cross <= 1e-7 { + continue; + } + let blocked = idx.iter().any(|&j| j != ia && j != ib && j != ic && point_in_tri(pts[j], a, b, c)); + if blocked { + continue; + } + tris.push([ia as u32, ib as u32, ic as u32]); + idx.remove(i); + clipped = true; + break; + } + if !clipped { + // Degenerate input: drop a vertex and carry on rather than loop forever. + idx.remove(0); + } + } + if let [a, b, c] = idx[..] { + tris.push([a as u32, b as u32, c as u32]); + } + tris +} + +impl Pen<'_> { + fn pt(&self, (x, y): P2) -> Pos2 { + pos2(self.o.x + x * self.k, self.o.y + y * self.k) + } + + fn v(&self, pts: &[P2]) -> Vec { + pts.iter().map(|&p| self.pt(p)).collect() + } + + fn col(&self, c: C) -> Color32 { + let dim = if self.dis { 0.35 } else { 1.0 }; + match c { + C::Ink(a) => self.ink.gamma_multiply(a * dim), + C::Acc(col, a) => { + if self.dis { + self.ink.gamma_multiply(a * dim) + } else { + col.gamma_multiply(a) + } + } + C::White => { + if self.dis { + // Glyphs on solid fills stay legible as a darker mark. + self.ink.gamma_multiply(0.6) + } else { + Color32::WHITE + } + } + C::None => Color32::TRANSPARENT, + } + } + + fn stroke(&self, c: C, w: f32) -> Stroke { + Stroke::new(self.sw * w, self.col(c)) + } + + // --- lines --- + + pub(super) fn line_w(&self, pts: &[P2], c: C, w: f32) { + if pts.len() < 2 { + return; + } + let col = self.col(c); + if col == Color32::TRANSPARENT { + return; + } + let v = self.v(pts); + if let [a, b] = v[..] { + // Single segments get round caps: draw them as one convex capsule. + let d = b - a; + let len = d.length(); + let r = self.sw * w * 0.5; + if len < 1e-3 { + self.p.circle_filled(a, r, col); + return; + } + let ang = d.y.atan2(d.x); + let mut cap = Vec::with_capacity(20); + for (c, start) in [(b, ang - PI * 0.5), (a, ang + PI * 0.5)] { + for i in 0..=8 { + let t = start + PI * i as f32 / 8.0; + cap.push(c + vec2(t.cos(), t.sin()) * r); + } + } + self.p.add(PathShape::convex_polygon(cap, col, Stroke::NONE)); + return; + } + self.p.add(PathShape::line(v, Stroke::new(self.sw * w, col))); + } + + pub(super) fn line(&self, pts: &[P2], c: C) { + self.line_w(pts, c, 1.0); + } + + pub(super) fn seg(&self, a: P2, b: P2, c: C) { + self.line_w(&[a, b], c, 1.0); + } + + pub(super) fn seg_w(&self, a: P2, b: P2, c: C, w: f32) { + self.line_w(&[a, b], c, w); + } + + pub(super) fn rline(&self, pts: &[P2], r: f32, c: C) { + self.line(&round_pts(pts, r, false), c); + } + + pub(super) fn rline_w(&self, pts: &[P2], r: f32, c: C, w: f32) { + self.line_w(&round_pts(pts, r, false), c, w); + } + + pub(super) fn dashed(&self, pts: &[P2], c: C, dash: f32, gap: f32) { + let v = self.v(pts); + self.p.extend(Shape::dashed_line(&v, self.stroke(c, 1.0), dash * self.k, gap * self.k)); + } + + pub(super) fn dashed_w(&self, pts: &[P2], c: C, dash: f32, gap: f32, w: f32) { + let v = self.v(pts); + self.p.extend(Shape::dashed_line(&v, self.stroke(c, w), dash * self.k, gap * self.k)); + } + + pub(super) fn dotted(&self, pts: &[P2], c: C, spacing: f32, r: f32) { + let v = self.v(pts); + self.p.extend(Shape::dotted_line(&v, self.col(c), spacing * self.k, r * self.sw)); + } + + // --- filled shapes --- + + /// Any simple polygon: filled via triangulation, stroked as a closed path. + pub(super) fn shape_w(&self, pts: &[P2], fill: C, stroke: C, w: f32) { + if pts.len() < 3 { + return; + } + let v = self.v(pts); + let fc = self.col(fill); + if fc != Color32::TRANSPARENT { + let mut mesh = Mesh::default(); + for &p in &v { + mesh.colored_vertex(p, fc); + } + for [a, b, c] in triangulate(&v) { + mesh.add_triangle(a, b, c); + } + self.p.add(Shape::mesh(mesh)); + // A hairline in the fill colour anti-aliases the mesh edge. + if matches!(stroke, C::None) { + self.p.add(PathShape::closed_line(v.clone(), Stroke::new(0.6_f32.min(self.sw * 0.5), fc))); + } + } + let sc = self.col(stroke); + if sc != Color32::TRANSPARENT { + self.p.add(PathShape::closed_line(v, Stroke::new(self.sw * w, sc))); + } + } + + pub(super) fn shape(&self, pts: &[P2], fill: C, stroke: C) { + self.shape_w(pts, fill, stroke, 1.0); + } + + pub(super) fn rshape(&self, pts: &[P2], r: f32, fill: C, stroke: C) { + self.shape(&round_pts(pts, r, true), fill, stroke); + } + + /// Convex polygon (anti-aliased fill). + pub(super) fn poly(&self, pts: &[P2], fill: C, stroke: C) { + self.poly_w(pts, fill, stroke, 1.0); + } + + pub(super) fn poly_w(&self, pts: &[P2], fill: C, stroke: C, w: f32) { + let v = self.v(pts); + self.p.add(PathShape::convex_polygon(v, self.col(fill), self.stroke(stroke, w))); + } + + fn rect_w(&self, x0: f32, y0: f32, x1: f32, y1: f32, r: f32, fill: C, stroke: C, w: f32) { + let rect = Rect::from_min_max(self.pt((x0, y0)), self.pt((x1, y1))); + self.p.rect(rect, r * self.k, self.col(fill), self.stroke(stroke, w), StrokeKind::Middle); + } + + pub(super) fn rect(&self, x0: f32, y0: f32, x1: f32, y1: f32, r: f32, fill: C, stroke: C) { + self.rect_w(x0, y0, x1, y1, r, fill, stroke, 1.0); + } + + fn circle_w(&self, c: P2, r: f32, fill: C, stroke: C, w: f32) { + self.p.circle(self.pt(c), r * self.k, self.col(fill), self.stroke(stroke, w)); + } + + pub(super) fn circle(&self, c: P2, r: f32, fill: C, stroke: C) { + self.circle_w(c, r, fill, stroke, 1.0); + } + + pub(super) fn dot(&self, c: P2, r: f32, fill: C) { + self.p.circle_filled(self.pt(c), r * self.k, self.col(fill)); + } + + pub(super) fn ellipse(&self, c: P2, rx: f32, ry: f32, fill: C, stroke: C) { + let mut pts = arc_pts(c, rx, ry, 0.0, 360.0); + pts.pop(); + self.poly(&pts, fill, stroke); + } + + // --- arcs and arrows --- + + pub(super) fn arc(&self, c: P2, r: f32, a0: f32, a1: f32, col: C) { + self.line(&arc_pts(c, r, r, a0, a1), col); + } + + /// Filled arrow head with its tip at `tip`, pointing along `dir` degrees. + pub(super) fn head(&self, tip: P2, dir: f32, size: f32, c: C) { + let (s, co) = rad(dir).sin_cos(); + let hw = size * 0.62; + let back = (tip.0 - co * size, tip.1 - s * size); + self.poly(&[tip, (back.0 - s * hw, back.1 + co * hw), (back.0 + s * hw, back.1 - co * hw)], c, N); + } + + pub(super) fn head_size(&self) -> f32 { + (2.2 * self.sw / self.k).max(3.6) + } + + pub(super) fn arrow(&self, a: P2, b: P2, c: C) { + let dir = (b.1 - a.1).atan2(b.0 - a.0); + let hs = self.head_size(); + let end = (b.0 - dir.cos() * hs * 0.7, b.1 - dir.sin() * hs * 0.7); + self.seg(a, end, c); + self.head(b, dir.to_degrees(), hs, c); + } + + pub(super) fn arrow2(&self, a: P2, b: P2, c: C) { + let dir = (b.1 - a.1).atan2(b.0 - a.0); + let hs = self.head_size(); + let s = (a.0 + dir.cos() * hs * 0.7, a.1 + dir.sin() * hs * 0.7); + let e = (b.0 - dir.cos() * hs * 0.7, b.1 - dir.sin() * hs * 0.7); + self.seg(s, e, c); + self.head(b, dir.to_degrees(), hs, c); + self.head(a, dir.to_degrees() + 180.0, hs, c); + } + + /// Polyline ending in an arrow head. + pub(super) fn path_arrow(&self, pts: &[P2], c: C) { + let n = pts.len(); + if n < 2 { + return; + } + let a = pts[n - 2]; + let b = pts[n - 1]; + let dir = (b.1 - a.1).atan2(b.0 - a.0); + let hs = self.head_size(); + let mut body = pts.to_vec(); + body[n - 1] = (b.0 - dir.cos() * hs * 0.7, b.1 - dir.sin() * hs * 0.7); + self.line(&body, c); + self.head(b, dir.to_degrees(), hs, c); + } + + /// Circular arc from `a0` to `a1` degrees with an arrow head at the `a1` end. + pub(super) fn arc_arrow(&self, c: P2, r: f32, a0: f32, a1: f32, col: C) { + let hs = self.head_size(); + let sign = if a1 >= a0 { 1.0 } else { -1.0 }; + let back = (hs * 0.6 / r).to_degrees() * sign; + self.line(&arc_pts(c, r, r, a0, a1 - back), col); + let tip_a = rad(a1); + let tip = (c.0 + r * tip_a.cos(), c.1 + r * tip_a.sin()); + // Tangent direction of travel, nudged inward to sit on the arc. + let dir = a1 + 90.0 * sign - (hs * 0.5 / r).to_degrees() * sign; + self.head(tip, dir, hs, col); + } + + pub(super) fn curve(&self, p0: P2, p1: P2, p2: P2, p3: P2, c: C) { + self.line(&cubic_pts(p0, p1, p2, p3, 24), c); + } + + pub(super) fn curve_w(&self, p0: P2, p1: P2, p2: P2, p3: P2, c: C, w: f32) { + self.line_w(&cubic_pts(p0, p1, p2, p3, 24), c, w); + } + + // --- text --- + + pub(super) fn text(&self, s: &str, at: P2, size: f32, c: C) { + self.p.text(self.pt(at), Align2::CENTER_CENTER, s, FontId::proportional(size * self.k), self.col(c)); + } + + pub(super) fn text_bold(&self, s: &str, at: P2, size: f32, c: C) { + let d = 0.32 * self.sw / self.k; + let reps: &[f32] = if self.dis { &[0.0] } else { &[-d, 0.0, d] }; + for &dx in reps { + self.text(s, (at.0 + dx, at.1), size, c); + } + } + + pub(super) fn text_rot(&self, s: &str, at: P2, size: f32, c: C, angle_deg: f32) { + let col = self.col(c); + let galley = self.p.layout_no_wrap(s.to_owned(), FontId::proportional(size * self.k), col); + let rot = egui::emath::Rot2::from_angle(rad(angle_deg)); + let pos = self.pt(at) - rot * (galley.size() * 0.5); + self.p.add(TextShape::new(pos, galley, col).with_angle(rad(angle_deg))); + } + + // --- composite parts --- + + /// Round badge with a white glyph (outline + ink glyph when disabled). + pub(super) fn badge(&self, c: P2, r: f32, fill: C, glyph: Glyph) { + let gc = if self.dis { + self.circle(c, r, N, fill); + K + } else { + self.circle(c, r, fill, N); + W + }; + let g = r * 0.5; + let w = 0.95; + match glyph { + Glyph::Plus => { + self.seg_w((c.0 - g, c.1), (c.0 + g, c.1), gc, w); + self.seg_w((c.0, c.1 - g), (c.0, c.1 + g), gc, w); + } + Glyph::Cross => { + let g = g * 0.85; + self.seg_w((c.0 - g, c.1 - g), (c.0 + g, c.1 + g), gc, w); + self.seg_w((c.0 + g, c.1 - g), (c.0 - g, c.1 + g), gc, w); + } + Glyph::Check => { + self.line_w(&[(c.0 - g, c.1 + 0.1), (c.0 - g * 0.25, c.1 + g * 0.75), (c.0 + g, c.1 - g * 0.6)], gc, w); + } + } + } + + /// Landscape slide frame. + pub(super) fn frame(&self, x0: f32, y0: f32, x1: f32, y1: f32) { + self.rect(x0, y0, x1, y1, 1.5, N, K); + } + + /// Document page with a folded top-right corner. + pub(super) fn page(&self, x0: f32, y0: f32, x1: f32, y1: f32, fill: C, stroke: C) { + let f = ((x1 - x0) * 0.32).min(5.0); + self.rshape(&[(x0, y0), (x1 - f, y0), (x1, y0 + f), (x1, y1), (x0, y1)], 1.2, fill, stroke); + self.line(&[(x1 - f, y0), (x1 - f, y0 + f), (x1, y0 + f)], stroke); + } + + /// Picture frame with sun and hills. + pub(super) fn picture(&self, x0: f32, y0: f32, x1: f32, y1: f32) { + let (w, h) = (x1 - x0, y1 - y0); + let m = w.min(h); + self.rect(x0, y0, x1, y1, 1.5, N, K); + self.dot((x1 - w * 0.27, y0 + h * 0.3), m * 0.12, Y); + let base = y1 - h * 0.14; + self.poly(&[(x0 + w * 0.12, base), (x0 + w * 0.38, y0 + h * 0.36), (x0 + w * 0.64, base)], G, N); + self.poly(&[(x0 + w * 0.48, base), (x0 + w * 0.68, y0 + h * 0.56), (x1 - w * 0.12, base)], G.a(0.7), N); + } + + pub(super) fn magnifier(&self, c: P2, r: f32, lens_fill: C, col: C) { + self.circle(c, r, lens_fill, col); + let d = r * 0.707; + self.seg_w((c.0 + d + 0.6, c.1 + d + 0.6), (c.0 + r * 1.55, c.1 + r * 1.55), col, 1.5); + } + + fn bubble_pts(x0: f32, y0: f32, x1: f32, y1: f32) -> Vec { + let r = 2.5; + let mut pts = arc_pts((x0 + r, y0 + r), r, r, 180.0, 270.0); + pts.extend(arc_pts((x1 - r, y0 + r), r, r, 270.0, 360.0)); + pts.extend(arc_pts((x1 - r, y1 - r), r, r, 0.0, 90.0)); + let tw = ((x1 - x0) * 0.22).clamp(2.5, 4.5); + pts.push((x0 + r + 0.8 + tw, y1)); + pts.push((x0 + r - 0.2, y1 + 3.8)); + pts.push((x0 + r + 0.8, y1)); + pts.extend(arc_pts((x0 + r, y1 - r), r, r, 90.0, 180.0)); + pts + } + + pub(super) fn bubble(&self, x0: f32, y0: f32, x1: f32, y1: f32, fill: C, stroke: C) { + self.shape(&Self::bubble_pts(x0, y0, x1, y1), fill, stroke); + } + + pub(super) fn person(&self, cx: f32, top: f32, s: f32, fill: C) { + self.dot((cx, top + 2.6 * s), 2.6 * s, fill); + let base = top + 12.0 * s; + self.poly(&arc_pts((cx, base), 5.2 * s, 5.0 * s, 180.0, 360.0), fill, N); + } + + pub(super) fn star(&self, c: P2, ro: f32, fill: C, stroke: C) { + self.shape(&star_pts(c, ro, ro * 0.47, 5, 0.0), fill, stroke); + } + + pub(super) fn sparkle(&self, c: P2, r: f32, fill: C) { + self.shape(&star_pts(c, r, r * 0.3, 4, 0.0), fill, N); + } + + pub(super) fn gear(&self, c: P2, ro: f32, ri: f32, teeth: usize, fill: C, stroke: C) { + let step = 360.0 / teeth as f32; + let mut pts = Vec::with_capacity(teeth * 4); + for i in 0..teeth { + let base = i as f32 * step; + for (a, r) in [(-0.3, ri), (-0.17, ro), (0.17, ro), (0.3, ri)] { + let ang = rad(base + a * step); + pts.push((c.0 + r * ang.cos(), c.1 + r * ang.sin())); + } + } + self.shape(&pts, fill, stroke); + } + + pub(super) fn speaker(&self, x: f32, cy: f32, fill: C, stroke: C) { + self.rshape( + &[(x, cy - 3.0), (x + 3.8, cy - 3.0), (x + 8.5, cy - 7.5), (x + 8.5, cy + 7.5), (x + 3.8, cy + 3.0), (x, cy + 3.0)], + 0.8, + fill, + stroke, + ); + } + + /// Table grid; `header` fills the first row. + pub(super) fn table(&self, x0: f32, y0: f32, x1: f32, y1: f32, rows: usize, cols: usize, header: C) { + let rh = (y1 - y0) / rows as f32; + let cw = (x1 - x0) / cols as f32; + if !matches!(header, C::None) { + self.rect(x0, y0, x1, y0 + rh, 1.2, header, N); + } + for i in 1..rows { + let y = y0 + rh * i as f32; + self.seg_w((x0, y), (x1, y), K, 0.8); + } + for j in 1..cols { + let x = x0 + cw * j as f32; + self.seg_w((x, y0), (x, y1), K, 0.8); + } + self.rect(x0, y0, x1, y1, 1.2, N, K); + } + + /// Bar chart in the box. + pub(super) fn bars(&self, x0: f32, y0: f32, x1: f32, y1: f32) { + let w = (x1 - x0) / 3.0; + let h = y1 - y0; + for (i, (frac, c)) in [(0.5, B), (1.0, O), (0.72, G)].into_iter().enumerate() { + let bx = x0 + w * i as f32; + self.rect(bx + w * 0.16, y1 - h * frac, bx + w * 0.84, y1, 0.6, c, N); + } + self.seg((x0 - 0.5, y1 + 0.2), (x1 + 0.5, y1 + 0.2), K); + } + + /// A writing tool lying along `ang` degrees with its point at `tip`. + pub(super) fn tool(&self, tip: P2, ang: f32, len: f32, hw: f32, cone: f32, body: C, lead: C, cap_len: f32, cap: C) { + let m = |pts: &[P2]| tf(tip, ang, pts); + self.poly(&m(&[(0.0, 0.0), (cone, -hw), (cone, hw)]), N, K); + self.poly(&m(&[(0.0, 0.0), (cone * 0.42, -hw * 0.42), (cone * 0.42, hw * 0.42)]), lead, N); + self.poly(&m(&[(cone, -hw), (len - cap_len, -hw), (len - cap_len, hw), (cone, hw)]), body, K); + if cap_len > 0.0 { + self.poly(&m(&[(len - cap_len, -hw), (len, -hw), (len, hw), (len - cap_len, hw)]), cap, K); + } + } + + /// Paint brush lying along `ang` with bristle tip at `tip`. + pub(super) fn brush(&self, tip: P2, ang: f32, len: f32, bristle: C) { + let m = |pts: &[P2]| tf(tip, ang, pts); + self.shape(&m(&[(0.0, -0.4), (2.5, -2.2), (5.5, -2.2), (5.5, 2.2), (2.5, 2.2), (0.0, 0.4)]), bristle, K); + self.poly(&m(&[(5.5, -2.0), (7.8, -2.0), (7.8, 2.0), (5.5, 2.0)]), K, K); + self.line(&m(&[(7.8, -1.1), (len, -1.3), (len, 1.3), (7.8, 1.1)]), K); + } + + /// Small path-drawn digit 1–3 in a `3 × h` box. + pub(super) fn digit(&self, d: u8, x: f32, y: f32, h: f32, c: C) { + let w = 0.8; + match d { + 1 => self.line_w(&[(x + 0.4, y + 1.2), (x + 2.0, y), (x + 2.0, y + h)], c, w), + 2 => self.line_w(&[(x, y + 1.0), (x + 0.7, y), (x + 2.4, y), (x + 3.1, y + 1.1), (x + 2.9, y + 2.2), (x, y + h), (x + 3.2, y + h)], c, w), + _ => self.line_w( + &[(x, y), (x + 3.0, y), (x + 1.3, y + h * 0.42), (x + 2.6, y + h * 0.5), (x + 3.1, y + h * 0.7), (x + 2.5, y + h), (x, y + h)], + c, + w, + ), + } + } + + /// Path-drawn capital A. + pub(super) fn letter_a(&self, x0: f32, top: f32, x1: f32, bottom: f32, c: C, w: f32) { + let mx = (x0 + x1) * 0.5; + self.line_w(&[(x0, bottom), (mx, top), (x1, bottom)], c, w); + let f = 0.62; + let y = top + (bottom - top) * f; + let dx = (x1 - x0) * 0.5 * f; + self.seg_w((mx - dx, y), (mx + dx, y), c, w); + } + + /// Hand-drawn scribble in the box. + pub(super) fn scribble(&self, x0: f32, y0: f32, x1: f32, y1: f32, c: C) { + let (w, h) = (x1 - x0, y1 - y0); + let p = |u: f32, v: f32| (x0 + w * u, y0 + h * v); + // A loopy cursive stroke (prolate cycloid), normalised into the box. + let raw: Vec = (0..=48) + .map(|i| { + let th = i as f32 / 48.0 * 4.0 * PI + 0.6; + (th - 1.9 * th.sin(), -1.9 * th.cos()) + }) + .collect(); + let (mut lx, mut hx, mut ly, mut hy) = (f32::MAX, f32::MIN, f32::MAX, f32::MIN); + for &(x, y) in &raw { + lx = lx.min(x); + hx = hx.max(x); + ly = ly.min(y); + hy = hy.max(y); + } + let sx = (hx - lx).max(1e-3); + let sy = (hy - ly).max(1e-3); + let pts: Vec = raw.iter().map(|&(x, y)| p((x - lx) / sx, (y - ly) / sy)).collect(); + self.line(&pts, c); + } + + /// Isometric "layer" diamond. + pub(super) fn layer(&self, cx: f32, cy: f32, hw: f32, hh: f32, fill: C, stroke: C) { + self.rshape(&[(cx, cy - hh), (cx + hw, cy), (cx, cy + hh), (cx - hw, cy)], 0.8, fill, stroke); + } + + /// Open book. + pub(super) fn book(&self, fill: C, col: C) { + let left = { + let mut p = quad_pts((12.0, 7.0), (7.5, 4.0), (2.0, 5.0), 8); + p.extend(quad_pts((2.0, 18.5), (7.5, 17.5), (12.0, 20.5), 8)); + p + }; + let right = { + let mut p = quad_pts((12.0, 7.0), (16.5, 4.0), (22.0, 5.0), 8); + p.extend(quad_pts((22.0, 18.5), (16.5, 17.5), (12.0, 20.5), 8)); + p + }; + self.shape(&left, fill, col); + self.shape(&right, fill, col); + } + + pub(super) fn monitor(&self, x0: f32, y0: f32, x1: f32, y1: f32) { + self.rect(x0, y0, x1, y1, 1.5, N, K); + let cx = (x0 + x1) * 0.5; + self.seg((cx, y1), (cx, y1 + 3.2), K); + self.seg((cx - 4.0, y1 + 3.6), (cx + 4.0, y1 + 3.6), K); + } + + pub(super) fn clock_badge(&self, c: P2, r: f32, fill: C) { + let hc = if self.dis { + self.circle(c, r, N, fill); + K + } else { + self.circle(c, r, fill, N); + W + }; + self.line_w(&[(c.0, c.1 - r * 0.6), c, (c.0 + r * 0.5, c.1 + r * 0.2)], hc, 0.9); + } + + pub(super) fn gear_badge(&self, c: P2, r: f32, fill: C) { + self.gear(c, r, r * 0.74, 8, fill, N); + if self.dis { + self.gear(c, r, r * 0.74, 8, N, fill); + } + self.dot(c, r * 0.32, if self.dis { N } else { W }); + if self.dis { + self.circle(c, r * 0.32, N, fill); + } + } + + pub(super) fn lock(&self, x0: f32, y0: f32, x1: f32, y1: f32) { + let cx = (x0 + x1) * 0.5; + let r = (x1 - x0) * 0.3; + let mut sh = vec![(cx - r, y0)]; + sh.extend(arc_pts((cx, y0 - r * 0.7), r, r, 180.0, 360.0)); + sh.push((cx + r, y0)); + self.line(&sh, K); + self.rect(x0, y0, x1, y1, 1.2, Y, K); + let ky = (y0 + y1) * 0.5; + self.dot((cx, ky - 0.4), 1.1, D); + self.seg_w((cx, ky), (cx, ky + 2.0), D, 0.9); + } + + pub(super) fn cursor(&self, x: f32, y: f32, s: f32) { + let p = |u: f32, v: f32| (x + u * s, y + v * s); + let pts = [p(0.0, 0.0), p(0.0, 15.0), p(3.8, 11.6), p(6.4, 17.0), p(8.6, 16.0), p(6.1, 10.8), p(11.0, 10.8)]; + self.shape(&pts, K, K); + } +}