/* ArozOS Office - Slides shape catalogue ====================================== Every shape the editor can draw, as geometry rather than as a picture. The names are the ones PresentationML uses for its preset geometries (``), so an imported deck keeps its own vocabulary and the converters on the Go side are a lookup rather than a translation. A handful of older editor names (`round`, `arrow`, `star`, ...) are kept as aliases, because documents already written carry them. Each shape produces an SVG path, and deliberately only ever uses M, L, C and Z: - `clip-path: path(...)` takes it, which is how a picture is cropped to a shape, - the PDF exporter turns it into path operators with a translator of about thirty lines (slides_pdf.js), and - there is nothing to get wrong in an arc flag. Circles and arcs therefore arrive as cubic curves; `arc()` below does that conversion once so no shape has to think about it. Some shapes have a second path - the divider bars of a predefined process, the cross of a summing junction. Those are *drawn*, not filled: they are the shape's markings, not its outline, so they come back separately from `detail()` and the renderer strokes them. Usage: SlidesShapes.path(kind, w, h) // "M0 0L100 0..." ("" if unknown) SlidesShapes.detail(kind, w, h) // markings to stroke, or "" SlidesShapes.points(kind, w, h) // polygon corners, or null SlidesShapes.evenOdd(kind) // does the path need even-odd fill SlidesShapes.canonical(kind) // legacy name -> catalogue name SlidesShapes.has(kind) SlidesShapes.label(kind) SlidesShapes.CATEGORIES // [{ id, label, kinds: [...] }] */ var SlidesShapes = (function () { "use strict"; function r2(v) { return Math.round(v * 1000) / 1000; } /* ---------------- the path builder ---------------- */ function Path() { this.s = ""; } Path.prototype.M = function (x, y) { this.s += "M" + r2(x) + " " + r2(y); return this; }; Path.prototype.L = function (x, y) { this.s += "L" + r2(x) + " " + r2(y); return this; }; Path.prototype.C = function (x1, y1, x2, y2, x, y) { this.s += "C" + r2(x1) + " " + r2(y1) + " " + r2(x2) + " " + r2(y2) + " " + r2(x) + " " + r2(y); return this; }; Path.prototype.Z = function () { this.s += "Z"; return this; }; Path.prototype.poly = function (pts) { for (var i = 0; i < pts.length; i++) { if (i === 0) this.M(pts[i][0], pts[i][1]); else this.L(pts[i][0], pts[i][1]); } return this.Z(); }; /* arc appends an elliptical arc from a0 to a1 (radians, y down). It moves to the start when the path is empty or `move` is asked for, and otherwise draws a line to it first, which is what every shape below wants. Cut into quarter turns, each a cubic - the standard approximation, exact to about one part in ten thousand. */ Path.prototype.arc = function (cx, cy, rx, ry, a0, a1, move) { var span = a1 - a0; var steps = Math.max(1, Math.ceil(Math.abs(span) / (Math.PI / 2))); var step = span / steps; var k = 4 / 3 * Math.tan(step / 4); var a = a0; var x0 = cx + rx * Math.cos(a), y0 = cy + ry * Math.sin(a); if (move || this.s === "") this.M(x0, y0); else this.L(x0, y0); for (var i = 0; i < steps; i++) { var b = a + step; var cosA = Math.cos(a), sinA = Math.sin(a); var cosB = Math.cos(b), sinB = Math.sin(b); var p0x = cx + rx * cosA, p0y = cy + ry * sinA; var p3x = cx + rx * cosB, p3y = cy + ry * sinB; this.C(p0x - k * rx * sinA, p0y + k * ry * cosA, p3x + k * rx * sinB, p3y - k * ry * cosB, p3x, p3y); a = b; } return this; }; Path.prototype.circle = function (cx, cy, rx, ry) { return this.arc(cx, cy, rx, ry, 0, 2 * Math.PI, true).Z(); }; Path.prototype.toString = function () { return this.s; }; function P() { return new Path(); } // regular polygon inscribed in the box, first corner at the top function ngon(w, h, n, rotate) { var pts = []; var cx = w / 2, cy = h / 2; var off = (rotate || 0) - Math.PI / 2; for (var i = 0; i < n; i++) { var a = off + i * 2 * Math.PI / n; pts.push([cx + cx * Math.cos(a), cy + cy * Math.sin(a)]); } return pts; } // a star with n points; inner is the inner radius as a fraction function starPts(w, h, n, inner) { var pts = []; var cx = w / 2, cy = h / 2; for (var i = 0; i < n * 2; i++) { var a = -Math.PI / 2 + i * Math.PI / n; var f = (i % 2 === 0) ? 1 : inner; pts.push([cx + cx * f * Math.cos(a), cy + cy * f * Math.sin(a)]); } return pts; } // rounded rectangle, with a radius per corner (tl, tr, br, bl) function roundRectPath(w, h, tl, tr, br, bl) { var p = P(); p.M(tl, 0).L(w - tr, 0); if (tr) p.arc(w - tr, tr, tr, tr, -Math.PI / 2, 0); p.L(w, h - br); if (br) p.arc(w - br, h - br, br, br, 0, Math.PI / 2); p.L(bl, h); if (bl) p.arc(bl, h - bl, bl, bl, Math.PI / 2, Math.PI); p.L(0, tl); if (tl) p.arc(tl, tl, tl, tl, Math.PI, Math.PI * 1.5); return p.Z().toString(); } // the straight-sided arrow every four directions share, drawn right // and then mapped onto the direction asked for function arrowPts(w, h, headLen, shaft) { var hx = w * (1 - headLen); var t = h * (1 - shaft) / 2; return [[0, t], [hx, t], [hx, 0], [w, h / 2], [hx, h], [hx, h - t], [0, h - t]]; } function mapPts(pts, fn) { return pts.map(fn); } function rotPts(pts, w, h, quarter) { // quarter turns clockwise about the centre of a w x h box, with the // box itself turning too - so an arrow drawn right becomes one // drawn down in the same box return mapPts(pts, function (p) { var x = p[0] / w, y = p[1] / h; if (quarter === 1) return [(1 - y) * w, x * h]; if (quarter === 2) return [(1 - x) * w, (1 - y) * h]; if (quarter === 3) return [y * w, (1 - x) * h]; return [x * w, y * h]; }); } /* ---------------- the catalogue ---------------- */ /* Each entry is either `pts` (a polygon, which also answers points()) or `path`. `detail` is stroked rather than filled; `evenOdd` says the path has holes in it. */ var DEFS = { /* ---------- basic shapes ---------- */ rect: { cat: "shape", label: "Rectangle", pts: function (w, h) { return [[0, 0], [w, 0], [w, h], [0, h]]; } }, roundRect: { cat: "shape", label: "Rounded rectangle", path: function (w, h) { var r = Math.min(w, h) * 0.15; return roundRectPath(w, h, r, r, r, r); } }, snip1Rect: { cat: "shape", label: "Snip single corner", pts: function (w, h) { var s = Math.min(w, h) * 0.18; return [[0, 0], [w - s, 0], [w, s], [w, h], [0, h]]; } }, snip2SameRect: { cat: "shape", label: "Snip same side corners", pts: function (w, h) { var s = Math.min(w, h) * 0.18; return [[s, 0], [w - s, 0], [w, s], [w, h], [0, h], [0, s]]; } }, snip2DiagRect: { cat: "shape", label: "Snip diagonal corners", pts: function (w, h) { var s = Math.min(w, h) * 0.18; return [[s, 0], [w, 0], [w, h - s], [w - s, h], [0, h], [0, s]]; } }, round1Rect: { cat: "shape", label: "Round single corner", path: function (w, h) { var r = Math.min(w, h) * 0.28; return roundRectPath(w, h, 0, r, 0, 0); } }, round2SameRect: { cat: "shape", label: "Round same side corners", path: function (w, h) { var r = Math.min(w, h) * 0.28; return roundRectPath(w, h, r, r, 0, 0); } }, round2DiagRect: { cat: "shape", label: "Round diagonal corners", path: function (w, h) { var r = Math.min(w, h) * 0.28; return roundRectPath(w, h, r, 0, r, 0); } }, ellipse: { cat: "shape", label: "Ellipse", path: function (w, h) { return P().circle(w / 2, h / 2, w / 2, h / 2).toString(); } }, triangle: { cat: "shape", label: "Triangle", pts: function (w, h) { return [[w / 2, 0], [w, h], [0, h]]; } }, rtTriangle: { cat: "shape", label: "Right triangle", pts: function (w, h) { return [[0, 0], [w, h], [0, h]]; } }, parallelogram: { cat: "shape", label: "Parallelogram", pts: function (w, h) { return [[w * 0.25, 0], [w, 0], [w * 0.75, h], [0, h]]; } }, trapezoid: { cat: "shape", label: "Trapezoid", pts: function (w, h) { return [[w * 0.25, 0], [w * 0.75, 0], [w, h], [0, h]]; } }, diamond: { cat: "shape", label: "Diamond", pts: function (w, h) { return [[w / 2, 0], [w, h / 2], [w / 2, h], [0, h / 2]]; } }, pentagon: { cat: "shape", label: "Pentagon", pts: function (w, h) { return ngon(w, h, 5); } }, hexagon: { cat: "shape", label: "Hexagon", pts: function (w, h) { return [[w * 0.25, 0], [w * 0.75, 0], [w, h / 2], [w * 0.75, h], [w * 0.25, h], [0, h / 2]]; } }, heptagon: { cat: "shape", label: "Heptagon", pts: function (w, h) { return ngon(w, h, 7); } }, octagon: { cat: "shape", label: "Octagon", pts: function (w, h) { var s = Math.min(w, h) * 0.29; return [[s, 0], [w - s, 0], [w, s], [w, h - s], [w - s, h], [s, h], [0, h - s], [0, s]]; } }, decagon: { cat: "shape", label: "Decagon", pts: function (w, h) { return ngon(w, h, 10); } }, dodecagon: { cat: "shape", label: "Dodecagon", pts: function (w, h) { return ngon(w, h, 12); } }, plus: { cat: "shape", label: "Cross", pts: function (w, h) { return [[w * 0.35, 0], [w * 0.65, 0], [w * 0.65, h * 0.35], [w, h * 0.35], [w, h * 0.65], [w * 0.65, h * 0.65], [w * 0.65, h], [w * 0.35, h], [w * 0.35, h * 0.65], [0, h * 0.65], [0, h * 0.35], [w * 0.35, h * 0.35]]; } }, teardrop: { cat: "shape", label: "Teardrop", // three quarters of a circle, the last quarter pulled out to the // corner it points at path: function (w, h) { return P().arc(w / 2, h / 2, w / 2, h / 2, 0, Math.PI * 1.5, true) .L(w, 0).Z().toString(); } }, frame: { cat: "shape", label: "Frame", evenOdd: true, path: function (w, h) { var t = Math.min(w, h) * 0.14; return P().poly([[0, 0], [w, 0], [w, h], [0, h]]).toString() + P().poly([[t, t], [t, h - t], [w - t, h - t], [w - t, t]]).toString(); } }, halfFrame: { cat: "shape", label: "Half frame", pts: function (w, h) { var t = Math.min(w, h) * 0.16; return [[0, 0], [w, 0], [w - t, t], [t, t], [t, h - t], [0, h]]; } }, corner: { cat: "shape", label: "L shape", pts: function (w, h) { var t = Math.min(w, h) * 0.33; return [[0, 0], [t, 0], [t, h - t], [w, h - t], [w, h], [0, h]]; } }, diagStripe: { cat: "shape", label: "Diagonal stripe", pts: function (w, h) { return [[0, h * 0.5], [w * 0.5, 0], [w, 0], [0, h]]; } }, plaque: { cat: "shape", label: "Plaque", path: function (w, h) { var r = Math.min(w, h) * 0.16; var p = P(); p.M(r, 0).L(w - r, 0); p.arc(w, 0, r, r, Math.PI * 0.5, Math.PI); p.L(w, h - r); p.arc(w, h, r, r, Math.PI, Math.PI * 1.5); p.L(r, h); p.arc(0, h, r, r, Math.PI * 1.5, Math.PI * 2); p.L(0, r); p.arc(0, 0, r, r, 0, Math.PI * 0.5); return p.Z().toString(); } }, can: { cat: "shape", label: "Cylinder", path: function (w, h) { var ry = Math.min(h * 0.18, w * 0.5); var p = P(); p.M(0, ry).arc(w / 2, ry, w / 2, ry, Math.PI, Math.PI * 2) .L(w, h - ry) .arc(w / 2, h - ry, w / 2, ry, 0, Math.PI) .Z(); return p.toString(); }, detail: function (w, h) { var ry = Math.min(h * 0.18, w * 0.5); return P().arc(w / 2, ry, w / 2, ry, 0, Math.PI, true).toString(); } }, cube: { cat: "shape", label: "Cube", path: function (w, h) { var d = Math.min(w, h) * 0.25; return P().poly([[0, d], [d, 0], [w, 0], [w, h - d], [w - d, h], [0, h]]).toString(); }, detail: function (w, h) { var d = Math.min(w, h) * 0.25; return P().M(0, d).L(w - d, d).L(w, 0).toString() + P().M(w - d, d).L(w - d, h).toString(); } }, bevel: { cat: "shape", label: "Bevel", evenOdd: true, path: function (w, h) { var t = Math.min(w, h) * 0.14; return P().poly([[0, 0], [w, 0], [w, h], [0, h]]).toString() + P().poly([[t, t], [w - t, t], [w - t, h - t], [t, h - t]]).toString(); }, detail: function (w, h) { var t = Math.min(w, h) * 0.14; return P().M(0, 0).L(t, t).toString() + P().M(w, 0).L(w - t, t).toString() + P().M(w, h).L(w - t, h - t).toString() + P().M(0, h).L(t, h - t).toString(); } }, donut: { cat: "shape", label: "Donut", evenOdd: true, path: function (w, h) { return P().circle(w / 2, h / 2, w / 2, h / 2).toString() + P().circle(w / 2, h / 2, w * 0.3, h * 0.3).toString(); } }, noSmoking: { cat: "shape", label: "\"No\" symbol", /* Not even-odd: the ring's hole comes from winding the inner circle the other way, which leaves the bar free to fill right across it. Even-odd would punch the bar out again wherever it crosses the ring. */ path: function (w, h) { var t = Math.min(w, h) * 0.09; var a = Math.PI / 4; var dx = t * Math.sin(a), dy = t * Math.cos(a); var rx = w * 0.36, ry = h * 0.36; var x0 = w / 2 - rx * Math.cos(a), y0 = h / 2 + ry * Math.sin(a); var x1 = w / 2 + rx * Math.cos(a), y1 = h / 2 - ry * Math.sin(a); return P().circle(w / 2, h / 2, w / 2, h / 2).toString() + P().arc(w / 2, h / 2, rx, ry, 2 * Math.PI, 0, true).Z().toString() + P().poly([[x0 - dx, y0 - dy], [x1 - dx, y1 - dy], [x1 + dx, y1 + dy], [x0 + dx, y0 + dy]]).toString(); } }, blockArc: { cat: "shape", label: "Block arc", path: function (w, h) { var a0 = Math.PI, a1 = 2 * Math.PI; var p = P(); p.arc(w / 2, h / 2, w / 2, h / 2, a0, a1, true); p.arc(w / 2, h / 2, w * 0.3, h * 0.3, a1, a0); return p.Z().toString(); } }, foldedCorner: { cat: "shape", label: "Folded corner", path: function (w, h) { var f = Math.min(w, h) * 0.22; return P().poly([[0, 0], [w, 0], [w, h - f], [w - f, h], [0, h]]).toString(); }, detail: function (w, h) { var f = Math.min(w, h) * 0.22; return P().M(w, h - f).L(w - f, h - f).L(w - f, h).toString(); } }, smileyFace: { cat: "shape", label: "Smiley face", evenOdd: true, path: function (w, h) { var eyeR = Math.min(w, h) * 0.055; var mouth = P(); mouth.arc(w / 2, h * 0.44, w * 0.29, h * 0.29, Math.PI * 0.22, Math.PI * 0.78, true); mouth.arc(w / 2, h * 0.40, w * 0.29, h * 0.29, Math.PI * 0.78, Math.PI * 0.22); return P().circle(w / 2, h / 2, w / 2, h / 2).toString() + P().circle(w * 0.33, h * 0.36, eyeR, eyeR).toString() + P().circle(w * 0.67, h * 0.36, eyeR, eyeR).toString() + mouth.Z().toString(); } }, heart: { cat: "shape", label: "Heart", path: function (w, h) { return P().M(w / 2, h) .C(w * -0.06, h * 0.6, w * 0.08, h * -0.1, w / 2, h * 0.24) .C(w * 0.92, h * -0.1, w * 1.06, h * 0.6, w / 2, h) .Z().toString(); } }, lightningBolt: { cat: "shape", label: "Lightning bolt", pts: function (w, h) { return [[w * 0.44, 0], [w * 0.84, h * 0.40], [w * 0.60, h * 0.44], [w * 0.93, h * 0.75], [w * 0.68, h * 0.74], [w * 0.83, h], [w * 0.30, h * 0.56], [w * 0.53, h * 0.52], [w * 0.19, h * 0.24], [w * 0.42, h * 0.26], [w * 0.16, h * 0.03]]; } }, sun: { cat: "shape", label: "Sun", // eight rays off a disc: the ring of "mid" points is the disc pts: function (w, h) { var cx = w / 2, cy = h / 2, mid = 0.62; var pts = []; for (var i = 0; i < 8; i++) { var a = -Math.PI / 2 + i * Math.PI / 4; var a1 = a - Math.PI / 10, a2 = a + Math.PI / 10; pts.push([cx + cx * mid * Math.cos(a1), cy + cy * mid * Math.sin(a1)]); pts.push([cx + cx * Math.cos(a), cy + cy * Math.sin(a)]); pts.push([cx + cx * mid * Math.cos(a2), cy + cy * mid * Math.sin(a2)]); } return pts; } }, moon: { cat: "shape", label: "Crescent moon", path: function (w, h) { return P().M(w, 0) .C(w * 0.2, h * 0.1, w * 0.2, h * 0.9, w, h) .C(w * 0.55, h * 0.78, w * 0.55, h * 0.22, w, 0) .Z().toString(); } }, cloud: { cat: "shape", label: "Cloud", path: function (w, h) { var p = P(); p.M(w * 0.22, h * 0.92) .C(w * 0.02, h * 0.92, w * -0.05, h * 0.58, w * 0.13, h * 0.5) .C(w * 0.06, h * 0.28, w * 0.26, h * 0.1, w * 0.42, h * 0.2) .C(w * 0.5, h * -0.03, w * 0.8, h * -0.03, w * 0.86, h * 0.22) .C(w * 1.04, h * 0.26, w * 1.04, h * 0.6, w * 0.87, h * 0.66) .C(w * 0.93, h * 0.88, w * 0.7, h * 1.02, w * 0.57, h * 0.9) .C(w * 0.48, h * 1.0, w * 0.3, h * 1.0, w * 0.22, h * 0.92) .Z(); return p.toString(); } }, arc: { cat: "shape", label: "Arc", open: true, path: function (w, h) { return P().arc(w / 2, h / 2, w / 2, h / 2, Math.PI * 1.5, Math.PI * 2, true).toString(); } }, chord: { cat: "shape", label: "Chord", path: function (w, h) { return P().arc(w / 2, h / 2, w / 2, h / 2, Math.PI * 1.25, Math.PI * 2.25, true) .Z().toString(); } }, pie: { cat: "shape", label: "Pie", path: function (w, h) { return P().M(w / 2, h / 2) .arc(w / 2, h / 2, w / 2, h / 2, Math.PI * 1.5, Math.PI * 3) .Z().toString(); } }, /* ---------- flow chart ---------- */ flowChartProcess: { cat: "shape", label: "Flowchart: process", pts: function (w, h) { return [[0, 0], [w, 0], [w, h], [0, h]]; } }, flowChartAlternateProcess: { cat: "shape", label: "Flowchart: alternate process", path: function (w, h) { var r = Math.min(w, h) * 0.17; return roundRectPath(w, h, r, r, r, r); } }, flowChartDecision: { cat: "shape", label: "Flowchart: decision", pts: function (w, h) { return [[w / 2, 0], [w, h / 2], [w / 2, h], [0, h / 2]]; } }, flowChartInputOutput: { cat: "shape", label: "Flowchart: data", pts: function (w, h) { return [[w * 0.2, 0], [w, 0], [w * 0.8, h], [0, h]]; } }, flowChartPredefinedProcess: { cat: "shape", label: "Flowchart: predefined process", pts: function (w, h) { return [[0, 0], [w, 0], [w, h], [0, h]]; }, detail: function (w, h) { return P().M(w * 0.12, 0).L(w * 0.12, h).toString() + P().M(w * 0.88, 0).L(w * 0.88, h).toString(); } }, flowChartInternalStorage: { cat: "shape", label: "Flowchart: internal storage", pts: function (w, h) { return [[0, 0], [w, 0], [w, h], [0, h]]; }, detail: function (w, h) { return P().M(w * 0.14, 0).L(w * 0.14, h).toString() + P().M(0, h * 0.14).L(w, h * 0.14).toString(); } }, flowChartDocument: { cat: "shape", label: "Flowchart: document", path: function (w, h) { return P().M(0, 0).L(w, 0).L(w, h * 0.83) .C(w * 0.72, h * 1.07, w * 0.28, h * 0.6, 0, h * 0.86) .Z().toString(); } }, flowChartMultidocument: { cat: "shape", label: "Flowchart: multidocument", path: function (w, h) { return P().M(0, h * 0.13).L(w * 0.9, h * 0.13).L(w * 0.9, h * 0.9) .C(w * 0.65, h * 1.1, w * 0.25, h * 0.68, 0, h * 0.92).Z().toString() + P().M(w * 0.05, h * 0.07).L(w * 0.95, h * 0.07).L(w * 0.95, h * 0.8) .L(w * 0.9, h * 0.8).L(w * 0.9, h * 0.13).L(w * 0.05, h * 0.13).Z().toString() + P().M(w * 0.1, 0).L(w, 0).L(w, h * 0.72).L(w * 0.95, h * 0.72) .L(w * 0.95, h * 0.07).L(w * 0.1, h * 0.07).Z().toString(); } }, flowChartTerminator: { cat: "shape", label: "Flowchart: terminator", path: function (w, h) { var r = Math.min(w / 2, h / 2); return roundRectPath(w, h, r, r, r, r); } }, flowChartPreparation: { cat: "shape", label: "Flowchart: preparation", pts: function (w, h) { return [[w * 0.2, 0], [w * 0.8, 0], [w, h / 2], [w * 0.8, h], [w * 0.2, h], [0, h / 2]]; } }, flowChartManualInput: { cat: "shape", label: "Flowchart: manual input", pts: function (w, h) { return [[0, h * 0.25], [w, 0], [w, h], [0, h]]; } }, flowChartManualOperation: { cat: "shape", label: "Flowchart: manual operation", pts: function (w, h) { return [[0, 0], [w, 0], [w * 0.8, h], [w * 0.2, h]]; } }, flowChartConnector: { cat: "shape", label: "Flowchart: connector", path: function (w, h) { return P().circle(w / 2, h / 2, w / 2, h / 2).toString(); } }, flowChartOffpageConnector: { cat: "shape", label: "Flowchart: off-page connector", pts: function (w, h) { return [[0, 0], [w, 0], [w, h * 0.8], [w / 2, h], [0, h * 0.8]]; } }, flowChartPunchedCard: { cat: "shape", label: "Flowchart: card", pts: function (w, h) { var s = Math.min(w, h) * 0.22; return [[s, 0], [w, 0], [w, h], [0, h], [0, s]]; } }, flowChartPunchedTape: { cat: "shape", label: "Flowchart: punched tape", path: function (w, h) { return P().M(0, h * 0.12) .C(w * 0.25, h * -0.1, w * 0.75, h * 0.34, w, h * 0.12) .L(w, h * 0.88) .C(w * 0.75, h * 1.1, w * 0.25, h * 0.66, 0, h * 0.88) .Z().toString(); } }, flowChartSummingJunction: { cat: "shape", label: "Flowchart: summing junction", path: function (w, h) { return P().circle(w / 2, h / 2, w / 2, h / 2).toString(); }, detail: function (w, h) { var k = 0.1464; // where the diagonal meets the circle return P().M(w * k, h * k).L(w * (1 - k), h * (1 - k)).toString() + P().M(w * (1 - k), h * k).L(w * k, h * (1 - k)).toString(); } }, flowChartOr: { cat: "shape", label: "Flowchart: or", path: function (w, h) { return P().circle(w / 2, h / 2, w / 2, h / 2).toString(); }, detail: function (w, h) { return P().M(w / 2, 0).L(w / 2, h).toString() + P().M(0, h / 2).L(w, h / 2).toString(); } }, flowChartCollate: { cat: "shape", label: "Flowchart: collate", pts: function (w, h) { return [[0, 0], [w, 0], [0, h], [w, h]]; } }, flowChartSort: { cat: "shape", label: "Flowchart: sort", pts: function (w, h) { return [[w / 2, 0], [w, h / 2], [w / 2, h], [0, h / 2]]; }, detail: function (w, h) { return P().M(0, h / 2).L(w, h / 2).toString(); } }, flowChartExtract: { cat: "shape", label: "Flowchart: extract", pts: function (w, h) { return [[w / 2, 0], [w, h], [0, h]]; } }, flowChartMerge: { cat: "shape", label: "Flowchart: merge", pts: function (w, h) { return [[0, 0], [w, 0], [w / 2, h]]; } }, flowChartDelay: { cat: "shape", label: "Flowchart: delay", path: function (w, h) { var r = Math.min(w / 2, h / 2); return roundRectPath(w, h, 0, r, r, 0); } }, flowChartMagneticDisk: { cat: "shape", label: "Flowchart: stored data", path: function (w, h) { var ry = Math.min(h * 0.18, w * 0.5); return P().M(0, ry).arc(w / 2, ry, w / 2, ry, Math.PI, Math.PI * 2) .L(w, h - ry).arc(w / 2, h - ry, w / 2, ry, 0, Math.PI).Z().toString(); }, detail: function (w, h) { var ry = Math.min(h * 0.18, w * 0.5); return P().arc(w / 2, ry, w / 2, ry, 0, Math.PI, true).toString(); } }, flowChartDisplay: { cat: "shape", label: "Flowchart: display", path: function (w, h) { return P().M(0, h / 2).L(w * 0.17, 0).L(w * 0.83, 0) .arc(w * 0.83, h / 2, w * 0.17, h / 2, -Math.PI / 2, Math.PI / 2) .L(w * 0.17, h).Z().toString(); } }, /* ---------- arrows ---------- */ rightArrow: { cat: "arrow", label: "Arrow: right", pts: function (w, h) { return arrowPts(w, h, 0.38, 0.4); } }, leftArrow: { cat: "arrow", label: "Arrow: left", pts: function (w, h) { return rotPts(arrowPts(w, h, 0.38, 0.4), w, h, 2); } }, upArrow: { cat: "arrow", label: "Arrow: up", pts: function (w, h) { return rotPts(arrowPts(w, h, 0.38, 0.4), w, h, 3); } }, downArrow: { cat: "arrow", label: "Arrow: down", pts: function (w, h) { return rotPts(arrowPts(w, h, 0.38, 0.4), w, h, 1); } }, leftRightArrow: { cat: "arrow", label: "Arrow: left-right", pts: function (w, h) { var t = h * 0.3, a = w * 0.25; return [[0, h / 2], [a, 0], [a, t], [w - a, t], [w - a, 0], [w, h / 2], [w - a, h], [w - a, h - t], [a, h - t], [a, h]]; } }, upDownArrow: { cat: "arrow", label: "Arrow: up-down", pts: function (w, h) { var t = w * 0.3, a = h * 0.25; return [[w / 2, 0], [w, a], [w - t, a], [w - t, h - a], [w, h - a], [w / 2, h], [0, h - a], [t, h - a], [t, a], [0, a]]; } }, quadArrow: { cat: "arrow", label: "Arrow: quad", pts: function (w, h) { var m = Math.min(w, h); var t = m * 0.13, hw = m * 0.26, hl = m * 0.26; var cx = w / 2, cy = h / 2; return [[cx, 0], [cx + hw, hl], [cx + t, hl], [cx + t, cy - t], [w - hl, cy - t], [w - hl, cy - hw], [w, cy], [w - hl, cy + hw], [w - hl, cy + t], [cx + t, cy + t], [cx + t, h - hl], [cx + hw, h - hl], [cx, h], [cx - hw, h - hl], [cx - t, h - hl], [cx - t, cy + t], [hl, cy + t], [hl, cy + hw], [0, cy], [hl, cy - hw], [hl, cy - t], [cx - t, cy - t], [cx - t, hl], [cx - hw, hl]]; } }, leftRightUpArrow: { cat: "arrow", label: "Arrow: left-right-up", pts: function (w, h) { var cy = h * 0.78, t = h * 0.2, hh = h * 0.22, hl = w * 0.14; var t2 = w * 0.12, vh = h * 0.3, vhw = w * 0.18; return [[w / 2, 0], [w / 2 + vhw, vh], [w / 2 + t2 / 2, vh], [w / 2 + t2 / 2, cy - t / 2], [w - hl, cy - t / 2], [w - hl, cy - hh], [w, cy], [w - hl, cy + hh], [w - hl, cy + t / 2], [hl, cy + t / 2], [hl, cy + hh], [0, cy], [hl, cy - hh], [hl, cy - t / 2], [w / 2 - t2 / 2, cy - t / 2], [w / 2 - t2 / 2, vh], [w / 2 - vhw, vh]]; } }, bentArrow: { cat: "arrow", label: "Arrow: bent", // runs right along the bottom, turns up, head at the top pts: function (w, h) { var cx = w * 0.7, hw = w * 0.3, t = h * 0.26, hh = h * 0.32; return [[0, h], [0, h - t], [cx - t / 2, h - t], [cx - t / 2, hh], [cx - hw, hh], [cx, 0], [cx + hw, hh], [cx + t / 2, hh], [cx + t / 2, h]]; } }, uturnArrow: { cat: "arrow", label: "Arrow: U-turn", path: function (w, h) { var t = Math.min(w, h) * 0.22; var cx = w * 0.62, r = w * 0.38; return P().M(0, h) .L(0, h * 0.55) .C(0, h * 0.2, cx - r * 0.1, h * 0.05, cx, h * 0.28) .L(cx, h * 0.1).L(w, h * 0.34).L(cx, h * 0.58).L(cx, h * 0.42) .C(cx - r * 0.1, h * 0.32, t, h * 0.35, t, h * 0.62) .L(t, h).Z().toString(); } }, leftUpArrow: { cat: "arrow", label: "Arrow: left-up", // an L with a head on each end: one points up, one points left pts: function (w, h) { var m = Math.min(w, h); var hw = m * 0.22, t = hw * 0.9, hh = m * 0.32; var vx = w - hw, hy = h - hw; return [[vx, 0], [w, hh], [vx + t / 2, hh], [vx + t / 2, hy + t / 2], [hh, hy + t / 2], [hh, h], [0, hy], [hh, hy - hw * 2 + hw], [hh, hy - t / 2], [vx - t / 2, hy - t / 2], [vx - t / 2, hh], [vx - hw, hh]]; } }, bentUpArrow: { cat: "arrow", label: "Arrow: bent up", pts: function (w, h) { var t = h * 0.28; return [[0, h - t], [w * 0.62, h - t], [w * 0.62, h * 0.3], [w * 0.45, h * 0.3], [w * 0.72, 0], [w, h * 0.3], [w * 0.83, h * 0.3], [w * 0.83, h], [0, h]]; } }, stripedRightArrow: { cat: "arrow", label: "Arrow: striped right", evenOdd: true, path: function (w, h) { var pts = arrowPts(w, h, 0.38, 0.4); var t = h * 0.3; return P().poly(pts).toString() + P().poly([[w * 0.05, t], [w * 0.09, t], [w * 0.09, h - t], [w * 0.05, h - t]]).toString() + P().poly([[w * 0.13, t], [w * 0.2, t], [w * 0.2, h - t], [w * 0.13, h - t]]).toString(); } }, notchedRightArrow: { cat: "arrow", label: "Arrow: notched right", pts: function (w, h) { var hx = w * 0.62, t = h * 0.3; return [[0, t], [hx, t], [hx, 0], [w, h / 2], [hx, h], [hx, h - t], [0, h - t], [t * 0.6, h / 2]]; } }, chevron: { cat: "arrow", label: "Chevron", pts: function (w, h) { return [[0, 0], [w * 0.72, 0], [w, h / 2], [w * 0.72, h], [0, h], [w * 0.28, h / 2]]; } }, homePlate: { cat: "arrow", label: "Pentagon arrow", pts: function (w, h) { return [[0, 0], [w * 0.72, 0], [w, h / 2], [w * 0.72, h], [0, h]]; } }, curvedRightArrow: { cat: "arrow", label: "Arrow: curved right", path: function (w, h) { var t = h * 0.2; return P().M(0, h * 0.08) .C(w * 0.6, h * 0.0, w * 0.86, h * 0.28, w * 0.78, h * 0.56) .L(w, h * 0.56).L(w * 0.7, h).L(w * 0.42, h * 0.56).L(w * 0.62, h * 0.56) .C(w * 0.68, h * 0.36, w * 0.5, h * 0.22, 0, h * 0.08 + t) .Z().toString(); } }, curvedLeftArrow: { cat: "arrow", label: "Arrow: curved left", path: function (w, h) { var t = h * 0.2; return P().M(w, h * 0.08) .C(w * 0.4, h * 0.0, w * 0.14, h * 0.28, w * 0.22, h * 0.56) .L(0, h * 0.56).L(w * 0.3, h).L(w * 0.58, h * 0.56).L(w * 0.38, h * 0.56) .C(w * 0.32, h * 0.36, w * 0.5, h * 0.22, w, h * 0.08 + t) .Z().toString(); } }, curvedUpArrow: { cat: "arrow", label: "Arrow: curved up", path: function (w, h) { var t = w * 0.2; return P().M(w * 0.08, h) .C(w * 0.0, h * 0.4, w * 0.28, h * 0.14, w * 0.56, h * 0.22) .L(w * 0.56, 0).L(w, h * 0.3).L(w * 0.56, h * 0.58).L(w * 0.56, h * 0.38) .C(w * 0.36, h * 0.32, w * 0.22, h * 0.5, w * 0.08 + t, h) .Z().toString(); } }, curvedDownArrow: { cat: "arrow", label: "Arrow: curved down", path: function (w, h) { var t = w * 0.2; return P().M(w * 0.08, 0) .C(w * 0.0, h * 0.6, w * 0.28, h * 0.86, w * 0.56, h * 0.78) .L(w * 0.56, h).L(w, h * 0.7).L(w * 0.56, h * 0.42).L(w * 0.56, h * 0.62) .C(w * 0.36, h * 0.68, w * 0.22, h * 0.5, w * 0.08 + t, 0) .Z().toString(); } }, circularArrow: { cat: "arrow", label: "Arrow: circular", path: function (w, h) { var cx = w / 2, cy = h / 2; var ro = Math.min(w, h) * 0.46, ri = Math.min(w, h) * 0.28; var a0 = Math.PI * 0.75, a1 = Math.PI * 2.15; var p = P(); p.arc(cx, cy, ro, ro, a0, a1, true); var ax = cx + ((ro + ri) / 2) * Math.cos(a1); var ay = cy + ((ro + ri) / 2) * Math.sin(a1); var hx = Math.cos(a1 + Math.PI / 2), hy = Math.sin(a1 + Math.PI / 2); var hl = Math.min(w, h) * 0.2; p.L(cx + (ro + hl * 0.5) * Math.cos(a1), cy + (ro + hl * 0.5) * Math.sin(a1)); p.L(ax + hx * hl, ay + hy * hl); p.L(cx + (ri - hl * 0.5) * Math.cos(a1), cy + (ri - hl * 0.5) * Math.sin(a1)); p.L(cx + ri * Math.cos(a1), cy + ri * Math.sin(a1)); p.arc(cx, cy, ri, ri, a1, a0); return p.Z().toString(); } }, rightArrowCallout: { cat: "arrow", label: "Callout: right arrow", pts: function (w, h) { var bx = w * 0.6, t = h * 0.22; return [[0, 0], [bx, 0], [bx, t], [w * 0.78, t], [w * 0.78, 0], [w, h / 2], [w * 0.78, h], [w * 0.78, h - t], [bx, h - t], [bx, h], [0, h]]; } }, leftArrowCallout: { cat: "arrow", label: "Callout: left arrow", pts: function (w, h) { var bx = w * 0.4, t = h * 0.22; return [[w, 0], [bx, 0], [bx, t], [w * 0.22, t], [w * 0.22, 0], [0, h / 2], [w * 0.22, h], [w * 0.22, h - t], [bx, h - t], [bx, h], [w, h]]; } }, upArrowCallout: { cat: "arrow", label: "Callout: up arrow", pts: function (w, h) { var by = h * 0.4, t = w * 0.22; return [[0, h], [0, by], [t, by], [t, h * 0.22], [0, h * 0.22], [w / 2, 0], [w, h * 0.22], [w - t, h * 0.22], [w - t, by], [w, by], [w, h]]; } }, downArrowCallout: { cat: "arrow", label: "Callout: down arrow", pts: function (w, h) { var by = h * 0.6, t = w * 0.22; return [[0, 0], [0, by], [t, by], [t, h * 0.78], [0, h * 0.78], [w / 2, h], [w, h * 0.78], [w - t, h * 0.78], [w - t, by], [w, by], [w, 0]]; } }, leftRightArrowCallout: { cat: "arrow", label: "Callout: left-right arrow", pts: function (w, h) { var t = h * 0.22, a = w * 0.2, b = w * 0.28; return [[b, 0], [w - b, 0], [w - b, t], [w - a, t], [w - a, 0], [w, h / 2], [w - a, h], [w - a, h - t], [w - b, h - t], [w - b, h], [b, h], [b, h - t], [a, h - t], [a, h], [0, h / 2], [a, 0], [a, t], [b, t]]; } }, /* ---------- call outs: stars, banners, bubbles ---------- */ star4: { cat: "callout", label: "4-point star", pts: function (w, h) { return starPts(w, h, 4, 0.3); } }, star5: { cat: "callout", label: "5-point star", pts: function (w, h) { return starPts(w, h, 5, 0.42); } }, star6: { cat: "callout", label: "6-point star", pts: function (w, h) { return starPts(w, h, 6, 0.5); } }, star7: { cat: "callout", label: "7-point star", pts: function (w, h) { return starPts(w, h, 7, 0.55); } }, star8: { cat: "callout", label: "8-point star", pts: function (w, h) { return starPts(w, h, 8, 0.6); } }, star10: { cat: "callout", label: "10-point star", pts: function (w, h) { return starPts(w, h, 10, 0.68); } }, star12: { cat: "callout", label: "12-point star", pts: function (w, h) { return starPts(w, h, 12, 0.72); } }, star16: { cat: "callout", label: "16-point star", pts: function (w, h) { return starPts(w, h, 16, 0.78); } }, star24: { cat: "callout", label: "24-point star", pts: function (w, h) { return starPts(w, h, 24, 0.84); } }, star32: { cat: "callout", label: "32-point star", pts: function (w, h) { return starPts(w, h, 32, 0.88); } }, irregularSeal1: { cat: "callout", label: "Explosion", pts: function (w, h) { var f = [[0.22, 0.14], [0.35, 0.30], [0.28, 0.0], [0.47, 0.24], [0.55, 0.06], [0.60, 0.28], [0.79, 0.10], [0.74, 0.32], [1.0, 0.28], [0.83, 0.46], [0.98, 0.56], [0.79, 0.60], [0.90, 0.83], [0.66, 0.70], [0.64, 1.0], [0.50, 0.74], [0.36, 0.94], [0.34, 0.70], [0.12, 0.82], [0.22, 0.60], [0.0, 0.56], [0.16, 0.44], [0.04, 0.30]]; return f.map(function (p) { return [p[0] * w, p[1] * h]; }); } }, irregularSeal2: { cat: "callout", label: "Explosion 2", pts: function (w, h) { var f = [[0.12, 0.26], [0.28, 0.18], [0.22, 0.02], [0.42, 0.14], [0.50, 0.0], [0.58, 0.16], [0.74, 0.06], [0.74, 0.24], [0.94, 0.18], [0.86, 0.36], [1.0, 0.44], [0.84, 0.54], [0.96, 0.70], [0.76, 0.68], [0.80, 0.88], [0.62, 0.76], [0.54, 1.0], [0.44, 0.80], [0.28, 0.92], [0.30, 0.72], [0.10, 0.76], [0.20, 0.58], [0.0, 0.50], [0.18, 0.40]]; return f.map(function (p) { return [p[0] * w, p[1] * h]; }); } }, ribbon: { cat: "callout", label: "Ribbon", pts: function (w, h) { var e = w * 0.14, t = h * 0.28; return [[0, 0], [e, h * 0.2], [0, h * 0.2], [w * 0.18, h * 0.5], [0, h * 0.8], [e, h], [w * 0.18, h * 0.8], [w * 0.82, h * 0.8], [w - e, h], [w, h * 0.8], [w * 0.82, h * 0.5], [w, h * 0.2], [w - e, h * 0.2], [w, 0], [w * 0.82, h * 0.2], [w * 0.18, h * 0.2]]; } }, ribbon2: { cat: "callout", label: "Banner", pts: function (w, h) { var e = w * 0.14; return [[0, h * 0.16], [w * 0.18, 0], [w * 0.82, 0], [w, h * 0.16], [w - e, h * 0.16], [w, h * 0.4], [w * 0.82, h * 0.34], [w * 0.82, h], [w * 0.5, h * 0.78], [w * 0.18, h], [w * 0.18, h * 0.34], [0, h * 0.4], [e, h * 0.16]]; } }, wave: { cat: "callout", label: "Wave", path: function (w, h) { return P().M(0, h * 0.18) .C(w * 0.25, h * -0.08, w * 0.75, h * 0.44, w, h * 0.18) .L(w, h * 0.82) .C(w * 0.75, h * 1.08, w * 0.25, h * 0.56, 0, h * 0.82) .Z().toString(); } }, doubleWave: { cat: "callout", label: "Double wave", path: function (w, h) { return P().M(0, h * 0.2) .C(w * 0.12, h * -0.06, w * 0.38, h * 0.4, w * 0.5, h * 0.2) .C(w * 0.62, h * 0.0, w * 0.88, h * 0.4, w, h * 0.2) .L(w, h * 0.8) .C(w * 0.88, h * 1.06, w * 0.62, h * 0.6, w * 0.5, h * 0.8) .C(w * 0.38, h * 1.0, w * 0.12, h * 0.6, 0, h * 0.8) .Z().toString(); } }, verticalScroll: { cat: "callout", label: "Vertical scroll", path: function (w, h) { var t = Math.min(w, h) * 0.16; return P().M(t, 0).L(w - t / 2, 0) .arc(w - t / 2, t / 2, t / 2, t / 2, -Math.PI / 2, Math.PI / 2) .L(w - t, t).L(w - t, h).L(t / 2, h) .arc(t / 2, h - t / 2, t / 2, t / 2, Math.PI / 2, Math.PI * 1.5) .L(t, h - t).Z().toString(); } }, horizontalScroll: { cat: "callout", label: "Horizontal scroll", path: function (w, h) { var t = Math.min(w, h) * 0.16; return P().M(0, t).L(0, h - t / 2) .arc(t / 2, h - t / 2, t / 2, t / 2, Math.PI, Math.PI * 2) .L(t, h - t).L(w, h - t).L(w, t / 2) .arc(w - t / 2, t / 2, t / 2, t / 2, 0, Math.PI) .L(w - t, t).Z().toString(); } }, wedgeRectCallout: { cat: "callout", label: "Speech bubble: rectangle", pts: function (w, h) { var b = h * 0.72; return [[0, 0], [w, 0], [w, b], [w * 0.42, b], [w * 0.2, h], [w * 0.28, b], [0, b]]; } }, wedgeRoundRectCallout: { cat: "callout", label: "Speech bubble: rounded", path: function (w, h) { var b = h * 0.72; var r = Math.min(w, b) * 0.18; var p = P(); p.M(r, 0).L(w - r, 0).arc(w - r, r, r, r, -Math.PI / 2, 0) .L(w, b - r).arc(w - r, b - r, r, r, 0, Math.PI / 2) .L(w * 0.42, b).L(w * 0.2, h).L(w * 0.28, b) .L(r, b).arc(r, b - r, r, r, Math.PI / 2, Math.PI) .L(0, r).arc(r, r, r, r, Math.PI, Math.PI * 1.5); return p.Z().toString(); } }, wedgeEllipseCallout: { cat: "callout", label: "Speech bubble: oval", path: function (w, h) { // all the way round from one side of the tail's base to the // other, then out to the tip and back var cy = h * 0.36, ry = h * 0.36; var a1 = Math.PI * 0.60, a2 = Math.PI * 0.80; return P().arc(w / 2, cy, w / 2, ry, a2, a1 + Math.PI * 2, true) .L(w * 0.16, h).Z().toString(); } }, cloudCallout: { cat: "callout", label: "Speech bubble: cloud", path: function (w, h) { var bw = w, bh = h * 0.74; var p = P(); p.M(bw * 0.22, bh * 0.92) .C(bw * 0.02, bh * 0.92, bw * -0.05, bh * 0.58, bw * 0.13, bh * 0.5) .C(bw * 0.06, bh * 0.28, bw * 0.26, bh * 0.1, bw * 0.42, bh * 0.2) .C(bw * 0.5, bh * -0.03, bw * 0.8, bh * -0.03, bw * 0.86, bh * 0.22) .C(bw * 1.04, bh * 0.26, bw * 1.04, bh * 0.6, bw * 0.87, bh * 0.66) .C(bw * 0.93, bh * 0.88, bw * 0.7, bh * 1.02, bw * 0.57, bh * 0.9) .C(bw * 0.48, bh * 1.0, bw * 0.3, bh * 1.0, bw * 0.22, bh * 0.92) .Z(); p.circle(w * 0.22, h * 0.85, w * 0.07, h * 0.07); p.circle(w * 0.13, h * 0.96, w * 0.045, h * 0.045); return p.toString(); } }, /* ---------- equation ---------- */ mathPlus: { cat: "equation", label: "Plus", pts: function (w, h) { var t = Math.min(w, h) * 0.18, m = w * 0.11, n = h * 0.11; var cx = w / 2, cy = h / 2; return [[cx - t / 2, n], [cx + t / 2, n], [cx + t / 2, cy - t / 2], [w - m, cy - t / 2], [w - m, cy + t / 2], [cx + t / 2, cy + t / 2], [cx + t / 2, h - n], [cx - t / 2, h - n], [cx - t / 2, cy + t / 2], [m, cy + t / 2], [m, cy - t / 2], [cx - t / 2, cy - t / 2]]; } }, mathMinus: { cat: "equation", label: "Minus", pts: function (w, h) { var t = Math.min(w, h) * 0.18, m = w * 0.11, cy = h / 2; return [[m, cy - t / 2], [w - m, cy - t / 2], [w - m, cy + t / 2], [m, cy + t / 2]]; } }, mathMultiply: { cat: "equation", label: "Multiply", path: function (w, h) { var t = Math.min(w, h) * 0.16; var cx = w / 2, cy = h / 2; var ax = w * 0.36, ay = h * 0.36; var d = t / Math.SQRT2; return P().poly([ [cx - ax, cy - ay + d], [cx - ax + d, cy - ay], [cx, cy - d * 1.4], [cx + ax - d, cy - ay], [cx + ax, cy - ay + d], [cx + d * 1.4, cy], [cx + ax, cy + ay - d], [cx + ax - d, cy + ay], [cx, cy + d * 1.4], [cx - ax + d, cy + ay], [cx - ax, cy + ay - d], [cx - d * 1.4, cy] ]).toString(); } }, mathDivide: { cat: "equation", label: "Divide", evenOdd: true, path: function (w, h) { var t = Math.min(w, h) * 0.14, m = w * 0.11, cy = h / 2; var r = Math.min(w, h) * 0.09; return P().poly([[m, cy - t / 2], [w - m, cy - t / 2], [w - m, cy + t / 2], [m, cy + t / 2]]).toString() + P().circle(w / 2, h * 0.22, r, r).toString() + P().circle(w / 2, h * 0.78, r, r).toString(); } }, mathEqual: { cat: "equation", label: "Equal", evenOdd: true, path: function (w, h) { var t = Math.min(w, h) * 0.14, m = w * 0.11; var y1 = h * 0.36, y2 = h * 0.64; return P().poly([[m, y1 - t / 2], [w - m, y1 - t / 2], [w - m, y1 + t / 2], [m, y1 + t / 2]]).toString() + P().poly([[m, y2 - t / 2], [w - m, y2 - t / 2], [w - m, y2 + t / 2], [m, y2 + t / 2]]).toString(); } }, mathNotEqual: { cat: "equation", label: "Not equal", evenOdd: true, path: function (w, h) { var t = Math.min(w, h) * 0.14, m = w * 0.11; var y1 = h * 0.36, y2 = h * 0.64; var sw = Math.min(w, h) * 0.12; return P().poly([[m, y1 - t / 2], [w - m, y1 - t / 2], [w - m, y1 + t / 2], [m, y1 + t / 2]]).toString() + P().poly([[m, y2 - t / 2], [w - m, y2 - t / 2], [w - m, y2 + t / 2], [m, y2 + t / 2]]).toString() + P().poly([[w * 0.56, h * 0.08], [w * 0.56 + sw, h * 0.08], [w * 0.44 + sw, h * 0.92], [w * 0.44, h * 0.92]]).toString(); } }, leftBracket: { cat: "equation", size: [70, 220], label: "Left bracket", open: true, path: function (w, h) { var r = Math.min(w, h * 0.5) * 0.9; return P().M(w, 0) .C(w - r * 0.55, 0, 0, r * 0.45, 0, r) .L(0, h - r) .C(0, h - r * 0.45, w - r * 0.55, h, w, h).toString(); } }, rightBracket: { cat: "equation", size: [70, 220], label: "Right bracket", open: true, path: function (w, h) { var r = Math.min(w, h * 0.5) * 0.9; return P().M(0, 0) .C(r * 0.55, 0, w, r * 0.45, w, r) .L(w, h - r) .C(w, h - r * 0.45, r * 0.55, h, 0, h).toString(); } }, bracketPair: { cat: "equation", size: [70, 220], label: "Bracket pair", open: true, path: function (w, h) { var bw = Math.min(w * 0.25, h * 0.3); var r = Math.min(bw, h * 0.5) * 0.9; return P().M(bw, 0).C(bw - r * 0.55, 0, 0, r * 0.45, 0, r) .L(0, h - r).C(0, h - r * 0.45, bw - r * 0.55, h, bw, h).toString() + P().M(w - bw, 0).C(w - bw + r * 0.55, 0, w, r * 0.45, w, r) .L(w, h - r).C(w, h - r * 0.45, w - bw + r * 0.55, h, w - bw, h).toString(); } }, rightBrace: { cat: "equation", size: [70, 220], label: "Right brace", open: true, path: function (w, h) { return bracePath(w, h, false); } }, leftBrace: { cat: "equation", size: [70, 220], label: "Left brace", open: true, path: function (w, h) { return bracePath(w, h, true); } }, bracePair: { cat: "equation", size: [70, 220], label: "Brace pair", open: true, path: function (w, h) { var bw = Math.min(w * 0.3, h * 0.3); return shiftPath(bracePath(bw, h, true), 0, 0) + shiftPath(bracePath(bw, h, false), w - bw, 0); } } }; /* A brace is two quarter turns out to the middle and two back, which is exactly what PresentationML's rightBrace draws and what a text-set "}" looks like. `left` mirrors it. */ function bracePath(w, h, left) { var r = Math.min(w * 0.5, h * 0.24); var x0 = left ? w : 0; // the open side var x1 = left ? 0 : w; // the point var dir = left ? -1 : 1; var p = P(); p.M(x0, 0); p.C(x0 + dir * r * 0.55, 0, x0 + dir * r, r * 0.45, x0 + dir * r, r); p.L(x0 + dir * r, h / 2 - r); p.C(x0 + dir * r, h / 2 - r * 0.45, x1 - dir * r * 0.45, h / 2, x1, h / 2); p.C(x1 - dir * r * 0.45, h / 2, x0 + dir * r, h / 2 + r * 0.45, x0 + dir * r, h / 2 + r); p.L(x0 + dir * r, h - r); p.C(x0 + dir * r, h - r * 0.45, x0 + dir * r * 0.55, h, x0, h); return p.toString(); } // shiftPath translates a finished path; only M/L/C coordinates exist in // what this file produces, so a straight number walk is enough function shiftPath(d, dx, dy) { var i = 0; return d.replace(/([MLC])([^MLCZ]*)/g, function (all, cmd, nums) { var v = nums.trim().split(/[\s,]+/).map(Number); for (var k = 0; k < v.length; k += 2) { v[k] = r2(v[k] + dx); v[k + 1] = r2(v[k + 1] + dy); } return cmd + v.join(" "); }); } /* ---------------- names documents may still carry ---------------- */ /* Three shapes had editor-invented names before the catalogue existed. They are gone; these are what a deck written back then says, and the only place that still knows. normalizeBody() in slides.js runs every kind through canonical() as a document loads, so a deck is rewritten to the catalogue's names the first time it is opened and these never have to be thought about again. */ var ALIASES = { round: "roundRect", arrow: "rightArrow", star: "star5" }; function canonical(kind) { var k = String(kind || "").trim(); if (!k) return "rect"; if (DEFS[k]) return k; if (ALIASES[k]) return ALIASES[k]; return k; } function def(kind) { return DEFS[canonical(kind)] || null; } function points(kind, w, h) { var d = def(kind); return d && d.pts ? d.pts(w, h) : null; } function path(kind, w, h) { var d = def(kind); if (!d) return ""; if (d.pts) return P().poly(d.pts(w, h)).toString(); return d.path(w, h); } function detail(kind, w, h) { var d = def(kind); return d && d.detail ? d.detail(w, h) : ""; } /* defaultSize is the box a shape is inserted at. Most want the editor's usual one; a brace or a bracket only reads as itself when it is tall and narrow, so those say so. */ function defaultSize(kind) { var d = def(kind); return (d && d.size) ? d.size.slice() : null; } /* icon draws a shape as a small outline for a picker, from the very same geometry the canvas uses - so an icon cannot come to disagree with what choosing it actually inserts. */ function icon(kind, size) { var s = size || 22, pad = 2; var w = s - pad * 2, h = s - pad * 2; var k = canonical(kind); var body = '"; var det = detail(k, w, h); if (det) body += ''; return '' + body + ""; } function evenOdd(kind) { var d = def(kind); return !!(d && d.evenOdd); } var CATEGORIES = [ { id: "shape", label: "Shapes", icon: "square outline" }, { id: "arrow", label: "Arrows", icon: "long arrow alternate right" }, { id: "callout", label: "Call outs", icon: "comment outline" }, { id: "equation", label: "Equation", icon: "plus" } ]; CATEGORIES.forEach(function (c) { c.kinds = Object.keys(DEFS).filter(function (k) { return DEFS[k].cat === c.id; }); }); return { CATEGORIES: CATEGORIES, canonical: canonical, has: function (kind) { return !!def(kind); }, label: function (kind) { var d = def(kind); return d ? d.label : "Shape"; }, category: function (kind) { var d = def(kind); return d ? d.cat : ""; }, evenOdd: evenOdd, icon: icon, isOpen: function (kind) { var d = def(kind); return !!(d && d.open); }, points: points, path: path, detail: detail, defaultSize: defaultSize }; })();