schematex 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/README.zh-CN.md +2 -2
- package/dist/ai/ai-sdk.cjs +23 -23
- package/dist/ai/ai-sdk.d.cts +3 -3
- package/dist/ai/ai-sdk.d.ts +3 -3
- package/dist/ai/ai-sdk.js +18 -18
- package/dist/ai/index.cjs +32 -32
- package/dist/ai/index.d.cts +4 -4
- package/dist/ai/index.d.ts +4 -4
- package/dist/ai/index.js +19 -19
- package/dist/{api-DXOhuK3e.d.ts → api-1xoGiseb.d.cts} +2 -2
- package/dist/{api-B7C7qGiG.d.cts → api-CgXRSnCn.d.ts} +2 -2
- package/dist/browser.cjs +24 -24
- package/dist/browser.d.cts +3 -3
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +18 -18
- package/dist/{chunk-C2BNO3CI.cjs → chunk-35NGXDT2.cjs} +12 -12
- package/dist/{chunk-C2BNO3CI.cjs.map → chunk-35NGXDT2.cjs.map} +1 -1
- package/dist/{chunk-ZUK4BY55.js → chunk-3J4DZPZC.js} +3 -3
- package/dist/{chunk-ZUK4BY55.js.map → chunk-3J4DZPZC.js.map} +1 -1
- package/dist/{chunk-JEQGWH5N.js → chunk-3PH2MQGN.js} +3 -3
- package/dist/{chunk-JEQGWH5N.js.map → chunk-3PH2MQGN.js.map} +1 -1
- package/dist/{chunk-ZQECHIBI.js → chunk-3WX24RCH.js} +3 -3
- package/dist/{chunk-ZQECHIBI.js.map → chunk-3WX24RCH.js.map} +1 -1
- package/dist/{chunk-ATE7LD6I.cjs → chunk-4AC6I7KJ.cjs} +4 -4
- package/dist/{chunk-ATE7LD6I.cjs.map → chunk-4AC6I7KJ.cjs.map} +1 -1
- package/dist/{chunk-D4QZ6UDO.js → chunk-4MRVJI7G.js} +110 -3
- package/dist/chunk-4MRVJI7G.js.map +1 -0
- package/dist/{chunk-VOFND6ZQ.cjs → chunk-4OC3CTGE.cjs} +4 -4
- package/dist/{chunk-VOFND6ZQ.cjs.map → chunk-4OC3CTGE.cjs.map} +1 -1
- package/dist/{chunk-VG5LP5A4.cjs → chunk-5ZQRHDMQ.cjs} +4 -4
- package/dist/{chunk-VG5LP5A4.cjs.map → chunk-5ZQRHDMQ.cjs.map} +1 -1
- package/dist/{chunk-6W6RFZND.cjs → chunk-627GHE2N.cjs} +5 -5
- package/dist/{chunk-6W6RFZND.cjs.map → chunk-627GHE2N.cjs.map} +1 -1
- package/dist/{chunk-TDSGQT7R.js → chunk-64LABNTF.js} +3 -3
- package/dist/{chunk-TDSGQT7R.js.map → chunk-64LABNTF.js.map} +1 -1
- package/dist/{chunk-AEZVCGH4.cjs → chunk-6QZQTASC.cjs} +12 -12
- package/dist/{chunk-AEZVCGH4.cjs.map → chunk-6QZQTASC.cjs.map} +1 -1
- package/dist/{chunk-FFAJQ36U.cjs → chunk-6ZD7TCWO.cjs} +15 -15
- package/dist/{chunk-FFAJQ36U.cjs.map → chunk-6ZD7TCWO.cjs.map} +1 -1
- package/dist/{chunk-PUD7PIY5.js → chunk-AXMBXAEA.js} +3 -3
- package/dist/{chunk-PUD7PIY5.js.map → chunk-AXMBXAEA.js.map} +1 -1
- package/dist/{chunk-X3GEGVW2.js → chunk-B4CMWA6Y.js} +3 -3
- package/dist/{chunk-X3GEGVW2.js.map → chunk-B4CMWA6Y.js.map} +1 -1
- package/dist/{chunk-O6A2GJLI.cjs → chunk-C4Y24X3U.cjs} +4 -4
- package/dist/{chunk-O6A2GJLI.cjs.map → chunk-C4Y24X3U.cjs.map} +1 -1
- package/dist/{chunk-CAAMBDEN.cjs → chunk-ENUM7GMZ.cjs} +110 -2
- package/dist/chunk-ENUM7GMZ.cjs.map +1 -0
- package/dist/{chunk-IOD2EFMX.cjs → chunk-GAQ36VFD.cjs} +4 -4
- package/dist/{chunk-IOD2EFMX.cjs.map → chunk-GAQ36VFD.cjs.map} +1 -1
- package/dist/{chunk-AHSSFGDJ.cjs → chunk-GTCAJPQR.cjs} +1738 -217
- package/dist/chunk-GTCAJPQR.cjs.map +1 -0
- package/dist/{chunk-6GHXO3WQ.js → chunk-GYYYULBL.js} +3 -3
- package/dist/{chunk-6GHXO3WQ.js.map → chunk-GYYYULBL.js.map} +1 -1
- package/dist/{chunk-IU26USXA.cjs → chunk-INVLJYAE.cjs} +4 -4
- package/dist/{chunk-IU26USXA.cjs.map → chunk-INVLJYAE.cjs.map} +1 -1
- package/dist/{chunk-BXS53MLV.cjs → chunk-ITI3STJ6.cjs} +4 -4
- package/dist/{chunk-BXS53MLV.cjs.map → chunk-ITI3STJ6.cjs.map} +1 -1
- package/dist/{chunk-XAYD5NVJ.js → chunk-JEMAOC2D.js} +3 -3
- package/dist/{chunk-XAYD5NVJ.js.map → chunk-JEMAOC2D.js.map} +1 -1
- package/dist/{chunk-PZCYFT2A.js → chunk-M26ORU4P.js} +3 -3
- package/dist/{chunk-PZCYFT2A.js.map → chunk-M26ORU4P.js.map} +1 -1
- package/dist/{chunk-JCJWSW5Y.js → chunk-NIYB6CHH.js} +326 -4
- package/dist/chunk-NIYB6CHH.js.map +1 -0
- package/dist/{chunk-U4AJLMHC.js → chunk-NKYR4PAS.js} +3 -3
- package/dist/{chunk-U4AJLMHC.js.map → chunk-NKYR4PAS.js.map} +1 -1
- package/dist/{chunk-TFNH2NLJ.cjs → chunk-OJ3P4IC4.cjs} +4 -4
- package/dist/{chunk-TFNH2NLJ.cjs.map → chunk-OJ3P4IC4.cjs.map} +1 -1
- package/dist/{chunk-7P4C5DMD.js → chunk-PFZKW3HE.js} +3 -3
- package/dist/chunk-PFZKW3HE.js.map +1 -0
- package/dist/{chunk-QHZEGWE7.js → chunk-RDYACU2G.js} +3 -3
- package/dist/{chunk-QHZEGWE7.js.map → chunk-RDYACU2G.js.map} +1 -1
- package/dist/{chunk-UNLLWCQK.js → chunk-SXOAAQNY.js} +3 -3
- package/dist/{chunk-UNLLWCQK.js.map → chunk-SXOAAQNY.js.map} +1 -1
- package/dist/{chunk-LFJE64RD.cjs → chunk-TX3YWZZX.cjs} +328 -6
- package/dist/chunk-TX3YWZZX.cjs.map +1 -0
- package/dist/{chunk-BP6MLXJU.js → chunk-UFXDAIDD.js} +1552 -31
- package/dist/chunk-UFXDAIDD.js.map +1 -0
- package/dist/{chunk-MZWVJFTV.cjs → chunk-VY6UZYYL.cjs} +15 -15
- package/dist/{chunk-MZWVJFTV.cjs.map → chunk-VY6UZYYL.cjs.map} +1 -1
- package/dist/{chunk-Y4OBXYGW.cjs → chunk-VYQXB2RC.cjs} +12 -12
- package/dist/{chunk-Y4OBXYGW.cjs.map → chunk-VYQXB2RC.cjs.map} +1 -1
- package/dist/{chunk-JYAL26WQ.cjs → chunk-WJXLF42K.cjs} +4 -4
- package/dist/chunk-WJXLF42K.cjs.map +1 -0
- package/dist/{chunk-OND4N5ZZ.js → chunk-XCCXG6RR.js} +3 -3
- package/dist/{chunk-OND4N5ZZ.js.map → chunk-XCCXG6RR.js.map} +1 -1
- package/dist/{chunk-4CLAD7VZ.js → chunk-ZCHGIWJK.js} +3 -3
- package/dist/{chunk-4CLAD7VZ.js.map → chunk-ZCHGIWJK.js.map} +1 -1
- package/dist/{diagnostics-fyjWjeDa.d.ts → diagnostics-CDwnQ65n.d.cts} +1 -1
- package/dist/{diagnostics-fyjWjeDa.d.cts → diagnostics-CDwnQ65n.d.ts} +1 -1
- package/dist/diagrams/blockdiagram/index.cjs +6 -6
- package/dist/diagrams/blockdiagram/index.d.cts +1 -1
- package/dist/diagrams/blockdiagram/index.d.ts +1 -1
- package/dist/diagrams/blockdiagram/index.js +2 -2
- package/dist/diagrams/circuit/index.cjs +9 -9
- package/dist/diagrams/circuit/index.d.cts +1 -1
- package/dist/diagrams/circuit/index.d.ts +1 -1
- package/dist/diagrams/circuit/index.js +2 -2
- package/dist/diagrams/ecomap/index.cjs +7 -7
- package/dist/diagrams/ecomap/index.d.cts +1 -1
- package/dist/diagrams/ecomap/index.d.ts +1 -1
- package/dist/diagrams/ecomap/index.js +2 -2
- package/dist/diagrams/entity/index.cjs +6 -6
- package/dist/diagrams/entity/index.d.cts +1 -1
- package/dist/diagrams/entity/index.d.ts +1 -1
- package/dist/diagrams/entity/index.js +2 -2
- package/dist/diagrams/fishbone/index.cjs +8 -8
- package/dist/diagrams/fishbone/index.d.cts +1 -1
- package/dist/diagrams/fishbone/index.d.ts +1 -1
- package/dist/diagrams/fishbone/index.js +2 -2
- package/dist/diagrams/flowchart/index.cjs +8 -8
- package/dist/diagrams/flowchart/index.d.cts +2 -2
- package/dist/diagrams/flowchart/index.d.ts +2 -2
- package/dist/diagrams/flowchart/index.js +2 -2
- package/dist/diagrams/genogram/index.cjs +9 -9
- package/dist/diagrams/genogram/index.d.cts +1 -1
- package/dist/diagrams/genogram/index.d.ts +1 -1
- package/dist/diagrams/genogram/index.js +2 -2
- package/dist/diagrams/ladder/index.cjs +6 -6
- package/dist/diagrams/ladder/index.d.cts +1 -1
- package/dist/diagrams/ladder/index.d.ts +1 -1
- package/dist/diagrams/ladder/index.js +2 -2
- package/dist/diagrams/logic/index.cjs +8 -8
- package/dist/diagrams/logic/index.d.cts +1 -1
- package/dist/diagrams/logic/index.d.ts +1 -1
- package/dist/diagrams/logic/index.js +2 -2
- package/dist/diagrams/orgchart/index.cjs +8 -8
- package/dist/diagrams/orgchart/index.d.cts +1 -1
- package/dist/diagrams/orgchart/index.d.ts +1 -1
- package/dist/diagrams/orgchart/index.js +2 -2
- package/dist/diagrams/pedigree/index.cjs +7 -7
- package/dist/diagrams/pedigree/index.d.cts +1 -1
- package/dist/diagrams/pedigree/index.d.ts +1 -1
- package/dist/diagrams/pedigree/index.js +2 -2
- package/dist/diagrams/phylo/index.cjs +7 -7
- package/dist/diagrams/phylo/index.d.cts +1 -1
- package/dist/diagrams/phylo/index.d.ts +1 -1
- package/dist/diagrams/phylo/index.js +2 -2
- package/dist/diagrams/sld/index.cjs +8 -8
- package/dist/diagrams/sld/index.d.cts +1 -1
- package/dist/diagrams/sld/index.d.ts +1 -1
- package/dist/diagrams/sld/index.js +2 -2
- package/dist/diagrams/sociogram/index.cjs +6 -6
- package/dist/diagrams/sociogram/index.d.cts +1 -1
- package/dist/diagrams/sociogram/index.d.ts +1 -1
- package/dist/diagrams/sociogram/index.js +2 -2
- package/dist/diagrams/timing/index.d.cts +1 -1
- package/dist/diagrams/timing/index.d.ts +1 -1
- package/dist/diagrams/venn/index.cjs +9 -9
- package/dist/diagrams/venn/index.d.cts +1 -1
- package/dist/diagrams/venn/index.d.ts +1 -1
- package/dist/diagrams/venn/index.js +2 -2
- package/dist/{index-CFaKjWPy.d.ts → index-CYSH3_ca.d.ts} +1 -1
- package/dist/{index-DZNmJWGg.d.cts → index-DNOoLmYX.d.cts} +1 -1
- package/dist/index.cjs +86 -86
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +22 -22
- package/dist/react.cjs +18 -18
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +17 -17
- package/dist/{tools-BDNih0Mb.d.ts → tools-BhTti-oG.d.ts} +3 -3
- package/dist/{tools-C23t_WeN.d.cts → tools-CbYmQ3hH.d.cts} +3 -3
- package/package.json +1 -1
- package/dist/chunk-7P4C5DMD.js.map +0 -1
- package/dist/chunk-AHSSFGDJ.cjs.map +0 -1
- package/dist/chunk-BP6MLXJU.js.map +0 -1
- package/dist/chunk-CAAMBDEN.cjs.map +0 -1
- package/dist/chunk-D4QZ6UDO.js.map +0 -1
- package/dist/chunk-JCJWSW5Y.js.map +0 -1
- package/dist/chunk-JYAL26WQ.cjs.map +0 -1
- package/dist/chunk-LFJE64RD.cjs.map +0 -1
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { orgchart } from './chunk-
|
|
2
|
-
import { circuit } from './chunk-
|
|
3
|
-
import { blockdiagram } from './chunk-
|
|
4
|
-
import { ladder } from './chunk-
|
|
5
|
-
import { sld } from './chunk-
|
|
6
|
-
import { entity } from './chunk-
|
|
7
|
-
import { fishbone } from './chunk-
|
|
8
|
-
import { venn } from './chunk-
|
|
9
|
-
import { flowchart, layoutFlowchart } from './chunk-
|
|
10
|
-
import { genogram } from './chunk-
|
|
11
|
-
import { ecomap, estimateTextWidth } from './chunk-
|
|
12
|
-
import { pedigree } from './chunk-
|
|
1
|
+
import { orgchart } from './chunk-3J4DZPZC.js';
|
|
2
|
+
import { circuit } from './chunk-RDYACU2G.js';
|
|
3
|
+
import { blockdiagram } from './chunk-3PH2MQGN.js';
|
|
4
|
+
import { ladder } from './chunk-B4CMWA6Y.js';
|
|
5
|
+
import { sld } from './chunk-PFZKW3HE.js';
|
|
6
|
+
import { entity } from './chunk-JEMAOC2D.js';
|
|
7
|
+
import { fishbone } from './chunk-GYYYULBL.js';
|
|
8
|
+
import { venn } from './chunk-SXOAAQNY.js';
|
|
9
|
+
import { flowchart, layoutFlowchart } from './chunk-AXMBXAEA.js';
|
|
10
|
+
import { genogram } from './chunk-M26ORU4P.js';
|
|
11
|
+
import { ecomap, estimateTextWidth } from './chunk-XCCXG6RR.js';
|
|
12
|
+
import { pedigree } from './chunk-ZCHGIWJK.js';
|
|
13
13
|
import { parseFrontmatter } from './chunk-2KTQ75LN.js';
|
|
14
|
-
import { phylo } from './chunk-
|
|
15
|
-
import { sociogram } from './chunk-
|
|
14
|
+
import { phylo } from './chunk-3WX24RCH.js';
|
|
15
|
+
import { sociogram } from './chunk-64LABNTF.js';
|
|
16
16
|
import { timing } from './chunk-MTIZIHWE.js';
|
|
17
|
-
import { logic } from './chunk-
|
|
18
|
-
import { resolveBaseTheme, resolveTimelineTheme, resolveStateTheme, cssCustomProperties, resolvePetriTheme, resolveNetworkTheme, resolveUmlClassTheme, DEFAULT_FONT_FAMILY, FONT_SIZE, resolveReliabilityTheme, STROKE_WIDTH, resolveBowtieTheme, resolveMindmapTheme, resolveMatrixTheme, resolveBpmnTheme, resolveFloorplanTheme, TITLE } from './chunk-
|
|
17
|
+
import { logic } from './chunk-NKYR4PAS.js';
|
|
18
|
+
import { resolveBaseTheme, resolveTimelineTheme, resolveStateTheme, cssCustomProperties, resolvePetriTheme, resolveNetworkTheme, resolveUmlClassTheme, DEFAULT_FONT_FAMILY, FONT_SIZE, resolveReliabilityTheme, STROKE_WIDTH, resolveBowtieTheme, resolveMindmapTheme, resolveMatrixTheme, resolveBpmnTheme, resolveFloorplanTheme, TITLE, resolvePlaybookTheme } from './chunk-4MRVJI7G.js';
|
|
19
19
|
import { QUOTE_PAIRS, stripQuotes, isOpenQuote, extractQuotedString, matchQuotedTitle } from './chunk-TO6PNBT3.js';
|
|
20
20
|
import { el, escapeXml, group, rect, text, line, path, circle, polygon, title, desc, svgRoot, defs, multilineText } from './chunk-SYYBKDL7.js';
|
|
21
21
|
|
|
@@ -1876,7 +1876,7 @@ function parseTimeline(src) {
|
|
|
1876
1876
|
let i = 0;
|
|
1877
1877
|
let autoId = 0;
|
|
1878
1878
|
const nextId2 = (prefix) => `${prefix}-${++autoId}`;
|
|
1879
|
-
const
|
|
1879
|
+
const ordinal2 = { index: 0 };
|
|
1880
1880
|
const first = lines[0];
|
|
1881
1881
|
if (/^timeline\b/i.test(first.text)) {
|
|
1882
1882
|
const rest = first.text.replace(/^timeline\b/i, "").trim();
|
|
@@ -1947,7 +1947,7 @@ function parseTimeline(src) {
|
|
|
1947
1947
|
i++;
|
|
1948
1948
|
continue;
|
|
1949
1949
|
}
|
|
1950
|
-
const parsed2 = parseEventLine(child.text, child.line, nextId2,
|
|
1950
|
+
const parsed2 = parseEventLine(child.text, child.line, nextId2, ordinal2);
|
|
1951
1951
|
if (!parsed2) throw new TimelineParseError(`Unrecognized line in ${keyword}: ${child.text}`, child.line);
|
|
1952
1952
|
parsed2.event.trackId = trackId;
|
|
1953
1953
|
ast.events.push(parsed2.event);
|
|
@@ -1961,7 +1961,7 @@ function parseTimeline(src) {
|
|
|
1961
1961
|
}
|
|
1962
1962
|
continue;
|
|
1963
1963
|
}
|
|
1964
|
-
const parsed = parseEventLine(text2, L.line, nextId2,
|
|
1964
|
+
const parsed = parseEventLine(text2, L.line, nextId2, ordinal2);
|
|
1965
1965
|
if (parsed) {
|
|
1966
1966
|
ast.events.push(parsed.event);
|
|
1967
1967
|
i++;
|
|
@@ -1985,12 +1985,12 @@ function safeParseDate(raw, line2) {
|
|
|
1985
1985
|
throw new TimelineParseError(msg, line2);
|
|
1986
1986
|
}
|
|
1987
1987
|
}
|
|
1988
|
-
function parseRowKey(raw,
|
|
1988
|
+
function parseRowKey(raw, ordinal2) {
|
|
1989
1989
|
const parsed = tryParseDate(raw);
|
|
1990
1990
|
if (parsed) return parsed;
|
|
1991
|
-
|
|
1991
|
+
ordinal2.index += 1;
|
|
1992
1992
|
return {
|
|
1993
|
-
value:
|
|
1993
|
+
value: ordinal2.index,
|
|
1994
1994
|
raw,
|
|
1995
1995
|
precision: "ordinal"
|
|
1996
1996
|
};
|
|
@@ -2027,7 +2027,7 @@ function applyConfig(ast, k, v, line2) {
|
|
|
2027
2027
|
(ast.metadata ??= {})[k] = v;
|
|
2028
2028
|
}
|
|
2029
2029
|
}
|
|
2030
|
-
function parseEventLine(text2, line2, nextId2,
|
|
2030
|
+
function parseEventLine(text2, line2, nextId2, ordinal2) {
|
|
2031
2031
|
const { props, rest } = parseProperties(text2, line2);
|
|
2032
2032
|
const { date, end, body } = splitDateAndBody(rest, line2);
|
|
2033
2033
|
let kind = end ? "range" : "point";
|
|
@@ -2044,8 +2044,8 @@ function parseEventLine(text2, line2, nextId2, ordinal) {
|
|
|
2044
2044
|
id: nextId2("ev"),
|
|
2045
2045
|
label,
|
|
2046
2046
|
kind,
|
|
2047
|
-
start: parseRowKey(date,
|
|
2048
|
-
end: end ? parseRowKey(end,
|
|
2047
|
+
start: parseRowKey(date, ordinal2),
|
|
2048
|
+
end: end ? parseRowKey(end, ordinal2) : void 0,
|
|
2049
2049
|
icon: props["icon"],
|
|
2050
2050
|
shape: props["shape"],
|
|
2051
2051
|
color: props["color"],
|
|
@@ -38071,7 +38071,7 @@ function renderErrorPanel(lay, t) {
|
|
|
38071
38071
|
const w = Math.max(560, ...lines.map((l) => l.length * 6.6 + 48));
|
|
38072
38072
|
const h = 56 + lines.length * 19;
|
|
38073
38073
|
return svgRoot(
|
|
38074
|
-
{ viewBox: `0 0 ${r24(w)} ${h}`, class: "sx-fp", role: "img" },
|
|
38074
|
+
{ viewBox: `0 0 ${r24(w)} ${h}`, width: r24(w), height: h, class: "sx-fp", role: "img" },
|
|
38075
38075
|
[
|
|
38076
38076
|
title(lay.title),
|
|
38077
38077
|
desc(`Floor plan validation failed with ${lines.length} error${lines.length === 1 ? "" : "s"}.`),
|
|
@@ -38407,7 +38407,7 @@ function renderFloorplanLayout(lay, config) {
|
|
|
38407
38407
|
const nRooms = lay.rooms.length;
|
|
38408
38408
|
const descText = `${nRooms} room${nRooms === 1 ? "" : "s"}, ${formatArea(lay.totalAreaM2, lay.unit)} total. ${lay.items.length} furniture item${lay.items.length === 1 ? "" : "s"}.` + (lay.warnings.length ? ` Warnings: ${lay.warnings.join("; ")}.` : "");
|
|
38409
38409
|
return svgRoot(
|
|
38410
|
-
{ viewBox: `0 0 ${W2} ${H2}`, class: "sx-fp", role: "img" },
|
|
38410
|
+
{ viewBox: `0 0 ${W2} ${H2}`, width: W2, height: H2, class: "sx-fp", role: "img" },
|
|
38411
38411
|
[
|
|
38412
38412
|
title(lay.title),
|
|
38413
38413
|
desc(descText),
|
|
@@ -38473,6 +38473,1526 @@ var floorplan = {
|
|
|
38473
38473
|
}
|
|
38474
38474
|
};
|
|
38475
38475
|
|
|
38476
|
+
// src/diagrams/playbook/parser.ts
|
|
38477
|
+
var PlaybookParseError = class extends Error {
|
|
38478
|
+
line;
|
|
38479
|
+
constructor(message, line2) {
|
|
38480
|
+
super(`line ${line2}: ${message}`);
|
|
38481
|
+
this.name = "PlaybookParseError";
|
|
38482
|
+
this.line = line2;
|
|
38483
|
+
}
|
|
38484
|
+
};
|
|
38485
|
+
var isStr2 = (t) => t !== void 0 && "str" in t;
|
|
38486
|
+
var isWord2 = (t, w) => t !== void 0 && "word" in t && (w === void 0 || t.word.toLowerCase() === w);
|
|
38487
|
+
var tokDisplay = (t) => "word" in t ? t.word : `"${t.str}"`;
|
|
38488
|
+
var normalizeQuotes4 = (s) => s.replace(/[“”「」『』]/g, '"').replace(/[‘’]/g, "'");
|
|
38489
|
+
function tokenize8(line2) {
|
|
38490
|
+
const out = [];
|
|
38491
|
+
const re = /"([^"]*)"|(\S+)/g;
|
|
38492
|
+
let m;
|
|
38493
|
+
while (m = re.exec(line2)) {
|
|
38494
|
+
if (m[1] !== void 0) out.push({ str: m[1] });
|
|
38495
|
+
else out.push({ word: m[2] });
|
|
38496
|
+
}
|
|
38497
|
+
return out;
|
|
38498
|
+
}
|
|
38499
|
+
function parseNum2(t, what, ln) {
|
|
38500
|
+
if (!isWord2(t)) throw new PlaybookParseError(`expected a number for ${what}`, ln);
|
|
38501
|
+
const v = Number(t.word);
|
|
38502
|
+
if (!Number.isFinite(v)) throw new PlaybookParseError(`expected a number for ${what}, got "${t.word}"`, ln);
|
|
38503
|
+
return v;
|
|
38504
|
+
}
|
|
38505
|
+
function parseId2(t, what, ln) {
|
|
38506
|
+
if (!isWord2(t)) throw new PlaybookParseError(`expected ${what}`, ln);
|
|
38507
|
+
return t.word;
|
|
38508
|
+
}
|
|
38509
|
+
var COORD = /^([+-]?\d*\.?\d+),([+-]?\d*\.?\d+)$/;
|
|
38510
|
+
var POSITIONS = ["o", "c", "ol", "qb", "rb", "wr", "te", "x", "dl", "lb", "db", "s", "gk"];
|
|
38511
|
+
var NAMED_ROUTES = [
|
|
38512
|
+
"go",
|
|
38513
|
+
"fly",
|
|
38514
|
+
"streak",
|
|
38515
|
+
"vertical",
|
|
38516
|
+
"slant",
|
|
38517
|
+
"flat",
|
|
38518
|
+
"hitch",
|
|
38519
|
+
"out",
|
|
38520
|
+
"in",
|
|
38521
|
+
"dig",
|
|
38522
|
+
"curl",
|
|
38523
|
+
"comeback",
|
|
38524
|
+
"corner",
|
|
38525
|
+
"flag",
|
|
38526
|
+
"post",
|
|
38527
|
+
"wheel",
|
|
38528
|
+
"cross",
|
|
38529
|
+
"drag",
|
|
38530
|
+
"seam",
|
|
38531
|
+
"screen",
|
|
38532
|
+
"dive",
|
|
38533
|
+
"iso",
|
|
38534
|
+
"power",
|
|
38535
|
+
"counter",
|
|
38536
|
+
"sweep",
|
|
38537
|
+
"toss",
|
|
38538
|
+
"draw",
|
|
38539
|
+
"trap"
|
|
38540
|
+
];
|
|
38541
|
+
var RUN_CONCEPTS = /* @__PURE__ */ new Set(["dive", "iso", "power", "counter", "sweep", "toss", "draw", "trap"]);
|
|
38542
|
+
var FORMATIONS = [
|
|
38543
|
+
"i-form",
|
|
38544
|
+
"shotgun",
|
|
38545
|
+
"singleback",
|
|
38546
|
+
"pistol",
|
|
38547
|
+
"trips",
|
|
38548
|
+
"spread",
|
|
38549
|
+
"trips-right",
|
|
38550
|
+
"trips-left",
|
|
38551
|
+
"empty",
|
|
38552
|
+
"goal-line",
|
|
38553
|
+
"wishbone",
|
|
38554
|
+
"4-4-2",
|
|
38555
|
+
"4-3-3",
|
|
38556
|
+
"4-2-3-1",
|
|
38557
|
+
"3-5-2",
|
|
38558
|
+
"4-4-1-1",
|
|
38559
|
+
"4-5-1",
|
|
38560
|
+
"3-4-3",
|
|
38561
|
+
"horns",
|
|
38562
|
+
"1-4-high",
|
|
38563
|
+
"1-4-low",
|
|
38564
|
+
"box",
|
|
38565
|
+
"spread-pnr",
|
|
38566
|
+
"5-out",
|
|
38567
|
+
"4-out"
|
|
38568
|
+
];
|
|
38569
|
+
var DEFENSES = [
|
|
38570
|
+
"4-3",
|
|
38571
|
+
"3-4",
|
|
38572
|
+
"4-4",
|
|
38573
|
+
"nickel",
|
|
38574
|
+
"dime",
|
|
38575
|
+
"cover-0",
|
|
38576
|
+
"cover-1",
|
|
38577
|
+
"cover-2",
|
|
38578
|
+
"cover-3",
|
|
38579
|
+
"cover-4",
|
|
38580
|
+
"cover-6",
|
|
38581
|
+
"man",
|
|
38582
|
+
"zone-2-3",
|
|
38583
|
+
"zone-3-2",
|
|
38584
|
+
"zone-1-3-1",
|
|
38585
|
+
"low-block",
|
|
38586
|
+
"mid-block",
|
|
38587
|
+
"high-press"
|
|
38588
|
+
];
|
|
38589
|
+
var DIRS = ["left", "right"];
|
|
38590
|
+
function parseHeader4(tok, ast, ln) {
|
|
38591
|
+
while (tok.length) {
|
|
38592
|
+
const t = tok.shift();
|
|
38593
|
+
if (isStr2(t)) ast.title = t.str;
|
|
38594
|
+
else if (isWord2(t, "sport")) {
|
|
38595
|
+
const s = parseId2(tok.shift(), "sport", ln).toLowerCase();
|
|
38596
|
+
if (s !== "football" && s !== "basketball" && s !== "soccer") {
|
|
38597
|
+
throw new PlaybookParseError(`sport must be football|basketball|soccer, got "${s}"`, ln);
|
|
38598
|
+
}
|
|
38599
|
+
ast.sport = s;
|
|
38600
|
+
} else throw new PlaybookParseError(`playbook: unexpected token "${tokDisplay(t)}"`, ln);
|
|
38601
|
+
}
|
|
38602
|
+
}
|
|
38603
|
+
function parseField(tok, ast, ln) {
|
|
38604
|
+
while (tok.length) {
|
|
38605
|
+
const t = tok.shift();
|
|
38606
|
+
if (isWord2(t, "down")) ast.down = parseNum2(tok.shift(), "down", ln);
|
|
38607
|
+
else if (isWord2(t, "distance") || isWord2(t, "togo")) ast.distance = parseNum2(tok.shift(), "distance", ln);
|
|
38608
|
+
else if (isWord2(t, "los") || isWord2(t, "ball")) ast.losYard = parseNum2(tok.shift(), "los", ln);
|
|
38609
|
+
else if (isWord2(t, "goal") || isWord2(t, "togoal")) ast.toGoal = parseNum2(tok.shift(), "goal", ln);
|
|
38610
|
+
else if (isWord2(t, "view")) {
|
|
38611
|
+
const v = parseId2(tok.shift(), "view (full|half)", ln).toLowerCase();
|
|
38612
|
+
if (v !== "full" && v !== "half" && v !== "auto") throw new PlaybookParseError(`view must be full|half`, ln);
|
|
38613
|
+
ast.view = v;
|
|
38614
|
+
} else if (isWord2(t, "hash")) {
|
|
38615
|
+
const h = parseId2(tok.shift(), "hash (nfl|college|none)", ln).toLowerCase();
|
|
38616
|
+
if (h !== "nfl" && h !== "college" && h !== "none") throw new PlaybookParseError(`hash must be nfl|college|none`, ln);
|
|
38617
|
+
ast.hash = h;
|
|
38618
|
+
} else throw new PlaybookParseError(`field: unexpected token "${tokDisplay(t)}"`, ln);
|
|
38619
|
+
}
|
|
38620
|
+
}
|
|
38621
|
+
function parseFormation(tok, ast, ln) {
|
|
38622
|
+
let name = parseId2(tok.shift(), "a formation", ln).toLowerCase();
|
|
38623
|
+
let side;
|
|
38624
|
+
const hy = /^(.*)-(left|right)$/.exec(name);
|
|
38625
|
+
if (hy && FORMATIONS.includes(hy[1]) && hy[1] !== "trips") {
|
|
38626
|
+
name = hy[1];
|
|
38627
|
+
side = hy[2];
|
|
38628
|
+
}
|
|
38629
|
+
if (!FORMATIONS.includes(name)) {
|
|
38630
|
+
throw new PlaybookParseError(`unknown formation "${name}"`, ln);
|
|
38631
|
+
}
|
|
38632
|
+
ast.formation = name;
|
|
38633
|
+
while (tok.length) {
|
|
38634
|
+
const t = tok.shift();
|
|
38635
|
+
if (isWord2(t, "left") || isWord2(t, "right")) side = t.word.toLowerCase();
|
|
38636
|
+
else throw new PlaybookParseError(`formation: unexpected token "${tokDisplay(t)}"`, ln);
|
|
38637
|
+
}
|
|
38638
|
+
if (name === "trips-right") ast.formationSide = "right";
|
|
38639
|
+
else if (name === "trips-left") ast.formationSide = "left";
|
|
38640
|
+
else if (side) ast.formationSide = side;
|
|
38641
|
+
}
|
|
38642
|
+
function parseDefense(tok, ast, ln) {
|
|
38643
|
+
const d = parseId2(tok.shift(), "a defensive scheme", ln).toLowerCase();
|
|
38644
|
+
if (!DEFENSES.includes(d)) throw new PlaybookParseError(`unknown defense "${d}"`, ln);
|
|
38645
|
+
ast.defense = d;
|
|
38646
|
+
}
|
|
38647
|
+
function parsePlayer(tok, ast, ln) {
|
|
38648
|
+
const id = parseId2(tok.shift(), "a player id", ln);
|
|
38649
|
+
const posRaw = parseId2(tok.shift(), "a position", ln).toLowerCase();
|
|
38650
|
+
if (!POSITIONS.includes(posRaw)) throw new PlaybookParseError(`unknown position "${posRaw}"`, ln);
|
|
38651
|
+
const isDef = posRaw === "x" || posRaw === "dl" || posRaw === "lb" || posRaw === "db" || posRaw === "s";
|
|
38652
|
+
const p = { id, side: isDef ? "defense" : "offense", pos: posRaw, label: id, line: ln };
|
|
38653
|
+
while (tok.length) {
|
|
38654
|
+
const t = tok.shift();
|
|
38655
|
+
if (isStr2(t)) p.label = t.str;
|
|
38656
|
+
else if (isWord2(t, "label")) {
|
|
38657
|
+
const lt = tok.shift();
|
|
38658
|
+
p.label = isStr2(lt) ? lt.str : parseId2(lt, "a label", ln);
|
|
38659
|
+
} else if (isWord2(t, "at")) {
|
|
38660
|
+
const c = COORD.exec(parseId2(tok.shift(), "x,y", ln));
|
|
38661
|
+
if (!c) throw new PlaybookParseError(`expected "x,y" after at`, ln);
|
|
38662
|
+
p.at = { x: Number(c[1]), y: Number(c[2]) };
|
|
38663
|
+
} else if (isWord2(t, "side")) {
|
|
38664
|
+
const sv = parseId2(tok.shift(), "side", ln).toLowerCase();
|
|
38665
|
+
if (sv !== "offense" && sv !== "defense") throw new PlaybookParseError(`side must be offense|defense`, ln);
|
|
38666
|
+
p.side = sv;
|
|
38667
|
+
} else throw new PlaybookParseError(`player: unexpected token "${tokDisplay(t)}"`, ln);
|
|
38668
|
+
}
|
|
38669
|
+
ast.players.push(p);
|
|
38670
|
+
}
|
|
38671
|
+
function splitNameDir(token) {
|
|
38672
|
+
const m = /^(.*)-(left|right)$/.exec(token.toLowerCase());
|
|
38673
|
+
if (m) return { name: m[1], dir: m[2] };
|
|
38674
|
+
return { name: token.toLowerCase() };
|
|
38675
|
+
}
|
|
38676
|
+
function parseDests(tok) {
|
|
38677
|
+
const pts = [];
|
|
38678
|
+
while (tok.length) {
|
|
38679
|
+
const t = tok.shift();
|
|
38680
|
+
if (isWord2(t, "to") || isWord2(t, "then") || isWord2(t, "->")) continue;
|
|
38681
|
+
if (!isWord2(t)) continue;
|
|
38682
|
+
const c = COORD.exec(t.word);
|
|
38683
|
+
if (c) {
|
|
38684
|
+
const rel = /^[+]/.test(c[1]) || /^[+]/.test(c[2]);
|
|
38685
|
+
pts.push({ x: Number(c[1]), y: Number(c[2]), rel });
|
|
38686
|
+
} else {
|
|
38687
|
+
pts.push({ ref: t.word });
|
|
38688
|
+
}
|
|
38689
|
+
}
|
|
38690
|
+
return pts;
|
|
38691
|
+
}
|
|
38692
|
+
function parseRouteRun(kind, tok, ast, ln) {
|
|
38693
|
+
const player = parseId2(tok.shift(), "a player id", ln);
|
|
38694
|
+
const m = { player, kind, line: ln };
|
|
38695
|
+
const first = tok[0];
|
|
38696
|
+
if (isWord2(first, "to") || isWord2(first, "path")) {
|
|
38697
|
+
tok.shift();
|
|
38698
|
+
m.points = parseDests(tok);
|
|
38699
|
+
ast.moves.push(m);
|
|
38700
|
+
return;
|
|
38701
|
+
}
|
|
38702
|
+
if (isWord2(first)) {
|
|
38703
|
+
const { name, dir } = splitNameDir(first.word);
|
|
38704
|
+
if (NAMED_ROUTES.includes(name)) {
|
|
38705
|
+
tok.shift();
|
|
38706
|
+
m.named = name;
|
|
38707
|
+
if (dir) m.dir = dir;
|
|
38708
|
+
if (kind === "route" && RUN_CONCEPTS.has(name)) m.kind = "run";
|
|
38709
|
+
while (tok.length) {
|
|
38710
|
+
const t = tok.shift();
|
|
38711
|
+
if (isWord2(t) && DIRS.includes(t.word.toLowerCase())) m.dir = t.word.toLowerCase();
|
|
38712
|
+
else if (isWord2(t) && Number.isFinite(Number(t.word))) m.depth = Number(t.word);
|
|
38713
|
+
else if (isWord2(t, "depth")) m.depth = parseNum2(tok.shift(), "depth", ln);
|
|
38714
|
+
else throw new PlaybookParseError(`${kind}: unexpected token "${tokDisplay(t)}"`, ln);
|
|
38715
|
+
}
|
|
38716
|
+
ast.moves.push(m);
|
|
38717
|
+
return;
|
|
38718
|
+
}
|
|
38719
|
+
}
|
|
38720
|
+
m.points = parseDests(tok);
|
|
38721
|
+
if (m.points.length === 0) throw new PlaybookParseError(`${kind} ${player}: needs a route name or destination`, ln);
|
|
38722
|
+
ast.moves.push(m);
|
|
38723
|
+
}
|
|
38724
|
+
function parsePoly(kind, tok, ast, ln) {
|
|
38725
|
+
const player = parseId2(tok.shift(), "a player id", ln);
|
|
38726
|
+
const points = parseDests(tok);
|
|
38727
|
+
if (points.length === 0) throw new PlaybookParseError(`${kind} ${player}: needs a destination (x,y or a landmark)`, ln);
|
|
38728
|
+
ast.moves.push({ player, kind, points, line: ln });
|
|
38729
|
+
}
|
|
38730
|
+
function parseTargeted(kind, tok, ast, ln, optionalTarget = false) {
|
|
38731
|
+
const player = parseId2(tok.shift(), "a player id", ln);
|
|
38732
|
+
const nxt = tok[0];
|
|
38733
|
+
if (isWord2(nxt, "to")) tok.shift();
|
|
38734
|
+
const tgt = tok.shift();
|
|
38735
|
+
if (!tgt) {
|
|
38736
|
+
if (optionalTarget) {
|
|
38737
|
+
ast.moves.push({ player, kind, line: ln });
|
|
38738
|
+
return;
|
|
38739
|
+
}
|
|
38740
|
+
throw new PlaybookParseError(`${kind} ${player}: needs a target (player id, landmark, or x,y)`, ln);
|
|
38741
|
+
}
|
|
38742
|
+
const target = isWord2(tgt) ? tgt.word : tgt.str;
|
|
38743
|
+
ast.moves.push({ player, kind, target, line: ln });
|
|
38744
|
+
}
|
|
38745
|
+
function parsePull(tok, ast, ln) {
|
|
38746
|
+
const player = parseId2(tok.shift(), "a lineman id", ln);
|
|
38747
|
+
const dt = tok.shift();
|
|
38748
|
+
const { dir } = splitNameDir(isWord2(dt) ? dt.word : "");
|
|
38749
|
+
let d = dir;
|
|
38750
|
+
if (!d && isWord2(dt) && DIRS.includes(dt.word.toLowerCase())) d = dt.word.toLowerCase();
|
|
38751
|
+
if (!d) throw new PlaybookParseError(`pull: expected left|right`, ln);
|
|
38752
|
+
ast.moves.push({ player, kind: "pull", named: "power", dir: d, line: ln });
|
|
38753
|
+
}
|
|
38754
|
+
function parseMotion(tok, ast, ln) {
|
|
38755
|
+
const player = parseId2(tok.shift(), "a player id", ln);
|
|
38756
|
+
const m = { player, kind: "motion", line: ln };
|
|
38757
|
+
const dt = tok.shift();
|
|
38758
|
+
if (isWord2(dt) && DIRS.includes(dt.word.toLowerCase())) {
|
|
38759
|
+
m.dir = dt.word.toLowerCase();
|
|
38760
|
+
const n = tok.shift();
|
|
38761
|
+
if (isWord2(n) && Number.isFinite(Number(n.word))) m.depth = Number(n.word);
|
|
38762
|
+
} else if (isWord2(dt, "to")) {
|
|
38763
|
+
m.points = parseDests(tok);
|
|
38764
|
+
} else if (isWord2(dt)) {
|
|
38765
|
+
const c = COORD.exec(dt.word);
|
|
38766
|
+
if (c) m.points = [{ x: Number(c[1]), y: Number(c[2]), rel: /^[+]/.test(c[1]) }];
|
|
38767
|
+
else m.points = [{ ref: dt.word }];
|
|
38768
|
+
} else throw new PlaybookParseError(`motion: expected left|right or a destination`, ln);
|
|
38769
|
+
ast.moves.push(m);
|
|
38770
|
+
}
|
|
38771
|
+
function parseZone(tok, ast, ln) {
|
|
38772
|
+
const z = { x: 0, y: 0, rx: 6, ry: 5, line: ln };
|
|
38773
|
+
while (tok.length) {
|
|
38774
|
+
const t = tok.shift();
|
|
38775
|
+
if (isStr2(t)) z.label = t.str;
|
|
38776
|
+
else if (isWord2(t, "at")) {
|
|
38777
|
+
const c = COORD.exec(parseId2(tok.shift(), "x,y", ln));
|
|
38778
|
+
if (!c) throw new PlaybookParseError(`zone: expected "x,y" after at`, ln);
|
|
38779
|
+
z.x = Number(c[1]);
|
|
38780
|
+
z.y = Number(c[2]);
|
|
38781
|
+
} else if (isWord2(t, "size")) {
|
|
38782
|
+
const st = tok.shift();
|
|
38783
|
+
const m = /^(\d*\.?\d+)x(\d*\.?\d+)$/i.exec(isWord2(st) ? st.word : "");
|
|
38784
|
+
if (!m) throw new PlaybookParseError(`zone size expects "rxXry"`, ln);
|
|
38785
|
+
z.rx = Number(m[1]);
|
|
38786
|
+
z.ry = Number(m[2]);
|
|
38787
|
+
} else if (isWord2(t, "label")) {
|
|
38788
|
+
const lt = tok.shift();
|
|
38789
|
+
z.label = isStr2(lt) ? lt.str : parseId2(lt, "a label", ln);
|
|
38790
|
+
} else throw new PlaybookParseError(`zone: unexpected token "${tokDisplay(t)}"`, ln);
|
|
38791
|
+
}
|
|
38792
|
+
ast.zones.push(z);
|
|
38793
|
+
}
|
|
38794
|
+
function parseHandoff(tok, ast, ln) {
|
|
38795
|
+
const from = parseId2(tok.shift(), "the source id", ln);
|
|
38796
|
+
const to = parseId2(tok.shift(), "the target id", ln);
|
|
38797
|
+
ast.moves.push({ player: from, kind: "handoff", target: to, line: ln });
|
|
38798
|
+
}
|
|
38799
|
+
function parsePlaybook(text2) {
|
|
38800
|
+
const ast = {
|
|
38801
|
+
type: "playbook",
|
|
38802
|
+
title: "Football Play",
|
|
38803
|
+
sport: "football",
|
|
38804
|
+
down: 0,
|
|
38805
|
+
distance: 0,
|
|
38806
|
+
hash: "nfl",
|
|
38807
|
+
view: "auto",
|
|
38808
|
+
formationSide: "right",
|
|
38809
|
+
players: [],
|
|
38810
|
+
moves: [],
|
|
38811
|
+
zones: []
|
|
38812
|
+
};
|
|
38813
|
+
let sawHeader = false;
|
|
38814
|
+
let titleSetByUser = false;
|
|
38815
|
+
const lines = text2.split(/\r?\n/);
|
|
38816
|
+
for (let i = 0; i < lines.length; i++) {
|
|
38817
|
+
const ln = i + 1;
|
|
38818
|
+
const raw = normalizeQuotes4(lines[i]).trim();
|
|
38819
|
+
if (!raw) continue;
|
|
38820
|
+
const all = tokenize8(raw);
|
|
38821
|
+
const tok = [];
|
|
38822
|
+
for (const t of all) {
|
|
38823
|
+
if (isWord2(t) && (t.word.startsWith("#") || t.word.startsWith("//"))) break;
|
|
38824
|
+
tok.push(t);
|
|
38825
|
+
}
|
|
38826
|
+
if (tok.length === 0) continue;
|
|
38827
|
+
const head = tok.shift();
|
|
38828
|
+
if (!isWord2(head)) throw new PlaybookParseError(`unexpected string at line start`, ln);
|
|
38829
|
+
const kw = head.word.toLowerCase();
|
|
38830
|
+
if (kw === "playbook") {
|
|
38831
|
+
const before = ast.title;
|
|
38832
|
+
parseHeader4(tok, ast, ln);
|
|
38833
|
+
if (ast.title !== before) titleSetByUser = true;
|
|
38834
|
+
sawHeader = true;
|
|
38835
|
+
continue;
|
|
38836
|
+
}
|
|
38837
|
+
if (!sawHeader) throw new PlaybookParseError(`the first statement must be the "playbook" header`, ln);
|
|
38838
|
+
if (kw === "field") parseField(tok, ast, ln);
|
|
38839
|
+
else if (kw === "view") {
|
|
38840
|
+
const v = parseId2(tok.shift(), "view (full|half)", ln).toLowerCase();
|
|
38841
|
+
if (v !== "full" && v !== "half" && v !== "auto") throw new PlaybookParseError(`view must be full|half`, ln);
|
|
38842
|
+
ast.view = v;
|
|
38843
|
+
} else if (kw === "formation" || kw === "set") parseFormation(tok, ast, ln);
|
|
38844
|
+
else if (kw === "defense" || kw === "def") parseDefense(tok, ast, ln);
|
|
38845
|
+
else if (kw === "player") parsePlayer(tok, ast, ln);
|
|
38846
|
+
else if (kw === "route") parseRouteRun("route", tok, ast, ln);
|
|
38847
|
+
else if (kw === "run") parseRouteRun("run", tok, ast, ln);
|
|
38848
|
+
else if (kw === "cut") parsePoly("cut", tok, ast, ln);
|
|
38849
|
+
else if (kw === "move") parsePoly("move", tok, ast, ln);
|
|
38850
|
+
else if (kw === "dribble" || kw === "drive") parsePoly("dribble", tok, ast, ln);
|
|
38851
|
+
else if (kw === "pass") parseTargeted("pass", tok, ast, ln);
|
|
38852
|
+
else if (kw === "screen") parseTargeted("screen", tok, ast, ln);
|
|
38853
|
+
else if (kw === "block") parseTargeted("block", tok, ast, ln);
|
|
38854
|
+
else if (kw === "shot" || kw === "shoot") parseTargeted("shot", tok, ast, ln, true);
|
|
38855
|
+
else if (kw === "pull") parsePull(tok, ast, ln);
|
|
38856
|
+
else if (kw === "handoff") parseHandoff(tok, ast, ln);
|
|
38857
|
+
else if (kw === "motion") parseMotion(tok, ast, ln);
|
|
38858
|
+
else if (kw === "zone") parseZone(tok, ast, ln);
|
|
38859
|
+
else throw new PlaybookParseError(`unknown keyword "${kw}"`, ln);
|
|
38860
|
+
}
|
|
38861
|
+
if (!titleSetByUser) {
|
|
38862
|
+
ast.title = ast.sport === "basketball" ? "Basketball Play" : ast.sport === "soccer" ? "Soccer Tactic" : "Football Play";
|
|
38863
|
+
}
|
|
38864
|
+
return ast;
|
|
38865
|
+
}
|
|
38866
|
+
|
|
38867
|
+
// src/diagrams/playbook/geometry/football.ts
|
|
38868
|
+
var K = {
|
|
38869
|
+
scale: 14,
|
|
38870
|
+
olSplit: 1.4,
|
|
38871
|
+
wrSplit: 13,
|
|
38872
|
+
slotSplit: 6,
|
|
38873
|
+
minHalfWidth: 20,
|
|
38874
|
+
margin: 2,
|
|
38875
|
+
hashNfl: 3.08,
|
|
38876
|
+
hashCollege: 6.67
|
|
38877
|
+
};
|
|
38878
|
+
var r25 = (n) => Math.round(n * 100) / 100;
|
|
38879
|
+
var round22 = (p) => ({ x: Math.round(p.x * 100) / 100, y: Math.round(p.y * 100) / 100 });
|
|
38880
|
+
var dirSign = (d) => d === "right" ? 1 : -1;
|
|
38881
|
+
function offensiveLine() {
|
|
38882
|
+
return [
|
|
38883
|
+
{ id: "LT", pos: "ol", label: "T", x: -2 * K.olSplit, y: 0 },
|
|
38884
|
+
{ id: "LG", pos: "ol", label: "G", x: -1 * K.olSplit, y: 0 },
|
|
38885
|
+
{ id: "C", pos: "c", label: "C", x: 0, y: 0 },
|
|
38886
|
+
{ id: "RG", pos: "ol", label: "G", x: 1 * K.olSplit, y: 0 },
|
|
38887
|
+
{ id: "RT", pos: "ol", label: "T", x: 2 * K.olSplit, y: 0 }
|
|
38888
|
+
];
|
|
38889
|
+
}
|
|
38890
|
+
function tightEnd(side) {
|
|
38891
|
+
const s = side === "right" ? 1 : -1;
|
|
38892
|
+
return { id: "Y", pos: "te", label: "Y", x: s * (2 * K.olSplit + 1.3), y: 0 };
|
|
38893
|
+
}
|
|
38894
|
+
function formationRoster(formation, side) {
|
|
38895
|
+
const s = side === "right" ? 1 : -1;
|
|
38896
|
+
const ol = offensiveLine();
|
|
38897
|
+
const X = { id: "X", pos: "wr", label: "X", x: -s * K.wrSplit, y: 0 };
|
|
38898
|
+
const Z = { id: "Z", pos: "wr", label: "Z", x: s * K.wrSplit, y: -1 };
|
|
38899
|
+
switch (formation) {
|
|
38900
|
+
case "i-form":
|
|
38901
|
+
return [
|
|
38902
|
+
...ol,
|
|
38903
|
+
tightEnd(side),
|
|
38904
|
+
{ id: "QB", pos: "qb", label: "QB", x: 0, y: -1.5 },
|
|
38905
|
+
{ id: "FB", pos: "rb", label: "F", x: 0, y: -3.6 },
|
|
38906
|
+
{ id: "RB", pos: "rb", label: "RB", x: 0, y: -6.2 },
|
|
38907
|
+
X,
|
|
38908
|
+
Z
|
|
38909
|
+
];
|
|
38910
|
+
case "wishbone":
|
|
38911
|
+
return [
|
|
38912
|
+
...ol,
|
|
38913
|
+
tightEnd(side),
|
|
38914
|
+
{ id: "QB", pos: "qb", label: "QB", x: 0, y: -1.5 },
|
|
38915
|
+
{ id: "FB", pos: "rb", label: "F", x: 0, y: -3.4 },
|
|
38916
|
+
{ id: "LH", pos: "rb", label: "H", x: -3.5, y: -5.2 },
|
|
38917
|
+
{ id: "RB", pos: "rb", label: "H", x: 3.5, y: -5.2 },
|
|
38918
|
+
X
|
|
38919
|
+
];
|
|
38920
|
+
case "singleback":
|
|
38921
|
+
return [
|
|
38922
|
+
...ol,
|
|
38923
|
+
tightEnd(side),
|
|
38924
|
+
{ id: "QB", pos: "qb", label: "QB", x: 0, y: -1.5 },
|
|
38925
|
+
{ id: "RB", pos: "rb", label: "RB", x: 0, y: -5 },
|
|
38926
|
+
X,
|
|
38927
|
+
{ id: "H", pos: "wr", label: "H", x: s * K.slotSplit, y: -1 },
|
|
38928
|
+
Z
|
|
38929
|
+
];
|
|
38930
|
+
case "pistol":
|
|
38931
|
+
return [
|
|
38932
|
+
...ol,
|
|
38933
|
+
tightEnd(side),
|
|
38934
|
+
{ id: "QB", pos: "qb", label: "QB", x: 0, y: -4 },
|
|
38935
|
+
{ id: "RB", pos: "rb", label: "RB", x: 0, y: -7 },
|
|
38936
|
+
X,
|
|
38937
|
+
Z
|
|
38938
|
+
];
|
|
38939
|
+
case "shotgun":
|
|
38940
|
+
return [
|
|
38941
|
+
...ol,
|
|
38942
|
+
tightEnd(side),
|
|
38943
|
+
{ id: "QB", pos: "qb", label: "QB", x: 0, y: -5 },
|
|
38944
|
+
{ id: "RB", pos: "rb", label: "RB", x: s * 1.8, y: -5 },
|
|
38945
|
+
X,
|
|
38946
|
+
Z
|
|
38947
|
+
];
|
|
38948
|
+
case "spread":
|
|
38949
|
+
return [
|
|
38950
|
+
...ol,
|
|
38951
|
+
{ id: "QB", pos: "qb", label: "QB", x: 0, y: -5 },
|
|
38952
|
+
{ id: "RB", pos: "rb", label: "RB", x: s * 1.8, y: -5 },
|
|
38953
|
+
{ id: "X", pos: "wr", label: "X", x: -13 - 1, y: 0 },
|
|
38954
|
+
{ id: "H", pos: "wr", label: "H", x: -6, y: -1 },
|
|
38955
|
+
{ id: "Y", pos: "wr", label: "Y", x: K.slotSplit, y: -1 },
|
|
38956
|
+
{ id: "Z", pos: "wr", label: "Z", x: K.wrSplit + 1, y: 0 }
|
|
38957
|
+
];
|
|
38958
|
+
case "trips":
|
|
38959
|
+
case "trips-right":
|
|
38960
|
+
case "trips-left": {
|
|
38961
|
+
const t = formation === "trips-left" ? -1 : formation === "trips-right" ? 1 : s;
|
|
38962
|
+
return [
|
|
38963
|
+
...ol,
|
|
38964
|
+
{ id: "QB", pos: "qb", label: "QB", x: 0, y: -5 },
|
|
38965
|
+
{ id: "RB", pos: "rb", label: "RB", x: -t * 1.8, y: -5 },
|
|
38966
|
+
{ id: "X", pos: "wr", label: "X", x: -t * (K.wrSplit + 1), y: 0 },
|
|
38967
|
+
{ id: "Z", pos: "wr", label: "Z", x: t * (K.wrSplit + 1), y: 0 },
|
|
38968
|
+
{ id: "H", pos: "wr", label: "H", x: t * 9, y: -1 },
|
|
38969
|
+
{ id: "Y", pos: "wr", label: "Y", x: t * 5, y: -1 }
|
|
38970
|
+
];
|
|
38971
|
+
}
|
|
38972
|
+
case "empty":
|
|
38973
|
+
return [
|
|
38974
|
+
...ol,
|
|
38975
|
+
{ id: "QB", pos: "qb", label: "QB", x: 0, y: -5 },
|
|
38976
|
+
{ id: "X", pos: "wr", label: "X", x: -13 - 1, y: 0 },
|
|
38977
|
+
{ id: "H", pos: "wr", label: "H", x: -6 - 1, y: -1 },
|
|
38978
|
+
{ id: "Z", pos: "wr", label: "Z", x: K.wrSplit + 1, y: 0 },
|
|
38979
|
+
{ id: "Y", pos: "wr", label: "Y", x: K.slotSplit + 1, y: -1 },
|
|
38980
|
+
{ id: "H2", pos: "wr", label: "h", x: K.slotSplit - 2, y: -1 }
|
|
38981
|
+
];
|
|
38982
|
+
case "goal-line":
|
|
38983
|
+
return [
|
|
38984
|
+
...ol,
|
|
38985
|
+
{ id: "Y", pos: "te", label: "Y", x: 2 * K.olSplit + 1.3, y: 0 },
|
|
38986
|
+
{ id: "TE2", pos: "te", label: "Y", x: -4.1, y: 0 },
|
|
38987
|
+
{ id: "QB", pos: "qb", label: "QB", x: 0, y: -1.5 },
|
|
38988
|
+
{ id: "FB", pos: "rb", label: "F", x: 0, y: -3.4 },
|
|
38989
|
+
{ id: "RB", pos: "rb", label: "RB", x: 0, y: -5.4 },
|
|
38990
|
+
{ id: "Z", pos: "wr", label: "Z", x: s * 9, y: 0 }
|
|
38991
|
+
];
|
|
38992
|
+
default:
|
|
38993
|
+
return [...ol, { id: "QB", pos: "qb", label: "QB", x: 0, y: -1.5 }];
|
|
38994
|
+
}
|
|
38995
|
+
}
|
|
38996
|
+
function front43(side) {
|
|
38997
|
+
const s = side === "right" ? 1 : -1;
|
|
38998
|
+
return [
|
|
38999
|
+
{ id: "DE_W", pos: "dl", label: "E", x: -3.6, y: 1 },
|
|
39000
|
+
{ id: "DT_W", pos: "dl", label: "T", x: -1.2, y: 1 },
|
|
39001
|
+
{ id: "DT_S", pos: "dl", label: "T", x: 1.2, y: 1 },
|
|
39002
|
+
{ id: "DE_S", pos: "dl", label: "E", x: 3.6, y: 1 },
|
|
39003
|
+
{ id: "WLB", pos: "lb", label: "W", x: -4.5, y: 4.3 },
|
|
39004
|
+
{ id: "MLB", pos: "lb", label: "M", x: 0, y: 4.8 },
|
|
39005
|
+
{ id: "SLB", pos: "lb", label: "S", x: s * 4.5, y: 4.3 }
|
|
39006
|
+
];
|
|
39007
|
+
}
|
|
39008
|
+
function front34(side) {
|
|
39009
|
+
const s = side === "right" ? 1 : -1;
|
|
39010
|
+
return [
|
|
39011
|
+
{ id: "DE_W", pos: "dl", label: "E", x: -2.6, y: 1 },
|
|
39012
|
+
{ id: "NT", pos: "dl", label: "N", x: 0, y: 1 },
|
|
39013
|
+
{ id: "DE_S", pos: "dl", label: "E", x: 2.6, y: 1 },
|
|
39014
|
+
{ id: "WOLB", pos: "lb", label: "W", x: -5.4, y: 3.6 },
|
|
39015
|
+
{ id: "WILB", pos: "lb", label: "M", x: -1.6, y: 4.6 },
|
|
39016
|
+
{ id: "SILB", pos: "lb", label: "M", x: 1.6, y: 4.6 },
|
|
39017
|
+
{ id: "SOLB", pos: "lb", label: "S", x: s * 5.4, y: 3.6 }
|
|
39018
|
+
];
|
|
39019
|
+
}
|
|
39020
|
+
function defensePreset(scheme, side) {
|
|
39021
|
+
const s = side === "right" ? 1 : -1;
|
|
39022
|
+
const cb = (sign, y, label = "C") => ({ id: sign < 0 ? "LCB" : "RCB", pos: "db", label, x: sign * K.wrSplit, y });
|
|
39023
|
+
let front;
|
|
39024
|
+
let cover;
|
|
39025
|
+
let showZones = true;
|
|
39026
|
+
if (scheme === "3-4") {
|
|
39027
|
+
front = front34(side);
|
|
39028
|
+
cover = "cover-2";
|
|
39029
|
+
showZones = false;
|
|
39030
|
+
} else if (scheme === "4-4") {
|
|
39031
|
+
front = [...front43(side), { id: "SS", pos: "s", label: "$", x: s * 6, y: 4.6 }];
|
|
39032
|
+
cover = "cover-3";
|
|
39033
|
+
} else if (scheme === "nickel") {
|
|
39034
|
+
front = front43(side).slice(0, 6);
|
|
39035
|
+
cover = "cover-2";
|
|
39036
|
+
} else if (scheme === "dime") {
|
|
39037
|
+
front = front43(side).slice(0, 5);
|
|
39038
|
+
cover = "cover-3";
|
|
39039
|
+
} else if (scheme.startsWith("cover")) {
|
|
39040
|
+
front = front43(side);
|
|
39041
|
+
cover = scheme;
|
|
39042
|
+
} else {
|
|
39043
|
+
front = front43(side);
|
|
39044
|
+
cover = "cover-2";
|
|
39045
|
+
showZones = false;
|
|
39046
|
+
}
|
|
39047
|
+
const sec = [];
|
|
39048
|
+
const zones = [];
|
|
39049
|
+
switch (cover) {
|
|
39050
|
+
case "cover-0":
|
|
39051
|
+
sec.push(cb(-1, 5.5), cb(1, 5.5), { id: "FS", pos: "s", label: "F", x: -6, y: 5 }, { id: "SS", pos: "s", label: "$", x: s * 6, y: 5 });
|
|
39052
|
+
break;
|
|
39053
|
+
case "cover-1":
|
|
39054
|
+
sec.push(cb(-1, 6), cb(1, 6), { id: "SS", pos: "s", label: "$", x: s * 6, y: 5 }, { id: "FS", pos: "s", label: "F", x: 0, y: 13.5 });
|
|
39055
|
+
zones.push({ x: 0, y: 13.5, rx: 5, ry: 3.5, label: "deep middle" });
|
|
39056
|
+
break;
|
|
39057
|
+
case "cover-2":
|
|
39058
|
+
sec.push(cb(-1, 5), cb(1, 5), { id: "FS", pos: "s", label: "F", x: -9, y: 12.5 }, { id: "SS", pos: "s", label: "$", x: 9, y: 12.5 });
|
|
39059
|
+
zones.push({ x: -9, y: 12.5, rx: 9, ry: 4, label: "deep half" }, { x: 9, y: 12.5, rx: 9, ry: 4, label: "deep half" });
|
|
39060
|
+
break;
|
|
39061
|
+
case "cover-3":
|
|
39062
|
+
sec.push(cb(-1, 11.5), cb(1, 11.5), { id: "FS", pos: "s", label: "F", x: 0, y: 14 }, { id: "SS", pos: "s", label: "$", x: s * 6, y: 5 });
|
|
39063
|
+
zones.push({ x: -13.5, y: 12, rx: 5.8, ry: 3.6, label: "deep \u2153" }, { x: 0, y: 14.5, rx: 5.8, ry: 3.6, label: "deep \u2153" }, { x: 13.5, y: 12, rx: 5.8, ry: 3.6, label: "deep \u2153" });
|
|
39064
|
+
break;
|
|
39065
|
+
case "cover-4":
|
|
39066
|
+
sec.push(cb(-1, 10), cb(1, 10), { id: "FS", pos: "s", label: "F", x: -6, y: 12.5 }, { id: "SS", pos: "s", label: "$", x: 6, y: 12.5 });
|
|
39067
|
+
zones.push({ x: -13, y: 12, rx: 6.5, ry: 4, label: "deep \xBC" }, { x: -5, y: 13, rx: 6.5, ry: 4, label: "deep \xBC" }, { x: 5, y: 13, rx: 6.5, ry: 4, label: "deep \xBC" }, { x: 13, y: 12, rx: 6.5, ry: 4, label: "deep \xBC" });
|
|
39068
|
+
break;
|
|
39069
|
+
case "cover-6":
|
|
39070
|
+
sec.push(cb(-1, 10), cb(1, 5), { id: "FS", pos: "s", label: "F", x: -7, y: 12.5 }, { id: "SS", pos: "s", label: "$", x: 9, y: 12.5 });
|
|
39071
|
+
zones.push({ x: -7, y: 12.5, rx: 7, ry: 4, label: "deep \xBC" }, { x: -13, y: 12, rx: 6.5, ry: 4, label: "deep \xBC" }, { x: 9, y: 12.5, rx: 9, ry: 4, label: "deep \xBD" });
|
|
39072
|
+
break;
|
|
39073
|
+
default:
|
|
39074
|
+
sec.push(cb(-1, 5), cb(1, 5), { id: "FS", pos: "s", label: "F", x: -9, y: 12.5 }, { id: "SS", pos: "s", label: "$", x: 9, y: 12.5 });
|
|
39075
|
+
}
|
|
39076
|
+
return { defenders: [...front, ...sec].slice(0, 11), zones: showZones ? zones : [] };
|
|
39077
|
+
}
|
|
39078
|
+
function passRoute(named, depth, outSign, inSign) {
|
|
39079
|
+
const d = depth;
|
|
39080
|
+
switch (named) {
|
|
39081
|
+
case "go":
|
|
39082
|
+
case "fly":
|
|
39083
|
+
case "streak":
|
|
39084
|
+
case "vertical":
|
|
39085
|
+
return [{ x: 0, y: 0 }, { x: 0, y: d ?? 16 }];
|
|
39086
|
+
case "seam":
|
|
39087
|
+
return [{ x: 0, y: 0 }, { x: inSign * 0.5, y: d ?? 14 }];
|
|
39088
|
+
case "slant":
|
|
39089
|
+
return [{ x: 0, y: 0 }, { x: 0, y: 1.6 }, { x: inSign * 4.5, y: 6 }];
|
|
39090
|
+
case "flat":
|
|
39091
|
+
return [{ x: 0, y: 0 }, { x: outSign * 1.5, y: 1 }, { x: outSign * 5.5, y: 2.2 }];
|
|
39092
|
+
case "hitch":
|
|
39093
|
+
return [{ x: 0, y: 0 }, { x: 0, y: d ?? 5.5 }, { x: inSign * 1, y: (d ?? 5.5) - 1.2 }];
|
|
39094
|
+
case "out":
|
|
39095
|
+
return [{ x: 0, y: 0 }, { x: 0, y: d ?? 10 }, { x: outSign * 5.5, y: d ?? 10 }];
|
|
39096
|
+
case "in":
|
|
39097
|
+
case "dig":
|
|
39098
|
+
return [{ x: 0, y: 0 }, { x: 0, y: d ?? 10 }, { x: inSign * 6.5, y: d ?? 10 }];
|
|
39099
|
+
case "curl":
|
|
39100
|
+
return [{ x: 0, y: 0 }, { x: 0, y: d ?? 12 }, { x: inSign * 1.6, y: (d ?? 12) - 1.6 }];
|
|
39101
|
+
case "comeback":
|
|
39102
|
+
return [{ x: 0, y: 0 }, { x: 0, y: d ?? 14 }, { x: outSign * 2.2, y: (d ?? 14) - 2.4 }];
|
|
39103
|
+
case "corner":
|
|
39104
|
+
case "flag":
|
|
39105
|
+
return [{ x: 0, y: 0 }, { x: 0, y: d ?? 10 }, { x: outSign * 5.5, y: (d ?? 10) + 5 }];
|
|
39106
|
+
case "post":
|
|
39107
|
+
return [{ x: 0, y: 0 }, { x: 0, y: d ?? 10 }, { x: inSign * 5.5, y: (d ?? 10) + 6 }];
|
|
39108
|
+
case "wheel":
|
|
39109
|
+
return [{ x: 0, y: 0 }, { x: outSign * 3, y: 1 }, { x: outSign * 4, y: d ?? 11 }];
|
|
39110
|
+
case "cross":
|
|
39111
|
+
case "drag":
|
|
39112
|
+
return [{ x: 0, y: 0 }, { x: 0, y: 1.5 }, { x: inSign * 14, y: 3.2 }];
|
|
39113
|
+
case "screen":
|
|
39114
|
+
return [{ x: 0, y: 0 }, { x: 0, y: -1 }, { x: outSign * 4.5, y: -1 }];
|
|
39115
|
+
default:
|
|
39116
|
+
return [{ x: 0, y: 0 }, { x: 0, y: d ?? 10 }];
|
|
39117
|
+
}
|
|
39118
|
+
}
|
|
39119
|
+
function runPath(named, start, dir) {
|
|
39120
|
+
const k = dirSign(dir);
|
|
39121
|
+
switch (named) {
|
|
39122
|
+
case "dive":
|
|
39123
|
+
case "trap":
|
|
39124
|
+
return [start, { x: k * 1, y: 0.5 }, { x: k * 1.6, y: 3.2 }];
|
|
39125
|
+
case "iso":
|
|
39126
|
+
return [start, { x: k * 0.4, y: 0.5 }, { x: k * 0.8, y: 3.4 }];
|
|
39127
|
+
case "power":
|
|
39128
|
+
return [start, { x: start.x + k * 1.4, y: start.y + 1.5 }, { x: k * 2.6, y: 0.6 }, { x: k * 3.6, y: 4.2 }];
|
|
39129
|
+
case "counter":
|
|
39130
|
+
return [start, { x: -k * 1.6, y: start.y + 0.3 }, { x: k * 2.2, y: 0.6 }, { x: k * 3.2, y: 4.2 }];
|
|
39131
|
+
case "sweep":
|
|
39132
|
+
case "toss":
|
|
39133
|
+
return [start, { x: k * 5, y: start.y + 0.6 }, { x: k * 7, y: 1.2 }, { x: k * 7, y: 5 }];
|
|
39134
|
+
case "draw":
|
|
39135
|
+
return [start, { x: 0, y: start.y + 1.5 }, { x: k * 1, y: 3.4 }];
|
|
39136
|
+
default:
|
|
39137
|
+
return [start, { x: k * 1.4, y: 3.2 }];
|
|
39138
|
+
}
|
|
39139
|
+
}
|
|
39140
|
+
var footballModule = {
|
|
39141
|
+
scale: K.scale,
|
|
39142
|
+
yUp: true,
|
|
39143
|
+
buildPlayers(ast) {
|
|
39144
|
+
const players = [];
|
|
39145
|
+
const byId = /* @__PURE__ */ new Map();
|
|
39146
|
+
const add = (p) => {
|
|
39147
|
+
if (byId.has(p.id)) players[byId.get(p.id)] = p;
|
|
39148
|
+
else {
|
|
39149
|
+
byId.set(p.id, players.length);
|
|
39150
|
+
players.push(p);
|
|
39151
|
+
}
|
|
39152
|
+
};
|
|
39153
|
+
if (ast.formation) for (const sl of formationRoster(ast.formation, ast.formationSide)) add({ id: sl.id, side: "offense", pos: sl.pos, label: sl.label, x: sl.x, y: sl.y });
|
|
39154
|
+
if (ast.defense) for (const d of defensePreset(ast.defense, ast.formationSide).defenders) add({ id: d.id, side: "defense", pos: d.pos, label: d.label, x: d.x, y: d.y });
|
|
39155
|
+
for (const p of ast.players) {
|
|
39156
|
+
const at = p.at ?? (byId.has(p.id) ? { x: players[byId.get(p.id)].x, y: players[byId.get(p.id)].y } : { x: 0, y: 0 });
|
|
39157
|
+
add({ id: p.id, side: p.side, pos: p.pos, label: p.label, x: at.x, y: at.y });
|
|
39158
|
+
}
|
|
39159
|
+
return players;
|
|
39160
|
+
},
|
|
39161
|
+
buildZones(ast) {
|
|
39162
|
+
const zones = [];
|
|
39163
|
+
if (ast.defense) zones.push(...defensePreset(ast.defense, ast.formationSide).zones);
|
|
39164
|
+
for (const z of ast.zones) zones.push({ x: z.x, y: z.y, rx: z.rx, ry: z.ry, label: z.label });
|
|
39165
|
+
return zones;
|
|
39166
|
+
},
|
|
39167
|
+
resolveNamed(m, src, players, byId) {
|
|
39168
|
+
const endFor2 = (kind) => m.end ?? ("arrow");
|
|
39169
|
+
if (m.kind === "pull") {
|
|
39170
|
+
const k = dirSign(m.dir ?? "right");
|
|
39171
|
+
const offTackle = 2 * K.olSplit + 0.8;
|
|
39172
|
+
const pts = [{ x: src.x, y: 0 }, { x: src.x, y: -1.3 }, { x: k * offTackle, y: -1.3 }, { x: k * offTackle, y: 1.6 }];
|
|
39173
|
+
return { player: m.player, kind: "pull", style: "solid", points: pts.map(round22), end: "arrow" };
|
|
39174
|
+
}
|
|
39175
|
+
if ((m.kind === "run" || m.kind === "route") && m.named) {
|
|
39176
|
+
if (m.kind === "run") {
|
|
39177
|
+
return { player: m.player, kind: "run", style: "solid", points: runPath(m.named, src, m.dir ?? "right").map(round22), end: "arrow" };
|
|
39178
|
+
}
|
|
39179
|
+
const outSign = m.dir ? dirSign(m.dir) : src.x >= 0 ? 1 : -1;
|
|
39180
|
+
const rel = passRoute(m.named, m.depth, outSign, -outSign);
|
|
39181
|
+
return { player: m.player, kind: "route", style: "solid", points: rel.map((p) => round22({ x: src.x + p.x, y: src.y + p.y })), end: endFor2() };
|
|
39182
|
+
}
|
|
39183
|
+
return null;
|
|
39184
|
+
},
|
|
39185
|
+
bounds(ast, players, moves, zones) {
|
|
39186
|
+
let minX = -20, maxX = K.minHalfWidth, minY = -7, maxY = 6;
|
|
39187
|
+
const ext = (x, y) => {
|
|
39188
|
+
minX = Math.min(minX, x);
|
|
39189
|
+
maxX = Math.max(maxX, x);
|
|
39190
|
+
minY = Math.min(minY, y);
|
|
39191
|
+
maxY = Math.max(maxY, y);
|
|
39192
|
+
};
|
|
39193
|
+
for (const p of players) ext(p.x, p.y);
|
|
39194
|
+
for (const mv of moves) for (const pt of mv.points) ext(pt.x, pt.y);
|
|
39195
|
+
for (const z of zones) {
|
|
39196
|
+
ext(z.x - z.rx, z.y - z.ry);
|
|
39197
|
+
ext(z.x + z.rx, z.y + z.ry);
|
|
39198
|
+
}
|
|
39199
|
+
if (ast.toGoal !== void 0) maxY = Math.max(maxY, ast.toGoal + 11);
|
|
39200
|
+
const m = K.margin;
|
|
39201
|
+
return { minX: minX - m, maxX: maxX + m, minY: minY - m, maxY: maxY + m };
|
|
39202
|
+
},
|
|
39203
|
+
drawField(lay, ctx, t) {
|
|
39204
|
+
const b = lay.bounds;
|
|
39205
|
+
const parts = [];
|
|
39206
|
+
const x1 = ctx.X(b.minX), x2 = ctx.X(b.maxX);
|
|
39207
|
+
if (lay.toGoal !== void 0) {
|
|
39208
|
+
const gl = lay.toGoal;
|
|
39209
|
+
const ezTop = Math.min(gl + 10, b.maxY);
|
|
39210
|
+
parts.push(rect({ class: "sx-pb-endzone", x: r25(x1), y: r25(ctx.Y(ezTop)), width: r25((b.maxX - b.minX) * K.scale), height: r25((ezTop - gl) * K.scale) }));
|
|
39211
|
+
parts.push(line({ class: "sx-pb-goalline", x1: r25(x1), y1: r25(ctx.Y(gl)), x2: r25(x2), y2: r25(ctx.Y(gl)) }));
|
|
39212
|
+
const gy = ctx.Y(Math.min(gl + 10, b.maxY) - 0.2);
|
|
39213
|
+
const cx = ctx.X(0);
|
|
39214
|
+
const up = ctx.px(2.2);
|
|
39215
|
+
const cross = ctx.px(3);
|
|
39216
|
+
parts.push(line({ class: "sx-pb-goalpost", x1: r25(cx), y1: r25(gy), x2: r25(cx), y2: r25(gy + ctx.px(1)) }));
|
|
39217
|
+
parts.push(line({ class: "sx-pb-goalpost", x1: r25(cx - cross / 2), y1: r25(gy), x2: r25(cx + cross / 2), y2: r25(gy) }));
|
|
39218
|
+
parts.push(line({ class: "sx-pb-goalpost", x1: r25(cx - cross / 2), y1: r25(gy), x2: r25(cx - cross / 2), y2: r25(gy - up) }));
|
|
39219
|
+
parts.push(line({ class: "sx-pb-goalpost", x1: r25(cx + cross / 2), y1: r25(gy), x2: r25(cx + cross / 2), y2: r25(gy - up) }));
|
|
39220
|
+
}
|
|
39221
|
+
const startY = Math.ceil(b.minY / 5) * 5;
|
|
39222
|
+
for (let yd = startY; yd <= b.maxY; yd += 5) {
|
|
39223
|
+
if (Math.abs(yd) < 1e-6) continue;
|
|
39224
|
+
if (lay.toGoal !== void 0 && yd > lay.toGoal + 0.01) continue;
|
|
39225
|
+
parts.push(line({ class: "sx-pb-yard", x1: r25(x1), y1: r25(ctx.Y(yd)), x2: r25(x2), y2: r25(ctx.Y(yd)) }));
|
|
39226
|
+
if (lay.toGoal !== void 0) {
|
|
39227
|
+
const yardNum = lay.toGoal - yd;
|
|
39228
|
+
if (yardNum > 0 && yardNum % 10 === 0) {
|
|
39229
|
+
parts.push(text({ class: "sx-pb-yardnum", x: r25(x1 + 12), y: r25(ctx.Y(yd) + 4), "text-anchor": "middle" }, String(yardNum)));
|
|
39230
|
+
parts.push(text({ class: "sx-pb-yardnum", x: r25(x2 - 12), y: r25(ctx.Y(yd) + 4), "text-anchor": "middle" }, String(yardNum)));
|
|
39231
|
+
}
|
|
39232
|
+
}
|
|
39233
|
+
}
|
|
39234
|
+
if (lay.hash !== "none") {
|
|
39235
|
+
const hx = lay.hash === "college" ? K.hashCollege : K.hashNfl;
|
|
39236
|
+
const top = lay.toGoal !== void 0 ? Math.min(b.maxY, lay.toGoal) : b.maxY;
|
|
39237
|
+
for (let yd = Math.ceil(b.minY); yd <= top; yd += 1) {
|
|
39238
|
+
for (const sgn of [-1, 1]) {
|
|
39239
|
+
const xc = ctx.X(sgn * hx);
|
|
39240
|
+
parts.push(line({ class: "sx-pb-hash", x1: r25(xc - 2), y1: r25(ctx.Y(yd)), x2: r25(xc + 2), y2: r25(ctx.Y(yd)) }));
|
|
39241
|
+
}
|
|
39242
|
+
}
|
|
39243
|
+
}
|
|
39244
|
+
parts.push(line({ class: "sx-pb-los", x1: r25(x1), y1: r25(ctx.Y(0)), x2: r25(x2), y2: r25(ctx.Y(0)) }));
|
|
39245
|
+
return group({ class: "sx-pb-field-g" }, parts);
|
|
39246
|
+
},
|
|
39247
|
+
legend(lay) {
|
|
39248
|
+
const items = [
|
|
39249
|
+
{ kind: "offense", label: "Offense" },
|
|
39250
|
+
{ kind: "defense", label: "Defense" },
|
|
39251
|
+
{ kind: "run", label: "Route / run" },
|
|
39252
|
+
{ kind: "screen", label: "Block" },
|
|
39253
|
+
{ kind: "motion", label: "Motion" }
|
|
39254
|
+
];
|
|
39255
|
+
if (lay.zones.length) items.push({ kind: "zone", label: "Zone" });
|
|
39256
|
+
return items;
|
|
39257
|
+
}
|
|
39258
|
+
};
|
|
39259
|
+
|
|
39260
|
+
// src/diagrams/playbook/geometry/basketball.ts
|
|
39261
|
+
var K2 = {
|
|
39262
|
+
scale: 11,
|
|
39263
|
+
// x ∈ [-25, 25]
|
|
39264
|
+
half: 47,
|
|
39265
|
+
rimY: 5.25,
|
|
39266
|
+
laneHalf: 8,
|
|
39267
|
+
// NBA lane half-width
|
|
39268
|
+
ftY: 15,
|
|
39269
|
+
ftR: 6,
|
|
39270
|
+
threeR: 23.75,
|
|
39271
|
+
cornerX: 22,
|
|
39272
|
+
cornerMeetY: 14.2,
|
|
39273
|
+
restrictedR: 4,
|
|
39274
|
+
centerR: 6,
|
|
39275
|
+
margin: 2.5
|
|
39276
|
+
};
|
|
39277
|
+
var r26 = (n) => Math.round(n * 100) / 100;
|
|
39278
|
+
var LANDMARKS = {
|
|
39279
|
+
top: { x: 0, y: 28 },
|
|
39280
|
+
rslot: { x: 8, y: 26 },
|
|
39281
|
+
lslot: { x: -8, y: 26 },
|
|
39282
|
+
slot: { x: 8, y: 26 },
|
|
39283
|
+
rwing: { x: 19, y: 17 },
|
|
39284
|
+
lwing: { x: -19, y: 17 },
|
|
39285
|
+
wing: { x: 19, y: 17 },
|
|
39286
|
+
rcorner: { x: 22, y: 3 },
|
|
39287
|
+
lcorner: { x: -22, y: 3 },
|
|
39288
|
+
corner: { x: 22, y: 3 },
|
|
39289
|
+
"rshort-corner": { x: 12, y: 2 },
|
|
39290
|
+
"lshort-corner": { x: -12, y: 2 },
|
|
39291
|
+
relbow: { x: 8, y: 15 },
|
|
39292
|
+
lelbow: { x: -8, y: 15 },
|
|
39293
|
+
elbow: { x: 8, y: 15 },
|
|
39294
|
+
rblock: { x: 8, y: 7 },
|
|
39295
|
+
lblock: { x: -8, y: 7 },
|
|
39296
|
+
block: { x: 8, y: 7 },
|
|
39297
|
+
rdunker: { x: 9, y: 4 },
|
|
39298
|
+
ldunker: { x: -9, y: 4 },
|
|
39299
|
+
"high-post": { x: 0, y: 15 },
|
|
39300
|
+
ft: { x: 0, y: 15 },
|
|
39301
|
+
"free-throw": { x: 0, y: 15 },
|
|
39302
|
+
"rlow-post": { x: 9, y: 8 },
|
|
39303
|
+
"llow-post": { x: -9, y: 8 },
|
|
39304
|
+
rim: { x: 0, y: 5.25 },
|
|
39305
|
+
basket: { x: 0, y: 5.25 },
|
|
39306
|
+
hoop: { x: 0, y: 5.25 },
|
|
39307
|
+
paint: { x: 0, y: 10 }
|
|
39308
|
+
};
|
|
39309
|
+
function setRoster(formation) {
|
|
39310
|
+
const L = LANDMARKS;
|
|
39311
|
+
const at = (lm) => L[lm];
|
|
39312
|
+
const slots = (pts) => pts.map((p, i) => ({ id: String(i + 1), label: String(i + 1), x: p.x, y: p.y }));
|
|
39313
|
+
switch (formation) {
|
|
39314
|
+
case "horns":
|
|
39315
|
+
return slots([at("top"), at("lcorner"), at("rcorner"), at("lelbow"), at("relbow")]);
|
|
39316
|
+
case "1-4-high":
|
|
39317
|
+
return slots([at("top"), at("lwing"), at("rwing"), at("lelbow"), at("relbow")]);
|
|
39318
|
+
case "1-4-low":
|
|
39319
|
+
return slots([at("top"), at("lcorner"), at("rcorner"), at("lblock"), at("rblock")]);
|
|
39320
|
+
case "box":
|
|
39321
|
+
return slots([at("top"), at("lelbow"), at("relbow"), at("lblock"), at("rblock")]);
|
|
39322
|
+
case "spread-pnr":
|
|
39323
|
+
return slots([at("top"), at("lcorner"), at("rcorner"), at("lwing"), { x: 8, y: 24 }]);
|
|
39324
|
+
case "4-out":
|
|
39325
|
+
return slots([at("top"), at("lwing"), at("rwing"), at("lcorner"), at("rblock")]);
|
|
39326
|
+
case "5-out":
|
|
39327
|
+
default:
|
|
39328
|
+
return slots([at("top"), at("lwing"), at("rwing"), at("lcorner"), at("rcorner")]);
|
|
39329
|
+
}
|
|
39330
|
+
}
|
|
39331
|
+
function defenseRoster(scheme, offense) {
|
|
39332
|
+
const zones = [];
|
|
39333
|
+
if (scheme === "man") {
|
|
39334
|
+
const defenders2 = offense.filter((p) => p.side === "offense").slice(0, 5).map((o) => {
|
|
39335
|
+
const dx = -o.x * 0.12;
|
|
39336
|
+
const dy = (K2.rimY - o.y) * 0.18;
|
|
39337
|
+
return { id: "X" + o.id, label: "X" + o.label, x: o.x + dx, y: o.y + dy };
|
|
39338
|
+
});
|
|
39339
|
+
return { defenders: defenders2, zones };
|
|
39340
|
+
}
|
|
39341
|
+
let pts;
|
|
39342
|
+
if (scheme === "zone-3-2") pts = [{ x: 0, y: 20 }, { x: -13, y: 17 }, { x: 13, y: 17 }, { x: -8, y: 8 }, { x: 8, y: 8 }];
|
|
39343
|
+
else if (scheme === "zone-1-3-1") pts = [{ x: 0, y: 22 }, { x: -12, y: 14 }, { x: 0, y: 13 }, { x: 12, y: 14 }, { x: 0, y: 5.5 }];
|
|
39344
|
+
else pts = [{ x: -7, y: 18 }, { x: 7, y: 18 }, { x: 0, y: 9 }, { x: -13, y: 6 }, { x: 13, y: 6 }];
|
|
39345
|
+
const defenders = pts.map((p, i) => ({ id: "X" + (i + 1), label: "X", x: p.x, y: p.y }));
|
|
39346
|
+
return { defenders, zones };
|
|
39347
|
+
}
|
|
39348
|
+
var basketballModule = {
|
|
39349
|
+
scale: K2.scale,
|
|
39350
|
+
yUp: false,
|
|
39351
|
+
buildPlayers(ast) {
|
|
39352
|
+
const players = [];
|
|
39353
|
+
const byId = /* @__PURE__ */ new Map();
|
|
39354
|
+
const add = (p) => {
|
|
39355
|
+
if (byId.has(p.id)) players[byId.get(p.id)] = p;
|
|
39356
|
+
else {
|
|
39357
|
+
byId.set(p.id, players.length);
|
|
39358
|
+
players.push(p);
|
|
39359
|
+
}
|
|
39360
|
+
};
|
|
39361
|
+
for (const sl of setRoster(ast.formation)) add({ id: sl.id, side: "offense", pos: "o", label: sl.label, x: sl.x, y: sl.y });
|
|
39362
|
+
if (ast.defense) for (const d of defenseRoster(ast.defense, players).defenders) add({ id: d.id, side: "defense", pos: "x", label: d.label, x: d.x, y: d.y });
|
|
39363
|
+
for (const p of ast.players) {
|
|
39364
|
+
const at = p.at ?? (byId.has(p.id) ? { x: players[byId.get(p.id)].x, y: players[byId.get(p.id)].y } : { x: 0, y: 0 });
|
|
39365
|
+
add({ id: p.id, side: p.side, pos: p.pos === "c" || p.pos === "ol" ? "o" : p.pos, label: p.label, x: at.x, y: at.y });
|
|
39366
|
+
}
|
|
39367
|
+
return players;
|
|
39368
|
+
},
|
|
39369
|
+
buildZones(ast) {
|
|
39370
|
+
return ast.zones.map((z) => ({ x: z.x, y: z.y, rx: z.rx, ry: z.ry, label: z.label }));
|
|
39371
|
+
},
|
|
39372
|
+
resolveLandmark(name) {
|
|
39373
|
+
return LANDMARKS[name.toLowerCase()] ?? null;
|
|
39374
|
+
},
|
|
39375
|
+
bounds(_ast, players, moves, zones) {
|
|
39376
|
+
let minX = -25, maxX = 25, minY = 0, maxY = 30;
|
|
39377
|
+
const ext = (x, y) => {
|
|
39378
|
+
minX = Math.min(minX, x);
|
|
39379
|
+
maxX = Math.max(maxX, x);
|
|
39380
|
+
minY = Math.min(minY, y);
|
|
39381
|
+
maxY = Math.max(maxY, y);
|
|
39382
|
+
};
|
|
39383
|
+
for (const p of players) ext(p.x, p.y);
|
|
39384
|
+
for (const mv of moves) for (const pt of mv.points) ext(pt.x, pt.y);
|
|
39385
|
+
for (const z of zones) {
|
|
39386
|
+
ext(z.x - z.rx, z.y - z.ry);
|
|
39387
|
+
ext(z.x + z.rx, z.y + z.ry);
|
|
39388
|
+
}
|
|
39389
|
+
const m = K2.margin;
|
|
39390
|
+
return { minX: Math.min(-25, minX) - m * 0.4, maxX: Math.max(25, maxX) + m * 0.4, minY: Math.min(0, minY) - m * 0.4, maxY: Math.min(K2.half, maxY + m * 0.6) };
|
|
39391
|
+
},
|
|
39392
|
+
drawField(_lay, ctx, t) {
|
|
39393
|
+
const parts = [];
|
|
39394
|
+
const X = ctx.X, Y = ctx.Y, px = ctx.px;
|
|
39395
|
+
parts.push(rect({ class: "sx-pb-court-line", fill: "none", x: r26(X(-8)), y: r26(Y(0)), width: r26(px(2 * K2.laneHalf)), height: r26(px(K2.ftY)) }));
|
|
39396
|
+
parts.push(circle({ class: "sx-pb-court-line", fill: "none", cx: r26(X(0)), cy: r26(Y(K2.ftY)), r: r26(px(K2.ftR)) }));
|
|
39397
|
+
parts.push(line({ class: "sx-pb-court-line", x1: r26(X(-3)), y1: r26(Y(4)), x2: r26(X(3)), y2: r26(Y(4)) }));
|
|
39398
|
+
parts.push(circle({ class: "sx-pb-rim", fill: "none", cx: r26(X(0)), cy: r26(Y(K2.rimY)), r: r26(px(0.75)) }));
|
|
39399
|
+
parts.push(path({ class: "sx-pb-court-line", fill: "none", d: `M ${r26(X(-4))} ${r26(Y(K2.rimY))} A ${r26(px(K2.restrictedR))} ${r26(px(K2.restrictedR))} 0 0 0 ${r26(X(K2.restrictedR))} ${r26(Y(K2.rimY))}` }));
|
|
39400
|
+
parts.push(path({
|
|
39401
|
+
class: "sx-pb-court-line",
|
|
39402
|
+
fill: "none",
|
|
39403
|
+
d: `M ${r26(X(-22))} ${r26(Y(0))} L ${r26(X(-22))} ${r26(Y(K2.cornerMeetY))} A ${r26(px(K2.threeR))} ${r26(px(K2.threeR))} 0 0 0 ${r26(X(K2.cornerX))} ${r26(Y(K2.cornerMeetY))} L ${r26(X(K2.cornerX))} ${r26(Y(0))}`
|
|
39404
|
+
}));
|
|
39405
|
+
parts.push(path({ class: "sx-pb-court-line", fill: "none", d: `M ${r26(X(-6))} ${r26(Y(K2.half))} A ${r26(px(K2.centerR))} ${r26(px(K2.centerR))} 0 0 1 ${r26(X(K2.centerR))} ${r26(Y(K2.half))}` }));
|
|
39406
|
+
return group({ class: "sx-pb-field-g" }, parts);
|
|
39407
|
+
},
|
|
39408
|
+
legend() {
|
|
39409
|
+
return [
|
|
39410
|
+
{ kind: "offense", label: "Offense (1\u20135)" },
|
|
39411
|
+
{ kind: "defense", label: "Defense (X)" },
|
|
39412
|
+
{ kind: "run", label: "Cut" },
|
|
39413
|
+
{ kind: "pass", label: "Pass" },
|
|
39414
|
+
{ kind: "dribble", label: "Dribble" },
|
|
39415
|
+
{ kind: "screen", label: "Screen" }
|
|
39416
|
+
];
|
|
39417
|
+
}
|
|
39418
|
+
};
|
|
39419
|
+
|
|
39420
|
+
// src/diagrams/playbook/geometry/soccer.ts
|
|
39421
|
+
var K3 = {
|
|
39422
|
+
scale: 8,
|
|
39423
|
+
L: 105,
|
|
39424
|
+
W: 68,
|
|
39425
|
+
circleR: 9.15,
|
|
39426
|
+
paDepth: 16.5,
|
|
39427
|
+
paHalf: 20.16,
|
|
39428
|
+
// 40.32 / 2
|
|
39429
|
+
gaDepth: 5.5,
|
|
39430
|
+
gaHalf: 9.16,
|
|
39431
|
+
// 18.32 / 2
|
|
39432
|
+
penDist: 11,
|
|
39433
|
+
goalHalf: 3.66,
|
|
39434
|
+
// 7.32 / 2
|
|
39435
|
+
cornerR: 1,
|
|
39436
|
+
margin: 3
|
|
39437
|
+
};
|
|
39438
|
+
var r27 = (n) => Math.round(n * 100) / 100;
|
|
39439
|
+
function formationRoster2(formation) {
|
|
39440
|
+
const p = (id, x, y, gk2 = false) => ({ id, label: id, x, y, gk: gk2 });
|
|
39441
|
+
const back4 = [p("2", 20, 12), p("4", 20, 27), p("5", 20, 41), p("3", 20, 56)];
|
|
39442
|
+
const gk = p("1", 5, 34, true);
|
|
39443
|
+
switch (formation) {
|
|
39444
|
+
case "4-4-2":
|
|
39445
|
+
return [gk, ...back4, p("7", 50, 10), p("6", 50, 27), p("8", 50, 41), p("11", 50, 58), p("9", 80, 28), p("10", 80, 40)];
|
|
39446
|
+
case "4-2-3-1":
|
|
39447
|
+
return [gk, ...back4, p("6", 40, 28), p("8", 40, 40), p("7", 65, 12), p("10", 65, 34), p("11", 65, 56), p("9", 85, 34)];
|
|
39448
|
+
case "4-5-1":
|
|
39449
|
+
return [gk, ...back4, p("6", 48, 27), p("8", 48, 41), p("7", 52, 10), p("11", 52, 58), p("10", 62, 34), p("9", 85, 34)];
|
|
39450
|
+
case "4-4-1-1":
|
|
39451
|
+
return [gk, ...back4, p("7", 50, 10), p("6", 50, 27), p("8", 50, 41), p("11", 50, 58), p("10", 68, 34), p("9", 84, 34)];
|
|
39452
|
+
case "3-5-2":
|
|
39453
|
+
return [gk, p("4", 20, 20), p("5", 20, 34), p("6", 20, 48), p("2", 52, 6), p("3", 52, 62), p("8", 50, 25), p("10", 55, 43), p("7", 45, 34), p("9", 82, 28), p("11", 82, 40)];
|
|
39454
|
+
case "3-4-3":
|
|
39455
|
+
return [gk, p("4", 20, 20), p("5", 20, 34), p("6", 20, 48), p("2", 48, 8), p("8", 48, 28), p("10", 48, 40), p("3", 48, 60), p("7", 82, 14), p("9", 82, 34), p("11", 82, 54)];
|
|
39456
|
+
case "4-3-3":
|
|
39457
|
+
default:
|
|
39458
|
+
return [gk, ...back4, p("6", 42, 34), p("8", 52, 24), p("10", 52, 44), p("7", 82, 12), p("9", 82, 34), p("11", 82, 56)];
|
|
39459
|
+
}
|
|
39460
|
+
}
|
|
39461
|
+
function opponentBlock(scheme) {
|
|
39462
|
+
const cx = scheme === "high-press" ? 45 : scheme === "mid-block" ? 70 : 88;
|
|
39463
|
+
const p = (i, x, y) => ({ id: "X" + i, label: "X", x, y });
|
|
39464
|
+
return [
|
|
39465
|
+
p(1, cx + 12, 34),
|
|
39466
|
+
p(2, cx, 12),
|
|
39467
|
+
p(3, cx, 27),
|
|
39468
|
+
p(4, cx, 41),
|
|
39469
|
+
p(5, cx, 56),
|
|
39470
|
+
p(6, cx - 14, 14),
|
|
39471
|
+
p(7, cx - 14, 28),
|
|
39472
|
+
p(8, cx - 14, 40),
|
|
39473
|
+
p(9, cx - 14, 54),
|
|
39474
|
+
p(10, cx - 26, 28),
|
|
39475
|
+
p(11, cx - 26, 40)
|
|
39476
|
+
];
|
|
39477
|
+
}
|
|
39478
|
+
var LANDMARKS2 = {
|
|
39479
|
+
center: { x: 52.5, y: 34 },
|
|
39480
|
+
"centre-spot": { x: 52.5, y: 34 },
|
|
39481
|
+
box: { x: 96, y: 34 },
|
|
39482
|
+
"top-box": { x: 88.5, y: 34 },
|
|
39483
|
+
d: { x: 84.85, y: 34 },
|
|
39484
|
+
"penalty-arc": { x: 84.85, y: 34 },
|
|
39485
|
+
"penalty-spot": { x: 94, y: 34 },
|
|
39486
|
+
"near-post": { x: 105, y: 30.5 },
|
|
39487
|
+
"far-post": { x: 105, y: 37.7 },
|
|
39488
|
+
goal: { x: 105, y: 34 },
|
|
39489
|
+
"rcorner": { x: 105, y: 2 },
|
|
39490
|
+
"lcorner": { x: 105, y: 66 },
|
|
39491
|
+
"six-yard": { x: 101, y: 34 }
|
|
39492
|
+
};
|
|
39493
|
+
var soccerModule = {
|
|
39494
|
+
scale: K3.scale,
|
|
39495
|
+
yUp: false,
|
|
39496
|
+
buildPlayers(ast) {
|
|
39497
|
+
const players = [];
|
|
39498
|
+
const byId = /* @__PURE__ */ new Map();
|
|
39499
|
+
const add = (pl2) => {
|
|
39500
|
+
if (byId.has(pl2.id)) players[byId.get(pl2.id)] = pl2;
|
|
39501
|
+
else {
|
|
39502
|
+
byId.set(pl2.id, players.length);
|
|
39503
|
+
players.push(pl2);
|
|
39504
|
+
}
|
|
39505
|
+
};
|
|
39506
|
+
for (const sl of formationRoster2(ast.formation)) add({ id: sl.id, side: "offense", pos: sl.gk ? "gk" : "o", label: sl.label, x: sl.x, y: sl.y });
|
|
39507
|
+
if (ast.defense) for (const d of opponentBlock(ast.defense)) add({ id: d.id, side: "defense", pos: "x", label: d.label, x: d.x, y: d.y });
|
|
39508
|
+
for (const pl2 of ast.players) {
|
|
39509
|
+
const at = pl2.at ?? (byId.has(pl2.id) ? { x: players[byId.get(pl2.id)].x, y: players[byId.get(pl2.id)].y } : { x: 0, y: 0 });
|
|
39510
|
+
add({ id: pl2.id, side: pl2.side, pos: pl2.pos === "c" || pl2.pos === "ol" ? "o" : pl2.pos, label: pl2.label, x: at.x, y: at.y });
|
|
39511
|
+
}
|
|
39512
|
+
return players;
|
|
39513
|
+
},
|
|
39514
|
+
buildZones(ast) {
|
|
39515
|
+
return ast.zones.map((z) => ({ x: z.x, y: z.y, rx: z.rx, ry: z.ry, label: z.label }));
|
|
39516
|
+
},
|
|
39517
|
+
resolveLandmark(name) {
|
|
39518
|
+
return LANDMARKS2[name.toLowerCase()] ?? null;
|
|
39519
|
+
},
|
|
39520
|
+
bounds(ast) {
|
|
39521
|
+
const m = K3.margin;
|
|
39522
|
+
if (ast.view === "half") return { minX: 52.5 - m, maxX: K3.L + m, minY: -m, maxY: K3.W + m };
|
|
39523
|
+
return { minX: -m, maxX: K3.L + m, minY: -m, maxY: K3.W + m };
|
|
39524
|
+
},
|
|
39525
|
+
drawField(_lay, ctx, t) {
|
|
39526
|
+
const X = ctx.X, Y = ctx.Y, px = ctx.px;
|
|
39527
|
+
const parts = [];
|
|
39528
|
+
const ln = (x1, y1, x2, y2) => line({ class: "sx-pb-pitch-line", x1: r27(X(x1)), y1: r27(Y(y1)), x2: r27(X(x2)), y2: r27(Y(y2)) });
|
|
39529
|
+
const bands = 10;
|
|
39530
|
+
for (let i = 0; i < bands; i++) {
|
|
39531
|
+
if (i % 2 === 0) continue;
|
|
39532
|
+
parts.push(rect({ class: "sx-pb-stripe", x: r27(X(i * K3.L / bands)), y: r27(Y(0)), width: r27(px(K3.L / bands)), height: r27(px(K3.W)) }));
|
|
39533
|
+
}
|
|
39534
|
+
const cr = px(K3.cornerR);
|
|
39535
|
+
const corners = [[0, 0, 0], [K3.L, 0, 1], [0, K3.W, 2], [K3.L, K3.W, 3]];
|
|
39536
|
+
for (const [cxF, cyF, q] of corners) {
|
|
39537
|
+
const cx = X(cxF), cy = Y(cyF);
|
|
39538
|
+
const sx = cxF === 0 ? 1 : -1;
|
|
39539
|
+
const sy = cyF === 0 ? 1 : -1;
|
|
39540
|
+
parts.push(path({ class: "sx-pb-pitch-line", fill: "none", d: `M ${r27(cx + sx * cr)} ${r27(cy)} A ${r27(cr)} ${r27(cr)} 0 0 ${q === 1 || q === 2 ? 1 : 0} ${r27(cx)} ${r27(cy + sy * cr)}` }));
|
|
39541
|
+
}
|
|
39542
|
+
parts.push(ln(52.5, 0, 52.5, K3.W));
|
|
39543
|
+
parts.push(circle({ class: "sx-pb-pitch-line", fill: "none", cx: r27(X(52.5)), cy: r27(Y(34)), r: r27(px(K3.circleR)) }));
|
|
39544
|
+
parts.push(circle({ class: "sx-pb-pitch-dot", cx: r27(X(52.5)), cy: r27(Y(34)), r: 1.6 }));
|
|
39545
|
+
const topY = (half) => Math.min(Y(34 - half), Y(34 + half));
|
|
39546
|
+
for (const end of [0, 1]) {
|
|
39547
|
+
const gx = end === 0 ? 0 : K3.L;
|
|
39548
|
+
const sgn = end === 0 ? 1 : -1;
|
|
39549
|
+
parts.push(rect({ class: "sx-pb-pitch-line", fill: "none", x: r27(X(Math.min(gx, gx + sgn * K3.paDepth))), y: r27(topY(K3.paHalf)), width: r27(px(K3.paDepth)), height: r27(px(2 * K3.paHalf)) }));
|
|
39550
|
+
parts.push(rect({ class: "sx-pb-pitch-line", fill: "none", x: r27(X(Math.min(gx, gx + sgn * K3.gaDepth))), y: r27(topY(K3.gaHalf)), width: r27(px(K3.gaDepth)), height: r27(px(2 * K3.gaHalf)) }));
|
|
39551
|
+
const spotX = gx + sgn * K3.penDist;
|
|
39552
|
+
parts.push(circle({ class: "sx-pb-pitch-dot", cx: r27(X(spotX)), cy: r27(Y(34)), r: 1.4 }));
|
|
39553
|
+
const edgeX = gx + sgn * K3.paDepth;
|
|
39554
|
+
const dy = Math.sqrt(K3.circleR * K3.circleR - (K3.paDepth - K3.penDist) * (K3.paDepth - K3.penDist));
|
|
39555
|
+
const sweep = end === 0 ? 1 : 0;
|
|
39556
|
+
parts.push(path({ class: "sx-pb-pitch-line", fill: "none", d: `M ${r27(X(edgeX))} ${r27(Y(34 - dy))} A ${r27(px(K3.circleR))} ${r27(px(K3.circleR))} 0 0 ${sweep} ${r27(X(edgeX))} ${r27(Y(34 + dy))}` }));
|
|
39557
|
+
const goalX = gx - sgn * 2.2;
|
|
39558
|
+
parts.push(rect({ class: "sx-pb-goalbox", x: r27(X(Math.min(gx, goalX))), y: r27(topY(K3.goalHalf)), width: r27(px(2.2)), height: r27(px(2 * K3.goalHalf)) }));
|
|
39559
|
+
}
|
|
39560
|
+
return group({ class: "sx-pb-field-g" }, parts);
|
|
39561
|
+
},
|
|
39562
|
+
legend() {
|
|
39563
|
+
return [
|
|
39564
|
+
{ kind: "offense", label: "Team" },
|
|
39565
|
+
{ kind: "gk", label: "Keeper" },
|
|
39566
|
+
{ kind: "defense", label: "Opponent" },
|
|
39567
|
+
{ kind: "pass", label: "Pass" },
|
|
39568
|
+
{ kind: "run", label: "Run" },
|
|
39569
|
+
{ kind: "dribble", label: "Dribble" },
|
|
39570
|
+
{ kind: "shot", label: "Shot" }
|
|
39571
|
+
];
|
|
39572
|
+
}
|
|
39573
|
+
};
|
|
39574
|
+
|
|
39575
|
+
// src/diagrams/playbook/layout.ts
|
|
39576
|
+
var MODULES = {
|
|
39577
|
+
football: footballModule,
|
|
39578
|
+
basketball: basketballModule,
|
|
39579
|
+
soccer: soccerModule
|
|
39580
|
+
};
|
|
39581
|
+
function sportModule(sport) {
|
|
39582
|
+
return MODULES[sport];
|
|
39583
|
+
}
|
|
39584
|
+
var round23 = (p) => ({ x: Math.round(p.x * 100) / 100, y: Math.round(p.y * 100) / 100 });
|
|
39585
|
+
function styleFor(sport, kind) {
|
|
39586
|
+
if (kind === "dribble") return "wavy";
|
|
39587
|
+
if (kind === "shot") return sport === "soccer" ? "double" : "solid";
|
|
39588
|
+
if (kind === "pass") return sport === "soccer" ? "solid" : "dashed";
|
|
39589
|
+
if (kind === "motion") return "dashed";
|
|
39590
|
+
if (sport === "soccer" && (kind === "run" || kind === "cut" || kind === "move")) return "dashed";
|
|
39591
|
+
return "solid";
|
|
39592
|
+
}
|
|
39593
|
+
function endFor(kind, override) {
|
|
39594
|
+
if (override) return override;
|
|
39595
|
+
if (kind === "screen" || kind === "block") return "tee";
|
|
39596
|
+
if (kind === "handoff") return "none";
|
|
39597
|
+
return "arrow";
|
|
39598
|
+
}
|
|
39599
|
+
function findPlayer(ref, byId) {
|
|
39600
|
+
if (byId.has(ref)) return byId.get(ref);
|
|
39601
|
+
const lower = ref.toLowerCase();
|
|
39602
|
+
for (const [k, v] of byId) if (k.toLowerCase() === lower) return v;
|
|
39603
|
+
return void 0;
|
|
39604
|
+
}
|
|
39605
|
+
function resolveRef(token, byId, players, mod) {
|
|
39606
|
+
const coord = /^(-?\d*\.?\d+),(-?\d*\.?\d+)$/.exec(token);
|
|
39607
|
+
if (coord) return { x: Number(coord[1]), y: Number(coord[2]) };
|
|
39608
|
+
const pi = findPlayer(token, byId);
|
|
39609
|
+
if (pi !== void 0) return { x: players[pi].x, y: players[pi].y };
|
|
39610
|
+
const lm = mod.resolveLandmark?.(token);
|
|
39611
|
+
if (lm) return { x: lm.x, y: lm.y };
|
|
39612
|
+
return null;
|
|
39613
|
+
}
|
|
39614
|
+
function layoutPlaybook(ast) {
|
|
39615
|
+
const mod = MODULES[ast.sport];
|
|
39616
|
+
const errors = [];
|
|
39617
|
+
const warnings = [];
|
|
39618
|
+
const players = mod.buildPlayers(ast);
|
|
39619
|
+
const byId = /* @__PURE__ */ new Map();
|
|
39620
|
+
players.forEach((p, i) => byId.set(p.id, i));
|
|
39621
|
+
if (players.length === 0) {
|
|
39622
|
+
errors.push("no players \u2014 add a `formation`/set or explicit `player` statements");
|
|
39623
|
+
}
|
|
39624
|
+
const zones = mod.buildZones(ast, players);
|
|
39625
|
+
const moves = [];
|
|
39626
|
+
for (const m of ast.moves) {
|
|
39627
|
+
const srcIdx = findPlayer(m.player, byId);
|
|
39628
|
+
if (srcIdx === void 0) {
|
|
39629
|
+
warnings.push(`move on unknown player "${m.player}" \u2014 skipped`);
|
|
39630
|
+
continue;
|
|
39631
|
+
}
|
|
39632
|
+
const src = { x: players[srcIdx].x, y: players[srcIdx].y };
|
|
39633
|
+
const named = mod.resolveNamed?.(m, src, players, byId, warnings);
|
|
39634
|
+
if (named) {
|
|
39635
|
+
moves.push(named);
|
|
39636
|
+
continue;
|
|
39637
|
+
}
|
|
39638
|
+
const geom = resolveGeneric(m, src, byId, players, mod, ast.sport, warnings);
|
|
39639
|
+
if (geom) moves.push(geom);
|
|
39640
|
+
}
|
|
39641
|
+
const bounds = mod.bounds(ast, players, moves, zones);
|
|
39642
|
+
return {
|
|
39643
|
+
title: ast.title,
|
|
39644
|
+
sport: ast.sport,
|
|
39645
|
+
down: ast.down,
|
|
39646
|
+
distance: ast.distance,
|
|
39647
|
+
losYard: ast.losYard,
|
|
39648
|
+
toGoal: ast.toGoal,
|
|
39649
|
+
hash: ast.hash,
|
|
39650
|
+
view: ast.view === "half" ? "half" : "full",
|
|
39651
|
+
players,
|
|
39652
|
+
moves,
|
|
39653
|
+
zones,
|
|
39654
|
+
bounds,
|
|
39655
|
+
errors,
|
|
39656
|
+
warnings
|
|
39657
|
+
};
|
|
39658
|
+
}
|
|
39659
|
+
function resolveGeneric(m, src, byId, players, mod, sport, warnings) {
|
|
39660
|
+
const style = styleFor(sport, m.kind);
|
|
39661
|
+
if (m.points && m.points.length) {
|
|
39662
|
+
const pts = [{ x: src.x, y: src.y }];
|
|
39663
|
+
let cur = { x: src.x, y: src.y };
|
|
39664
|
+
for (const p of m.points) {
|
|
39665
|
+
if (p.ref) {
|
|
39666
|
+
const r6 = resolveRef(p.ref, byId, players, mod);
|
|
39667
|
+
if (!r6) {
|
|
39668
|
+
warnings.push(`${m.kind} ${m.player}: unknown destination "${p.ref}" \u2014 skipped`);
|
|
39669
|
+
return null;
|
|
39670
|
+
}
|
|
39671
|
+
cur = r6;
|
|
39672
|
+
} else if (p.rel) {
|
|
39673
|
+
cur = { x: cur.x + (p.x ?? 0), y: cur.y + (p.y ?? 0) };
|
|
39674
|
+
} else {
|
|
39675
|
+
cur = { x: p.x ?? cur.x, y: p.y ?? cur.y };
|
|
39676
|
+
}
|
|
39677
|
+
pts.push(cur);
|
|
39678
|
+
}
|
|
39679
|
+
return { player: m.player, kind: m.kind, style, points: pts.map(round23), end: endFor(m.kind, m.end) };
|
|
39680
|
+
}
|
|
39681
|
+
let dest = null;
|
|
39682
|
+
if (m.target) dest = resolveRef(m.target, byId, players, mod);
|
|
39683
|
+
else if (m.kind === "shot") dest = mod.resolveLandmark?.("rim") ?? mod.resolveLandmark?.("goal") ?? null;
|
|
39684
|
+
if (!dest) {
|
|
39685
|
+
warnings.push(`${m.kind} ${m.player}: needs a target/destination \u2014 skipped`);
|
|
39686
|
+
return null;
|
|
39687
|
+
}
|
|
39688
|
+
if (m.kind === "screen" || m.kind === "block") {
|
|
39689
|
+
const dx = dest.x - src.x, dy = dest.y - src.y;
|
|
39690
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
39691
|
+
const gap = sport === "football" ? 0.6 : sport === "basketball" ? 1.2 : 1.6;
|
|
39692
|
+
const stop = Math.max(0, len - gap);
|
|
39693
|
+
const end = { x: src.x + dx / len * stop, y: src.y + dy / len * stop };
|
|
39694
|
+
return { player: m.player, kind: m.kind, style, points: [round23(src), round23(end)], end: "tee" };
|
|
39695
|
+
}
|
|
39696
|
+
return { player: m.player, kind: m.kind, style, points: [round23(src), round23(dest)], end: endFor(m.kind, m.end) };
|
|
39697
|
+
}
|
|
39698
|
+
|
|
39699
|
+
// src/diagrams/playbook/renderer.ts
|
|
39700
|
+
var r28 = (n) => Math.round(n * 100) / 100;
|
|
39701
|
+
function buildCss14(t) {
|
|
39702
|
+
return `
|
|
39703
|
+
.sx-pb { font-family: ${DEFAULT_FONT_FAMILY}; }
|
|
39704
|
+
.sx-pb-title { font: ${TITLE.weight} ${TITLE.size}px sans-serif; fill: ${t.text}; }
|
|
39705
|
+
.sx-pb-field, .sx-pb-turf { fill: ${t.surface}; }
|
|
39706
|
+
.sx-pb-court { fill: ${t.courtSurface}; }
|
|
39707
|
+
.sx-pb-surround { fill: ${t.surround}; }
|
|
39708
|
+
.sx-pb-surround-court { fill: ${t.courtSurround}; }
|
|
39709
|
+
.sx-pb-boundary { fill: none; stroke: ${t.lineBold}; stroke-width: 2.6; }
|
|
39710
|
+
.sx-pb-boundary-court { fill: none; stroke: ${t.courtLine}; stroke-width: 2.6; }
|
|
39711
|
+
.sx-pb-stripe { fill: ${t.surfaceAlt}; }
|
|
39712
|
+
.sx-pb-yard, .sx-pb-pitch-line, .sx-pb-goalbox { fill: none; stroke: ${t.lineSoft}; stroke-width: 1.4; }
|
|
39713
|
+
.sx-pb-court-line { fill: none; stroke: ${t.courtLine}; stroke-width: 1.5; }
|
|
39714
|
+
.sx-pb-hash { fill: none; stroke: ${t.lineSoft}; stroke-width: 1.2; }
|
|
39715
|
+
.sx-pb-los, .sx-pb-goalline { fill: none; stroke: ${t.lineBold}; stroke-width: 2.4; }
|
|
39716
|
+
.sx-pb-goalline { stroke: ${t.goalAccent}; }
|
|
39717
|
+
.sx-pb-goalpost { fill: none; stroke: ${t.goalAccent}; stroke-width: 2.4; stroke-linecap: round; }
|
|
39718
|
+
.sx-pb-endzone { fill: ${t.endzoneFill}; }
|
|
39719
|
+
.sx-pb-yardnum { font: 600 11px sans-serif; fill: ${t.surfaceText}; }
|
|
39720
|
+
.sx-pb-rim { stroke: ${t.rim}; stroke-width: 2.4; }
|
|
39721
|
+
.sx-pb-pitch-dot { fill: ${t.lineBold}; }
|
|
39722
|
+
.sx-pb-zone { fill: ${t.zoneFill}; stroke: ${t.zoneStroke}; stroke-width: 1.3; stroke-dasharray: 5 4; }
|
|
39723
|
+
.sx-pb-zone-text { font: 9px sans-serif; fill: ${t.zoneStroke}; }
|
|
39724
|
+
.sx-pb-move { fill: none; stroke: ${t.moveStroke}; stroke-width: 2.4; stroke-linejoin: round; stroke-linecap: round; }
|
|
39725
|
+
.sx-pb-move-dash { fill: none; stroke: ${t.moveStroke}; stroke-width: 2.2; stroke-dasharray: 6 4; stroke-linejoin: round; stroke-linecap: round; }
|
|
39726
|
+
.sx-pb-shot { fill: none; stroke: ${t.shotStroke}; stroke-width: 3.4; stroke-linecap: round; }
|
|
39727
|
+
.sx-pb-motion { fill: none; stroke: ${t.motionStroke}; stroke-width: 1.9; stroke-dasharray: 4 3; }
|
|
39728
|
+
.sx-pb-move-fill { fill: ${t.moveStroke}; stroke: none; }
|
|
39729
|
+
.sx-pb-shot-fill { fill: ${t.shotStroke}; stroke: none; }
|
|
39730
|
+
.sx-pb-motion-fill { fill: ${t.motionStroke}; stroke: none; }
|
|
39731
|
+
.sx-pb-o { fill: ${t.offenseFill}; stroke: ${t.offenseStroke}; stroke-width: 2; }
|
|
39732
|
+
.sx-pb-gk { fill: ${t.gkFill}; stroke: ${t.offenseStroke}; stroke-width: 2; }
|
|
39733
|
+
.sx-pb-o-text { font: 700 10.5px sans-serif; fill: ${t.offenseLabel}; }
|
|
39734
|
+
.sx-pb-x { fill: none; stroke: ${t.defenseStroke}; stroke-width: 2.6; stroke-linecap: round; }
|
|
39735
|
+
.sx-pb-x-text { font: 700 9px sans-serif; fill: ${t.defenseStroke}; }
|
|
39736
|
+
.sx-pb-ball { fill: ${t.ballFill}; stroke: ${t.lineBold}; stroke-width: 0.8; }
|
|
39737
|
+
.sx-pb-anno { font: 600 12px sans-serif; fill: ${t.annotation}; }
|
|
39738
|
+
.sx-pb-legend { font: 11px sans-serif; fill: ${t.annotation}; }
|
|
39739
|
+
.sx-pb-error-box { fill: ${t.bg}; stroke: ${t.negative}; stroke-width: 1.5; }
|
|
39740
|
+
.sx-pb-error-title { font: 700 13px ui-monospace, Menlo, monospace; fill: ${t.negative}; }
|
|
39741
|
+
.sx-pb-error-line { font: 12px ui-monospace, Menlo, monospace; fill: ${t.negative}; }
|
|
39742
|
+
`.trim();
|
|
39743
|
+
}
|
|
39744
|
+
function renderErrorPanel2(lay, t) {
|
|
39745
|
+
const lines = lay.errors;
|
|
39746
|
+
const w = Math.max(520, ...lines.map((l) => l.length * 6.6 + 48));
|
|
39747
|
+
const h = 56 + lines.length * 19;
|
|
39748
|
+
return svgRoot(
|
|
39749
|
+
{ viewBox: `0 0 ${r28(w)} ${h}`, width: r28(w), height: h, class: "sx-pb", role: "img" },
|
|
39750
|
+
[
|
|
39751
|
+
title(lay.title),
|
|
39752
|
+
desc(`Playbook validation failed with ${lines.length} error${lines.length === 1 ? "" : "s"}.`),
|
|
39753
|
+
el("style", {}, buildCss14(t)),
|
|
39754
|
+
rect({ class: "sx-pb-error-box", x: 1, y: 1, width: r28(w - 2), height: h - 2, rx: 6 }),
|
|
39755
|
+
text({ class: "sx-pb-error-title", x: 16, y: 26 }, `playbook: ${lines.length} validation error${lines.length === 1 ? "" : "s"}`),
|
|
39756
|
+
...lines.map((e, i) => text({ class: "sx-pb-error-line", x: 16, y: 50 + i * 19 }, `\u26A0 ${e}`))
|
|
39757
|
+
]
|
|
39758
|
+
);
|
|
39759
|
+
}
|
|
39760
|
+
function wavyPath(pts, amp = 3.4, wl = 11) {
|
|
39761
|
+
const flat = [];
|
|
39762
|
+
let acc = 0;
|
|
39763
|
+
for (let i = 1; i < pts.length; i++) {
|
|
39764
|
+
const a = pts[i - 1], b = pts[i];
|
|
39765
|
+
const dx = b.x - a.x, dy = b.y - a.y;
|
|
39766
|
+
const segLen = Math.hypot(dx, dy) || 1;
|
|
39767
|
+
const ux = dx / segLen, uy = dy / segLen;
|
|
39768
|
+
const nx = -uy, ny = ux;
|
|
39769
|
+
const lastSeg = i === pts.length - 1;
|
|
39770
|
+
const straightTail = lastSeg ? 9 : 0;
|
|
39771
|
+
const usable = Math.max(0, segLen - straightTail);
|
|
39772
|
+
const steps = Math.max(2, Math.round(usable / 2));
|
|
39773
|
+
for (let s = 0; s <= steps; s++) {
|
|
39774
|
+
const d = usable * s / steps;
|
|
39775
|
+
const off = amp * Math.sin(2 * Math.PI * (acc + d) / wl);
|
|
39776
|
+
flat.push({ x: a.x + ux * d + nx * off, y: a.y + uy * d + ny * off });
|
|
39777
|
+
}
|
|
39778
|
+
acc += usable;
|
|
39779
|
+
if (lastSeg) flat.push({ x: b.x, y: b.y });
|
|
39780
|
+
}
|
|
39781
|
+
return flat.map((p, i) => `${i === 0 ? "M" : "L"} ${r28(p.x)} ${r28(p.y)}`).join(" ");
|
|
39782
|
+
}
|
|
39783
|
+
function arrowHead4(pts, cls) {
|
|
39784
|
+
if (pts.length < 2) return "";
|
|
39785
|
+
const a = pts[pts.length - 2], b = pts[pts.length - 1];
|
|
39786
|
+
const ang = Math.atan2(b.y - a.y, b.x - a.x);
|
|
39787
|
+
const size = 8, wing = 0.42;
|
|
39788
|
+
const p2 = `${r28(b.x - size * Math.cos(ang - wing))},${r28(b.y - size * Math.sin(ang - wing))}`;
|
|
39789
|
+
const p3 = `${r28(b.x - size * Math.cos(ang + wing))},${r28(b.y - size * Math.sin(ang + wing))}`;
|
|
39790
|
+
return polygon({ class: cls, points: `${r28(b.x)},${r28(b.y)} ${p2} ${p3}` });
|
|
39791
|
+
}
|
|
39792
|
+
function teeBar(pts, cls) {
|
|
39793
|
+
if (pts.length < 2) return "";
|
|
39794
|
+
const a = pts[pts.length - 2], b = pts[pts.length - 1];
|
|
39795
|
+
const ang = Math.atan2(b.y - a.y, b.x - a.x) + Math.PI / 2;
|
|
39796
|
+
const half = 7;
|
|
39797
|
+
return line({ class: cls, x1: r28(b.x - half * Math.cos(ang)), y1: r28(b.y - half * Math.sin(ang)), x2: r28(b.x + half * Math.cos(ang)), y2: r28(b.y + half * Math.sin(ang)) });
|
|
39798
|
+
}
|
|
39799
|
+
function renderMove(mv, ctx) {
|
|
39800
|
+
const pts = mv.points.map((p) => ({ x: ctx.X(p.x), y: ctx.Y(p.y) }));
|
|
39801
|
+
const strokeCls = mv.kind === "motion" ? "sx-pb-motion" : mv.kind === "shot" ? "sx-pb-shot" : mv.style === "dashed" ? "sx-pb-move-dash" : "sx-pb-move";
|
|
39802
|
+
const headCls = mv.kind === "motion" ? "sx-pb-motion-fill" : mv.kind === "shot" ? "sx-pb-shot-fill" : "sx-pb-move-fill";
|
|
39803
|
+
const parts = [];
|
|
39804
|
+
if (mv.style === "wavy") {
|
|
39805
|
+
parts.push(path({ class: strokeCls, d: wavyPath(pts) }));
|
|
39806
|
+
} else {
|
|
39807
|
+
parts.push(path({ class: strokeCls, d: pts.map((p, i) => `${i === 0 ? "M" : "L"} ${r28(p.x)} ${r28(p.y)}`).join(" ") }));
|
|
39808
|
+
}
|
|
39809
|
+
if (mv.end === "arrow") parts.push(arrowHead4(pts, headCls));
|
|
39810
|
+
else if (mv.end === "tee") parts.push(teeBar(pts, strokeCls));
|
|
39811
|
+
return group({ class: "sx-pb-move-g", "data-kind": mv.kind, "data-player": mv.player }, parts);
|
|
39812
|
+
}
|
|
39813
|
+
function playerSymbol(p, ctx) {
|
|
39814
|
+
const cx = ctx.X(p.x), cy = ctx.Y(p.y), r6 = 10;
|
|
39815
|
+
const parts = [];
|
|
39816
|
+
if (p.side === "defense" || p.pos === "x") {
|
|
39817
|
+
const k = r6 * 0.78;
|
|
39818
|
+
parts.push(line({ class: "sx-pb-x", x1: r28(cx - k), y1: r28(cy - k), x2: r28(cx + k), y2: r28(cy + k) }));
|
|
39819
|
+
parts.push(line({ class: "sx-pb-x", x1: r28(cx - k), y1: r28(cy + k), x2: r28(cx + k), y2: r28(cy - k) }));
|
|
39820
|
+
if (p.label) parts.push(text({ class: "sx-pb-x-text", x: r28(cx + k + 5), y: r28(cy - k + 2), "text-anchor": "middle" }, p.label));
|
|
39821
|
+
} else if (p.pos === "gk") {
|
|
39822
|
+
const h = r6 * 1.15;
|
|
39823
|
+
parts.push(polygon({ class: "sx-pb-gk", points: `${r28(cx)},${r28(cy - h)} ${r28(cx + h)},${r28(cy + h * 0.8)} ${r28(cx - h)},${r28(cy + h * 0.8)}` }));
|
|
39824
|
+
parts.push(text({ class: "sx-pb-o-text", x: r28(cx), y: r28(cy + 6), "text-anchor": "middle" }, p.label));
|
|
39825
|
+
} else if (p.pos === "c") {
|
|
39826
|
+
parts.push(rect({ class: "sx-pb-o", x: r28(cx - r6 * 0.82), y: r28(cy - r6 * 0.82), width: r28(r6 * 1.64), height: r28(r6 * 1.64) }));
|
|
39827
|
+
parts.push(text({ class: "sx-pb-o-text", x: r28(cx), y: r28(cy + 3.6), "text-anchor": "middle" }, p.label));
|
|
39828
|
+
} else {
|
|
39829
|
+
parts.push(circle({ class: "sx-pb-o", cx: r28(cx), cy: r28(cy), r: r6 }));
|
|
39830
|
+
parts.push(text({ class: "sx-pb-o-text", x: r28(cx), y: r28(cy + 3.6), "text-anchor": "middle" }, p.label));
|
|
39831
|
+
}
|
|
39832
|
+
return group({ class: "sx-pb-player", "data-side": p.side, "data-id": p.id }, parts);
|
|
39833
|
+
}
|
|
39834
|
+
function legendSwatch(kind, sport) {
|
|
39835
|
+
const moveCls = (dashed) => dashed ? "sx-pb-move-dash" : "sx-pb-move";
|
|
39836
|
+
const arrow2 = polygon({ class: "sx-pb-move-fill", points: "20,0 13,-3.5 13,3.5" });
|
|
39837
|
+
switch (kind) {
|
|
39838
|
+
case "offense":
|
|
39839
|
+
return circle({ class: "sx-pb-o", cx: 8, cy: 0, r: 6 });
|
|
39840
|
+
case "gk":
|
|
39841
|
+
return polygon({ class: "sx-pb-gk", points: "8,-7 15,5 1,5" });
|
|
39842
|
+
case "defense":
|
|
39843
|
+
return `${line({ class: "sx-pb-x", x1: 3, y1: -5, x2: 13, y2: 5 })}${line({ class: "sx-pb-x", x1: 3, y1: 5, x2: 13, y2: -5 })}`;
|
|
39844
|
+
case "run":
|
|
39845
|
+
return `${line({ class: moveCls(sport === "soccer"), x1: 0, y1: 0, x2: 14, y2: 0 })}${arrow2}`;
|
|
39846
|
+
case "pass":
|
|
39847
|
+
return `${line({ class: moveCls(sport !== "soccer"), x1: 0, y1: 0, x2: 14, y2: 0 })}${arrow2}`;
|
|
39848
|
+
case "dribble":
|
|
39849
|
+
return `${path({ class: "sx-pb-move", d: "M0,0 Q3,-4 6,0 T12,0" })}${polygon({ class: "sx-pb-move-fill", points: "20,0 13,-3.5 13,3.5" })}`;
|
|
39850
|
+
case "screen":
|
|
39851
|
+
return `${line({ class: "sx-pb-move", x1: 0, y1: 0, x2: 16, y2: 0 })}${line({ class: "sx-pb-move", x1: 16, y1: -5, x2: 16, y2: 5 })}`;
|
|
39852
|
+
case "shot":
|
|
39853
|
+
return `${line({ class: "sx-pb-shot", x1: 0, y1: 0, x2: 14, y2: 0 })}${polygon({ class: "sx-pb-shot-fill", points: "20,0 13,-3.5 13,3.5" })}`;
|
|
39854
|
+
case "motion":
|
|
39855
|
+
return `${line({ class: "sx-pb-motion", x1: 0, y1: 0, x2: 14, y2: 0 })}${polygon({ class: "sx-pb-motion-fill", points: "20,0 13,-3.5 13,3.5" })}`;
|
|
39856
|
+
case "zone":
|
|
39857
|
+
return rect({ class: "sx-pb-zone", x: 0, y: -6, width: 18, height: 12, rx: 6 });
|
|
39858
|
+
default:
|
|
39859
|
+
return "";
|
|
39860
|
+
}
|
|
39861
|
+
}
|
|
39862
|
+
function renderLegend4(items, y, sport) {
|
|
39863
|
+
const parts = [];
|
|
39864
|
+
let cx = 12;
|
|
39865
|
+
for (const it of items) {
|
|
39866
|
+
parts.push(group({ transform: `translate(${r28(cx)},${r28(y)})` }, [legendSwatch(it.kind, sport)]));
|
|
39867
|
+
parts.push(text({ class: "sx-pb-legend", x: r28(cx + 26), y: r28(y + 4) }, it.label));
|
|
39868
|
+
cx += 26 + it.label.length * 6.5 + 20;
|
|
39869
|
+
}
|
|
39870
|
+
return group({ class: "sx-pb-legend-g" }, parts);
|
|
39871
|
+
}
|
|
39872
|
+
function ordinal(n) {
|
|
39873
|
+
return n === 1 ? "1st" : n === 2 ? "2nd" : n === 3 ? "3rd" : n === 4 ? "4th" : `${n}th`;
|
|
39874
|
+
}
|
|
39875
|
+
function renderPlaybookLayout(lay, config) {
|
|
39876
|
+
let themeName = config?.theme ?? "default";
|
|
39877
|
+
if (lay.sport === "soccer" && themeName === "dark") themeName = "default";
|
|
39878
|
+
const t = resolvePlaybookTheme(themeName);
|
|
39879
|
+
if (lay.errors.length > 0) return renderErrorPanel2(lay, t);
|
|
39880
|
+
const mod = sportModule(lay.sport);
|
|
39881
|
+
const scale = mod.scale;
|
|
39882
|
+
const b = lay.bounds;
|
|
39883
|
+
const titleH = TITLE.bandH;
|
|
39884
|
+
const annoH = lay.sport === "football" && (lay.down || lay.distance || lay.losYard !== void 0) ? 20 : 0;
|
|
39885
|
+
const topH = titleH + annoH;
|
|
39886
|
+
const legendH = 30;
|
|
39887
|
+
const EDGE = 16;
|
|
39888
|
+
const fieldW = (b.maxX - b.minX) * scale;
|
|
39889
|
+
const fieldH = (b.maxY - b.minY) * scale;
|
|
39890
|
+
const W2 = r28(fieldW + EDGE * 2);
|
|
39891
|
+
const fieldTop = topH + EDGE;
|
|
39892
|
+
const H2 = r28(fieldH + topH + EDGE * 2 + legendH);
|
|
39893
|
+
const X = (u) => r28((u - b.minX) * scale + EDGE);
|
|
39894
|
+
const Y = (v) => r28(mod.yUp ? (b.maxY - v) * scale + fieldTop : (v - b.minY) * scale + fieldTop);
|
|
39895
|
+
const px = (u) => r28(u * scale);
|
|
39896
|
+
const ctx = { X, Y, px };
|
|
39897
|
+
const isCourt = lay.sport === "basketball";
|
|
39898
|
+
const surfaceCls = isCourt ? "sx-pb-court" : lay.sport === "soccer" ? "sx-pb-turf" : "sx-pb-field";
|
|
39899
|
+
const surroundCls = isCourt ? "sx-pb-surround-court" : "sx-pb-surround";
|
|
39900
|
+
const boundaryCls = isCourt ? "sx-pb-boundary-court" : "sx-pb-boundary";
|
|
39901
|
+
const fieldRx = 7;
|
|
39902
|
+
const surround = rect({ class: surroundCls, x: 2, y: r28(topH), width: r28(W2 - 4), height: r28(fieldH + EDGE * 2), rx: 12 });
|
|
39903
|
+
const surfaceBase = rect({ class: surfaceCls, x: EDGE, y: r28(fieldTop), width: r28(fieldW), height: r28(fieldH), rx: fieldRx });
|
|
39904
|
+
const boundary = rect({ class: boundaryCls, x: EDGE, y: r28(fieldTop), width: r28(fieldW), height: r28(fieldH), rx: fieldRx });
|
|
39905
|
+
const clipId = "sx-pb-clip";
|
|
39906
|
+
const clip6 = el("clipPath", { id: clipId }, [rect({ x: EDGE, y: r28(fieldTop), width: r28(fieldW), height: r28(fieldH), rx: fieldRx })]);
|
|
39907
|
+
const field = group({ "clip-path": `url(#${clipId})` }, [mod.drawField(lay, ctx, t)]);
|
|
39908
|
+
const zones = [];
|
|
39909
|
+
for (const z of lay.zones) {
|
|
39910
|
+
zones.push(el("ellipse", { class: "sx-pb-zone", cx: X(z.x), cy: Y(z.y), rx: px(z.rx), ry: px(z.ry) }));
|
|
39911
|
+
if (z.label) zones.push(text({ class: "sx-pb-zone-text", x: X(z.x), y: r28(Y(z.y) - px(z.ry) + (mod.yUp ? 11 : -4)), "text-anchor": "middle" }, z.label));
|
|
39912
|
+
}
|
|
39913
|
+
const moves = lay.moves.map((m) => renderMove(m, ctx));
|
|
39914
|
+
const players = lay.players.map((p) => playerSymbol(p, ctx));
|
|
39915
|
+
const ball = lay.sport === "football" ? el("ellipse", { class: "sx-pb-ball", cx: X(0), cy: Y(0), rx: 4.5, ry: 2.7 }) : "";
|
|
39916
|
+
const annoParts = [];
|
|
39917
|
+
if (annoH) {
|
|
39918
|
+
const bits = [];
|
|
39919
|
+
if (lay.down) bits.push(ordinal(lay.down) + (lay.distance ? ` & ${lay.distance}` : ""));
|
|
39920
|
+
else if (lay.distance) bits.push(`${lay.distance} to go`);
|
|
39921
|
+
if (lay.losYard !== void 0) bits.push(`ball on ${lay.losYard}`);
|
|
39922
|
+
annoParts.push(text({ class: "sx-pb-anno", x: 8, y: titleH + 14 }, bits.join(" \xB7 ")));
|
|
39923
|
+
}
|
|
39924
|
+
const nOff = lay.players.filter((p) => p.side === "offense").length;
|
|
39925
|
+
const nDef = lay.players.filter((p) => p.side === "defense").length;
|
|
39926
|
+
const descText = `${lay.sport} play. ${nOff} ${lay.sport === "football" ? "offensive players" : "players"}, ${nDef} ${lay.sport === "football" ? "defenders" : "opponents"}, ${lay.moves.length} assignment${lay.moves.length === 1 ? "" : "s"}.` + (lay.warnings.length ? ` Warnings: ${lay.warnings.join("; ")}.` : "");
|
|
39927
|
+
return svgRoot(
|
|
39928
|
+
{ viewBox: `0 0 ${W2} ${H2}`, width: W2, height: H2, class: "sx-pb", role: "img" },
|
|
39929
|
+
[
|
|
39930
|
+
title(lay.title),
|
|
39931
|
+
desc(descText),
|
|
39932
|
+
el("style", {}, buildCss14(t)),
|
|
39933
|
+
rect({ fill: t.bg, x: 0, y: 0, width: W2, height: H2 }),
|
|
39934
|
+
el("defs", {}, [clip6]),
|
|
39935
|
+
text({ class: "sx-pb-title", x: r28(W2 / 2), y: TITLE.y, "text-anchor": "middle" }, lay.title),
|
|
39936
|
+
...annoParts,
|
|
39937
|
+
surround,
|
|
39938
|
+
surfaceBase,
|
|
39939
|
+
field,
|
|
39940
|
+
group({ class: "sx-pb-zones", "clip-path": `url(#${clipId})` }, zones),
|
|
39941
|
+
group({ class: "sx-pb-moves" }, moves),
|
|
39942
|
+
boundary,
|
|
39943
|
+
group({ class: "sx-pb-players" }, players),
|
|
39944
|
+
ball,
|
|
39945
|
+
renderLegend4(mod.legend(lay), fieldH + topH + EDGE * 2 + 18, lay.sport)
|
|
39946
|
+
]
|
|
39947
|
+
);
|
|
39948
|
+
}
|
|
39949
|
+
function renderPlaybook(text2, config) {
|
|
39950
|
+
return renderPlaybookLayout(layoutPlaybook(parsePlaybook(text2)), config);
|
|
39951
|
+
}
|
|
39952
|
+
|
|
39953
|
+
// src/diagrams/playbook/index.ts
|
|
39954
|
+
var playbook = {
|
|
39955
|
+
type: "playbook",
|
|
39956
|
+
detect(text2) {
|
|
39957
|
+
for (const raw of text2.split(/\r?\n/)) {
|
|
39958
|
+
const t = raw.trim();
|
|
39959
|
+
if (!t) continue;
|
|
39960
|
+
if (t.startsWith("#") || t.startsWith("//")) continue;
|
|
39961
|
+
return /^playbook\b/i.test(t);
|
|
39962
|
+
}
|
|
39963
|
+
return false;
|
|
39964
|
+
},
|
|
39965
|
+
parse: parsePlaybook,
|
|
39966
|
+
render(text2, config) {
|
|
39967
|
+
return renderPlaybook(text2, config);
|
|
39968
|
+
},
|
|
39969
|
+
lint(text2) {
|
|
39970
|
+
try {
|
|
39971
|
+
const lay = layoutPlaybook(parsePlaybook(text2));
|
|
39972
|
+
return [
|
|
39973
|
+
...lay.errors.map(
|
|
39974
|
+
(message) => ({
|
|
39975
|
+
severity: "error",
|
|
39976
|
+
code: "playbook/validation",
|
|
39977
|
+
message,
|
|
39978
|
+
fatal: false
|
|
39979
|
+
})
|
|
39980
|
+
),
|
|
39981
|
+
...lay.warnings.map(
|
|
39982
|
+
(message) => ({
|
|
39983
|
+
severity: "warning",
|
|
39984
|
+
code: "playbook/warning",
|
|
39985
|
+
message,
|
|
39986
|
+
fatal: false
|
|
39987
|
+
})
|
|
39988
|
+
)
|
|
39989
|
+
];
|
|
39990
|
+
} catch {
|
|
39991
|
+
return [];
|
|
39992
|
+
}
|
|
39993
|
+
}
|
|
39994
|
+
};
|
|
39995
|
+
|
|
38476
39996
|
// src/core/api.ts
|
|
38477
39997
|
var plugins = [
|
|
38478
39998
|
genogram,
|
|
@@ -38520,7 +40040,8 @@ var plugins = [
|
|
|
38520
40040
|
idef0,
|
|
38521
40041
|
threatmodel,
|
|
38522
40042
|
welding,
|
|
38523
|
-
floorplan
|
|
40043
|
+
floorplan,
|
|
40044
|
+
playbook
|
|
38524
40045
|
];
|
|
38525
40046
|
function detectPlugin(text2, config) {
|
|
38526
40047
|
if (config?.type) {
|
|
@@ -38531,7 +40052,7 @@ function detectPlugin(text2, config) {
|
|
|
38531
40052
|
if (plugin.detect(text2)) return plugin;
|
|
38532
40053
|
}
|
|
38533
40054
|
throw new Error(
|
|
38534
|
-
"Cannot detect diagram type. Start your text with 'genogram', 'ecomap', 'pedigree', 'phylo', 'sociogram', 'timing', 'logic', 'circuit', 'blockdiagram', 'ladder', 'sld', 'entity-structure', 'fishbone', 'venn', 'flowchart', 'mindmap', 'matrix', 'orgchart', 'state', 'pid', 'erd', 'breadboard', 'bpmn', 'fbd', 'sfc', 'prisma', 'usecase', 'pert', 'sequence', 'petri', 'network', 'umlclass', 'faulttree', 'bowtie', or '
|
|
40055
|
+
"Cannot detect diagram type. Start your text with 'genogram', 'ecomap', 'pedigree', 'phylo', 'sociogram', 'timing', 'logic', 'circuit', 'blockdiagram', 'ladder', 'sld', 'entity-structure', 'fishbone', 'venn', 'flowchart', 'mindmap', 'matrix', 'orgchart', 'state', 'pid', 'erd', 'breadboard', 'bpmn', 'fbd', 'sfc', 'prisma', 'usecase', 'pert', 'sequence', 'petri', 'network', 'umlclass', 'faulttree', 'bowtie', 'floorplan', or 'playbook'."
|
|
38535
40056
|
);
|
|
38536
40057
|
}
|
|
38537
40058
|
function stripCodeFences(text2) {
|
|
@@ -38669,5 +40190,5 @@ function renderWithPlugin(prepared, plugin, config) {
|
|
|
38669
40190
|
}
|
|
38670
40191
|
|
|
38671
40192
|
export { FLOORPLAN_SYMBOLS, GEOMETRY, bowtie2 as bowtie, causalloop, decisiontree, drawDeviceIcon, epc, eventtree, faulttree, fmea, gitgraph, iconSize, idef0, markov, network, parse, parseResult, pert, petri, pid, prisma, render, renderEquip, renderPreview, renderResult, sequence, state, threatmodel, timeline, umlclass, usecase, welding };
|
|
38672
|
-
//# sourceMappingURL=chunk-
|
|
38673
|
-
//# sourceMappingURL=chunk-
|
|
40193
|
+
//# sourceMappingURL=chunk-UFXDAIDD.js.map
|
|
40194
|
+
//# sourceMappingURL=chunk-UFXDAIDD.js.map
|