@johnmorrisdotca/kyogijo-stadiums 0.0.0-stage → 0.4.0
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/CHANGELOG.md +129 -0
- package/LICENSE +21 -0
- package/NOTICE.md +293 -0
- package/README.md +600 -2
- package/dist/annotate.d.ts +47 -0
- package/dist/annotate.js +349 -0
- package/dist/atlas-entry.d.ts +4 -0
- package/dist/atlas-entry.js +3 -0
- package/dist/atlas.d.ts +131 -0
- package/dist/atlas.js +149 -0
- package/dist/background.d.ts +56 -0
- package/dist/background.js +127 -0
- package/dist/ballpark.d.ts +48 -0
- package/dist/ballpark.js +129 -0
- package/dist/canvas-entry.d.ts +2 -0
- package/dist/canvas-entry.js +2 -0
- package/dist/canvas.d.ts +85 -0
- package/dist/canvas.js +279 -0
- package/dist/charts-entry.d.ts +10 -0
- package/dist/charts-entry.js +8 -0
- package/dist/charts.baseball.d.ts +30 -0
- package/dist/charts.baseball.js +152 -0
- package/dist/charts.curling.d.ts +52 -0
- package/dist/charts.curling.js +196 -0
- package/dist/charts.d.ts +172 -0
- package/dist/charts.js +508 -0
- package/dist/charts.network.d.ts +66 -0
- package/dist/charts.network.js +64 -0
- package/dist/charts.soccer.d.ts +43 -0
- package/dist/charts.soccer.js +191 -0
- package/dist/charts.types.d.ts +396 -0
- package/dist/charts.types.js +1 -0
- package/dist/colour.d.ts +29 -0
- package/dist/colour.js +97 -0
- package/dist/common.d.ts +14 -0
- package/dist/common.js +32 -0
- package/dist/court.d.ts +89 -0
- package/dist/court.js +248 -0
- package/dist/courts.style.d.ts +20 -0
- package/dist/courts.style.js +54 -0
- package/dist/cricket.board.d.ts +18 -0
- package/dist/cricket.board.js +67 -0
- package/dist/curling.board.d.ts +24 -0
- package/dist/curling.board.js +147 -0
- package/dist/d3-entry.d.ts +3 -0
- package/dist/d3-entry.js +2 -0
- package/dist/d3.d.ts +45 -0
- package/dist/d3.js +56 -0
- package/dist/draw.d.ts +52 -0
- package/dist/draw.js +705 -0
- package/dist/element-define.d.ts +2 -0
- package/dist/element-define.js +4 -0
- package/dist/element-entry.d.ts +1 -0
- package/dist/element-entry.js +2 -0
- package/dist/element.d.ts +39 -0
- package/dist/element.js +321 -0
- package/dist/eras-entry.d.ts +2 -0
- package/dist/eras-entry.js +2 -0
- package/dist/eras.d.ts +204 -0
- package/dist/eras.js +396 -0
- package/dist/events-entry.d.ts +3 -0
- package/dist/events-entry.js +3 -0
- package/dist/events.d.ts +54 -0
- package/dist/events.data.d.ts +59 -0
- package/dist/events.data.js +297 -0
- package/dist/events.js +127 -0
- package/dist/features.d.ts +31 -0
- package/dist/features.js +284 -0
- package/dist/field.score.d.ts +5 -0
- package/dist/field.score.js +33 -0
- package/dist/fields/afl.d.ts +11 -0
- package/dist/fields/afl.js +103 -0
- package/dist/fields/cricket.d.ts +9 -0
- package/dist/fields/cricket.js +81 -0
- package/dist/fields/fieldhockey.d.ts +6 -0
- package/dist/fields/fieldhockey.js +81 -0
- package/dist/fields/gaelic.d.ts +10 -0
- package/dist/fields/gaelic.js +86 -0
- package/dist/fields/handball.d.ts +6 -0
- package/dist/fields/handball.js +85 -0
- package/dist/fields/lacrosse.d.ts +17 -0
- package/dist/fields/lacrosse.js +169 -0
- package/dist/fields/rugby.d.ts +10 -0
- package/dist/fields/rugby.js +130 -0
- package/dist/fields.d.ts +12 -0
- package/dist/fields.js +44 -0
- package/dist/fields.kit.d.ts +63 -0
- package/dist/fields.kit.js +108 -0
- package/dist/fields.style.d.ts +4 -0
- package/dist/fields.style.js +19 -0
- package/dist/fields.types.d.ts +155 -0
- package/dist/fields.types.js +1 -0
- package/dist/format.d.ts +10 -0
- package/dist/format.js +28 -0
- package/dist/formations.d.ts +18 -0
- package/dist/formations.js +211 -0
- package/dist/frames-entry.d.ts +8 -0
- package/dist/frames-entry.js +7 -0
- package/dist/frames.d.ts +199 -0
- package/dist/frames.js +271 -0
- package/dist/frames.providers.d.ts +16 -0
- package/dist/frames.providers.js +302 -0
- package/dist/geojson-entry.d.ts +2 -0
- package/dist/geojson-entry.js +2 -0
- package/dist/geojson.d.ts +79 -0
- package/dist/geojson.js +147 -0
- package/dist/geometry-entry.d.ts +7 -0
- package/dist/geometry-entry.js +5 -0
- package/dist/geometry.d.ts +295 -0
- package/dist/geometry.js +361 -0
- package/dist/grounds-entry.d.ts +4 -0
- package/dist/grounds-entry.js +3 -0
- package/dist/grounds.d.ts +134 -0
- package/dist/grounds.data.d.ts +2 -0
- package/dist/grounds.data.js +1849 -0
- package/dist/grounds.js +74 -0
- package/dist/heat.colour.d.ts +37 -0
- package/dist/heat.colour.js +54 -0
- package/dist/heat.d.ts +43 -0
- package/dist/heat.js +614 -0
- package/dist/heat.stats.d.ts +123 -0
- package/dist/heat.stats.js +413 -0
- package/dist/heat.types.d.ts +145 -0
- package/dist/heat.types.js +1 -0
- package/dist/heat.zones.d.ts +54 -0
- package/dist/heat.zones.js +424 -0
- package/dist/homeside.d.ts +16 -0
- package/dist/homeside.js +43 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +29 -0
- package/dist/layers-entry.d.ts +19 -0
- package/dist/layers-entry.js +11 -0
- package/dist/layers.d.ts +41 -0
- package/dist/layers.env.d.ts +72 -0
- package/dist/layers.env.js +79 -0
- package/dist/layers.js +533 -0
- package/dist/layers.marks.d.ts +45 -0
- package/dist/layers.marks.js +168 -0
- package/dist/layers.types.d.ts +226 -0
- package/dist/layers.types.js +1 -0
- package/dist/layout.d.ts +97 -0
- package/dist/layout.js +381 -0
- package/dist/layout.style.d.ts +6 -0
- package/dist/layout.style.js +17 -0
- package/dist/layouts-entry.d.ts +5 -0
- package/dist/layouts-entry.js +4 -0
- package/dist/leagues/aussie.d.ts +57 -0
- package/dist/leagues/aussie.js +36 -0
- package/dist/leagues/baseball.d.ts +58 -0
- package/dist/leagues/baseball.js +171 -0
- package/dist/leagues/basketball.d.ts +67 -0
- package/dist/leagues/basketball.js +128 -0
- package/dist/leagues/courts.d.ts +212 -0
- package/dist/leagues/courts.js +114 -0
- package/dist/leagues/cricket.d.ts +45 -0
- package/dist/leagues/cricket.js +34 -0
- package/dist/leagues/curling.d.ts +64 -0
- package/dist/leagues/curling.js +53 -0
- package/dist/leagues/fieldhockey.d.ts +48 -0
- package/dist/leagues/fieldhockey.js +29 -0
- package/dist/leagues/fields.d.ts +10 -0
- package/dist/leagues/fields.js +6 -0
- package/dist/leagues/football.d.ts +8 -0
- package/dist/leagues/football.js +82 -0
- package/dist/leagues/gaelic.d.ts +50 -0
- package/dist/leagues/gaelic.js +29 -0
- package/dist/leagues/handball.d.ts +45 -0
- package/dist/leagues/handball.js +32 -0
- package/dist/leagues/hockey.d.ts +63 -0
- package/dist/leagues/hockey.js +166 -0
- package/dist/leagues/lacrosse.d.ts +103 -0
- package/dist/leagues/lacrosse.js +175 -0
- package/dist/leagues/rugby.d.ts +59 -0
- package/dist/leagues/rugby.js +72 -0
- package/dist/leagues/sources.d.ts +18 -0
- package/dist/leagues/sources.js +95 -0
- package/dist/leagues/types.d.ts +43 -0
- package/dist/leagues/types.js +5 -0
- package/dist/leagues-entry.d.ts +23 -0
- package/dist/leagues-entry.js +15 -0
- package/dist/leagues.d.ts +172 -0
- package/dist/leagues.js +462 -0
- package/dist/look.d.ts +50 -0
- package/dist/look.js +186 -0
- package/dist/mark.d.ts +35 -0
- package/dist/mark.js +59 -0
- package/dist/mount-entry.d.ts +2 -0
- package/dist/mount-entry.js +2 -0
- package/dist/mount.d.ts +63 -0
- package/dist/mount.js +167 -0
- package/dist/mow.d.ts +36 -0
- package/dist/mow.js +111 -0
- package/dist/names.d.ts +77 -0
- package/dist/names.js +97 -0
- package/dist/outline.d.ts +25 -0
- package/dist/outline.js +112 -0
- package/dist/parks-entry.d.ts +4 -0
- package/dist/parks-entry.js +3 -0
- package/dist/parks.d.ts +200 -0
- package/dist/parks.data.d.ts +2 -0
- package/dist/parks.data.js +4489 -0
- package/dist/parks.js +92 -0
- package/dist/plot-entry.d.ts +2 -0
- package/dist/plot-entry.js +2 -0
- package/dist/plot.d.ts +74 -0
- package/dist/plot.js +68 -0
- package/dist/png-entry.d.ts +2 -0
- package/dist/png-entry.js +2 -0
- package/dist/png.d.ts +26 -0
- package/dist/png.js +46 -0
- package/dist/ranges.d.ts +33 -0
- package/dist/ranges.js +178 -0
- package/dist/ranges.names.d.ts +31 -0
- package/dist/ranges.names.js +65 -0
- package/dist/react-entry.d.ts +2 -0
- package/dist/react-entry.js +2 -0
- package/dist/react.d.ts +53 -0
- package/dist/react.js +58 -0
- package/dist/replay-entry.d.ts +5 -0
- package/dist/replay-entry.js +4 -0
- package/dist/replay.control.d.ts +47 -0
- package/dist/replay.control.js +184 -0
- package/dist/replay.d.ts +170 -0
- package/dist/replay.dom.d.ts +21 -0
- package/dist/replay.dom.js +388 -0
- package/dist/replay.js +579 -0
- package/dist/replay.types.d.ts +152 -0
- package/dist/replay.types.js +1 -0
- package/dist/restyle.d.ts +21 -0
- package/dist/restyle.js +70 -0
- package/dist/road.d.ts +26 -0
- package/dist/road.draw.d.ts +6 -0
- package/dist/road.draw.js +269 -0
- package/dist/road.js +59 -0
- package/dist/road.model.d.ts +63 -0
- package/dist/road.model.js +215 -0
- package/dist/road.style.d.ts +7 -0
- package/dist/road.style.js +26 -0
- package/dist/road.types.d.ts +122 -0
- package/dist/road.types.js +1 -0
- package/dist/scene-entry.d.ts +7 -0
- package/dist/scene-entry.js +5 -0
- package/dist/scene.css.d.ts +44 -0
- package/dist/scene.css.js +296 -0
- package/dist/scene.d.ts +27 -0
- package/dist/scene.fit.d.ts +12 -0
- package/dist/scene.fit.js +18 -0
- package/dist/scene.flat.d.ts +38 -0
- package/dist/scene.flat.js +167 -0
- package/dist/scene.js +420 -0
- package/dist/scene.path.d.ts +40 -0
- package/dist/scene.path.js +357 -0
- package/dist/scene.types.d.ts +172 -0
- package/dist/scene.types.js +1 -0
- package/dist/scene.xml.d.ts +31 -0
- package/dist/scene.xml.js +81 -0
- package/dist/score.d.ts +70 -0
- package/dist/score.js +212 -0
- package/dist/sets.board.d.ts +22 -0
- package/dist/sets.board.js +89 -0
- package/dist/sheet.d.ts +18 -0
- package/dist/sheet.js +38 -0
- package/dist/special.d.ts +44 -0
- package/dist/special.js +204 -0
- package/dist/special.style.d.ts +8 -0
- package/dist/special.style.js +36 -0
- package/dist/strings.d.ts +581 -0
- package/dist/strings.js +411 -0
- package/dist/style.d.ts +35 -0
- package/dist/style.js +177 -0
- package/dist/surfaces/baseball.d.ts +33 -0
- package/dist/surfaces/baseball.js +208 -0
- package/dist/surfaces/baseball.walls.d.ts +16 -0
- package/dist/surfaces/baseball.walls.js +150 -0
- package/dist/surfaces/basketball.d.ts +8 -0
- package/dist/surfaces/basketball.js +199 -0
- package/dist/surfaces/curling.d.ts +23 -0
- package/dist/surfaces/curling.js +128 -0
- package/dist/surfaces/field.d.ts +12 -0
- package/dist/surfaces/field.js +163 -0
- package/dist/surfaces/football.d.ts +12 -0
- package/dist/surfaces/football.js +208 -0
- package/dist/surfaces/hockey.d.ts +10 -0
- package/dist/surfaces/hockey.js +166 -0
- package/dist/surfaces/net.d.ts +7 -0
- package/dist/surfaces/net.js +151 -0
- package/dist/surfaces/soccer-providers.d.ts +124 -0
- package/dist/surfaces/soccer-providers.js +334 -0
- package/dist/surfaces/soccer-spec.d.ts +138 -0
- package/dist/surfaces/soccer-spec.js +175 -0
- package/dist/surfaces/soccer.d.ts +25 -0
- package/dist/surfaces/soccer.js +270 -0
- package/dist/surfaces/surface.d.ts +180 -0
- package/dist/surfaces/surface.js +49 -0
- package/dist/team.common.d.ts +75 -0
- package/dist/team.common.js +97 -0
- package/dist/team.formation.d.ts +24 -0
- package/dist/team.formation.js +121 -0
- package/dist/team.geometry.d.ts +66 -0
- package/dist/team.geometry.js +304 -0
- package/dist/team.layers.d.ts +16 -0
- package/dist/team.layers.js +32 -0
- package/dist/team.motion.d.ts +9 -0
- package/dist/team.motion.js +325 -0
- package/dist/team.network.d.ts +43 -0
- package/dist/team.network.js +215 -0
- package/dist/team.shape.d.ts +12 -0
- package/dist/team.shape.js +217 -0
- package/dist/team.types.d.ts +189 -0
- package/dist/team.types.js +1 -0
- package/dist/text.d.ts +31 -0
- package/dist/text.js +88 -0
- package/dist/themes.d.ts +14 -0
- package/dist/themes.js +24 -0
- package/dist/types.d.ts +565 -0
- package/dist/types.js +1 -0
- package/dist/units.d.ts +45 -0
- package/dist/units.js +90 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/package.json +234 -4
package/dist/scene.js
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
// A drawing as data. The drawing is still written once, by the code that writes the SVG; the scene reads that markup (a tree that gives the same text back,
|
|
2
|
+
// see scene.xml.ts), works out what a browser would paint for each shape in light or dark (the stylesheet that is in the drawing, the custom properties,
|
|
3
|
+
// the inheritance), and lists the shapes in paint order with their transforms and clips. There is no second drawing of anything.
|
|
4
|
+
import { drawKyogijo } from "./draw.js";
|
|
5
|
+
import { evaluate, inlineStyle, lengthOf, matched, parseStylesheet, substitute } from "./scene.css.js";
|
|
6
|
+
import { IDENTITY, multiply, parseTransform, pathCommands } from "./scene.path.js";
|
|
7
|
+
import { decode, elements, parseSvg, textOf } from "./scene.xml.js";
|
|
8
|
+
import { KYOGIJO_STYLE } from "./style.js";
|
|
9
|
+
const START = { fill: "#000000", stroke: "none", strokeWidth: 1, lineJoin: "miter", lineCap: "butt", dash: undefined, fillRule: "nonzero", fillOpacity: 1, strokeOpacity: 1, fontSize: 16, fontWeight: 400, fontFamily: "sans-serif", anchor: "start", letterSpacing: 0, paintOrder: undefined };
|
|
10
|
+
/** The properties an element may carry as an attribute as well as in CSS. */
|
|
11
|
+
const PRESENTATION = ["fill", "stroke", "stroke-width", "stroke-linejoin", "stroke-linecap", "stroke-dasharray", "fill-rule", "fill-opacity", "stroke-opacity", "font-size", "font-weight", "font-family", "text-anchor", "letter-spacing", "paint-order", "opacity", "filter", "stop-color", "stop-opacity", "display", "visibility"];
|
|
12
|
+
const unit = (value, fallback) => {
|
|
13
|
+
const n = Number(value);
|
|
14
|
+
return value !== undefined && Number.isFinite(n) ? Math.min(1, Math.max(0, n)) : fallback;
|
|
15
|
+
};
|
|
16
|
+
function decodedAttrs(node) {
|
|
17
|
+
const out = {};
|
|
18
|
+
for (const [key, value] of Object.entries(node.attrs))
|
|
19
|
+
out[key] = decode(value);
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
/** The context of a node inside its parent's. */
|
|
23
|
+
function contextOf(parent, node, sheet) {
|
|
24
|
+
const attrs = decodedAttrs(node);
|
|
25
|
+
const css = { name: node.name, attrs, parent: parent.css };
|
|
26
|
+
const declarations = [];
|
|
27
|
+
for (const name of PRESENTATION)
|
|
28
|
+
if (attrs[name] !== undefined)
|
|
29
|
+
declarations.push([name, attrs[name]]);
|
|
30
|
+
declarations.push(...matched(sheet, css), ...inlineStyle(attrs.style));
|
|
31
|
+
let vars = parent.vars;
|
|
32
|
+
if (declarations.some(([name]) => name.startsWith("--"))) {
|
|
33
|
+
const own = { ...vars };
|
|
34
|
+
for (const [name, value] of declarations)
|
|
35
|
+
if (name.startsWith("--"))
|
|
36
|
+
own[name] = value;
|
|
37
|
+
vars = own;
|
|
38
|
+
}
|
|
39
|
+
const inherited = { ...parent.inherited };
|
|
40
|
+
const own = { opacity: 1, filter: undefined, stopColor: undefined, stopOpacity: 1, hidden: false };
|
|
41
|
+
// The font size first: a length in em is of the node's own.
|
|
42
|
+
const sized = [...declarations.filter(([name]) => name === "font-size"), ...declarations.filter(([name]) => name !== "font-size" && !name.startsWith("--"))];
|
|
43
|
+
for (const [name, raw] of sized) {
|
|
44
|
+
const value = substitute(raw, vars);
|
|
45
|
+
if (value === undefined)
|
|
46
|
+
continue;
|
|
47
|
+
const v = value.trim();
|
|
48
|
+
switch (name) {
|
|
49
|
+
case "font-size":
|
|
50
|
+
inherited.fontSize = lengthOf(v, parent.inherited.fontSize) ?? inherited.fontSize;
|
|
51
|
+
break;
|
|
52
|
+
case "fill":
|
|
53
|
+
inherited.fill = v;
|
|
54
|
+
break;
|
|
55
|
+
case "stroke":
|
|
56
|
+
inherited.stroke = v;
|
|
57
|
+
break;
|
|
58
|
+
case "stroke-width":
|
|
59
|
+
inherited.strokeWidth = lengthOf(v, inherited.fontSize) ?? inherited.strokeWidth;
|
|
60
|
+
break;
|
|
61
|
+
case "stroke-linejoin":
|
|
62
|
+
if (v === "miter" || v === "round" || v === "bevel")
|
|
63
|
+
inherited.lineJoin = v;
|
|
64
|
+
break;
|
|
65
|
+
case "stroke-linecap":
|
|
66
|
+
if (v === "butt" || v === "round" || v === "square")
|
|
67
|
+
inherited.lineCap = v;
|
|
68
|
+
break;
|
|
69
|
+
case "stroke-dasharray": {
|
|
70
|
+
if (v === "none")
|
|
71
|
+
inherited.dash = undefined;
|
|
72
|
+
else {
|
|
73
|
+
const list = evaluate(v, inherited.fontSize).split(/[\s,]+/).filter((part) => part !== "").map(Number);
|
|
74
|
+
inherited.dash = list.length > 0 && list.every((n) => Number.isFinite(n) && n >= 0) && list.some((n) => n > 0) ? list : undefined;
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case "fill-rule":
|
|
79
|
+
if (v === "nonzero" || v === "evenodd")
|
|
80
|
+
inherited.fillRule = v;
|
|
81
|
+
break;
|
|
82
|
+
case "fill-opacity":
|
|
83
|
+
inherited.fillOpacity = unit(v, inherited.fillOpacity);
|
|
84
|
+
break;
|
|
85
|
+
case "stroke-opacity":
|
|
86
|
+
inherited.strokeOpacity = unit(v, inherited.strokeOpacity);
|
|
87
|
+
break;
|
|
88
|
+
case "font-weight":
|
|
89
|
+
inherited.fontWeight = v === "bold" ? 700 : v === "normal" ? 400 : Number.isFinite(Number(v)) ? Number(v) : inherited.fontWeight;
|
|
90
|
+
break;
|
|
91
|
+
case "font-family":
|
|
92
|
+
inherited.fontFamily = v;
|
|
93
|
+
break;
|
|
94
|
+
case "text-anchor":
|
|
95
|
+
if (v === "start" || v === "middle" || v === "end")
|
|
96
|
+
inherited.anchor = v;
|
|
97
|
+
break;
|
|
98
|
+
case "letter-spacing":
|
|
99
|
+
inherited.letterSpacing = v === "normal" ? 0 : (lengthOf(v, inherited.fontSize) ?? 0);
|
|
100
|
+
break;
|
|
101
|
+
case "paint-order":
|
|
102
|
+
inherited.paintOrder = v.startsWith("stroke") ? "stroke" : undefined;
|
|
103
|
+
break;
|
|
104
|
+
case "opacity":
|
|
105
|
+
own.opacity = unit(v, 1);
|
|
106
|
+
break;
|
|
107
|
+
case "filter":
|
|
108
|
+
own.filter = v === "none" ? undefined : v;
|
|
109
|
+
break;
|
|
110
|
+
case "stop-color":
|
|
111
|
+
own.stopColor = v;
|
|
112
|
+
break;
|
|
113
|
+
case "stop-opacity":
|
|
114
|
+
own.stopOpacity = unit(v, 1);
|
|
115
|
+
break;
|
|
116
|
+
case "display":
|
|
117
|
+
if (v === "none")
|
|
118
|
+
own.hidden = true;
|
|
119
|
+
break;
|
|
120
|
+
case "visibility":
|
|
121
|
+
if (v === "hidden" || v === "collapse")
|
|
122
|
+
own.hidden = true;
|
|
123
|
+
break;
|
|
124
|
+
default:
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const data = { ...parent.data };
|
|
128
|
+
for (const [key, value] of Object.entries(attrs))
|
|
129
|
+
if (key.startsWith("data-"))
|
|
130
|
+
data[key.slice(5)] = value;
|
|
131
|
+
const matrix = attrs.transform === undefined ? parent.matrix : multiply(parent.matrix, parseTransform(attrs.transform));
|
|
132
|
+
const surface = parent.surface || (attrs.class ?? "").split(/\s+/).includes("ky-surface-group");
|
|
133
|
+
return { ctx: { css, vars, inherited, opacity: parent.opacity * own.opacity, matrix, clips: parent.clips, data, surface, filter: own.filter === undefined ? parent.filter : [parent.filter, own.filter].filter((f) => f !== undefined).join(" ") }, own };
|
|
134
|
+
}
|
|
135
|
+
const SKIPPED = new Set(["defs", "title", "desc", "style", "metadata", "clipPath", "linearGradient", "radialGradient", "pattern", "marker", "mask", "symbol", "filter"]);
|
|
136
|
+
const SHAPES = new Set(["rect", "circle", "ellipse", "line", "polygon", "polyline", "path"]);
|
|
137
|
+
const n = (node, name, fallback = 0) => {
|
|
138
|
+
const v = Number(node[name]);
|
|
139
|
+
return node[name] !== undefined && Number.isFinite(v) ? v : fallback;
|
|
140
|
+
};
|
|
141
|
+
function* walkAll(node) {
|
|
142
|
+
yield node;
|
|
143
|
+
for (const child of elements(node))
|
|
144
|
+
yield* walkAll(child);
|
|
145
|
+
}
|
|
146
|
+
const isShape = (item) => item.kind !== "text" && item.kind !== "image";
|
|
147
|
+
const urlId = (value) => /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec(value)?.[1];
|
|
148
|
+
/**
|
|
149
|
+
* A drawing the package wrote, as a scene. `theme` works the colours out for light or dark: without it, the drawing's own `data-theme`, else light.
|
|
150
|
+
* A drawing made with `style: "none"` is read with the package's own stylesheet (`KYOGIJO_STYLE`); `css` adds a page's rules after the drawing's.
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* import { drawKyogijo } from "@johnmorrisdotca/kyogijo-stadiums";
|
|
154
|
+
* import { sceneFromSvg } from "@johnmorrisdotca/kyogijo-stadiums/scene";
|
|
155
|
+
* const scene = sceneFromSvg(drawKyogijo({ sport: "nhl", id: "rink" }), { theme: "dark" });
|
|
156
|
+
* console.log(scene.theme, scene.items[0].kind, scene.items[0].style.fill);
|
|
157
|
+
* // dark rect #1a2025
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export function sceneFromSvg(svg, options = {}) {
|
|
161
|
+
const root = parseSvg(svg);
|
|
162
|
+
if (root.name !== "svg")
|
|
163
|
+
throw new Error(`An <svg> is expected, not <${root.name}>`);
|
|
164
|
+
const rootAttrs = decodedAttrs(root);
|
|
165
|
+
const theme = options.theme ?? (rootAttrs["data-theme"] === "dark" ? "dark" : "light");
|
|
166
|
+
const own = [];
|
|
167
|
+
const find = (node) => {
|
|
168
|
+
if (node.name === "style")
|
|
169
|
+
own.push(textOf(node));
|
|
170
|
+
for (const child of elements(node))
|
|
171
|
+
find(child);
|
|
172
|
+
};
|
|
173
|
+
find(root);
|
|
174
|
+
const sheet = parseStylesheet(own.length > 0 ? own.join("\n") : KYOGIJO_STYLE, theme);
|
|
175
|
+
if (options.css !== undefined)
|
|
176
|
+
parseStylesheet(options.css, theme, sheet);
|
|
177
|
+
const html = { name: "html", attrs: { "data-theme": theme } };
|
|
178
|
+
const top = { css: html, vars: {}, inherited: START, opacity: 1, matrix: IDENTITY, clips: [], data: {}, surface: false, filter: undefined };
|
|
179
|
+
// Everything with an id that a shape may refer to, with the chain of nodes above it (for the context it is computed in).
|
|
180
|
+
const definitions = new Map();
|
|
181
|
+
const index = (node, chain) => {
|
|
182
|
+
const id = node.attrs.id;
|
|
183
|
+
if (id !== undefined && (node.name === "clipPath" || node.name === "linearGradient" || node.name === "pattern"))
|
|
184
|
+
definitions.set(decode(id), { node, chain });
|
|
185
|
+
for (const child of elements(node))
|
|
186
|
+
index(child, [...chain, node]);
|
|
187
|
+
};
|
|
188
|
+
index(root, []);
|
|
189
|
+
const contexts = new Map();
|
|
190
|
+
const contextAt = (chain, node) => {
|
|
191
|
+
const cached = contexts.get(node);
|
|
192
|
+
if (cached !== undefined)
|
|
193
|
+
return cached;
|
|
194
|
+
let parent = top;
|
|
195
|
+
for (const above of chain) {
|
|
196
|
+
let known = contexts.get(above);
|
|
197
|
+
if (known === undefined) {
|
|
198
|
+
known = contextOf(parent, above, sheet);
|
|
199
|
+
contexts.set(above, known);
|
|
200
|
+
}
|
|
201
|
+
parent = known.ctx;
|
|
202
|
+
}
|
|
203
|
+
const made = contextOf(parent, node, sheet);
|
|
204
|
+
contexts.set(node, made);
|
|
205
|
+
return made;
|
|
206
|
+
};
|
|
207
|
+
const clips = [];
|
|
208
|
+
const clipIndex = new Map();
|
|
209
|
+
const gradients = [];
|
|
210
|
+
const gradientDone = new Set();
|
|
211
|
+
const patterns = [];
|
|
212
|
+
const patternDone = new Set();
|
|
213
|
+
const paintOf = (value) => {
|
|
214
|
+
const id = urlId(value);
|
|
215
|
+
if (id === undefined)
|
|
216
|
+
return value;
|
|
217
|
+
const def = definitions.get(id);
|
|
218
|
+
if (def === undefined)
|
|
219
|
+
return "none";
|
|
220
|
+
if (def.node.name === "linearGradient") {
|
|
221
|
+
if (!gradientDone.has(id)) {
|
|
222
|
+
gradientDone.add(id);
|
|
223
|
+
const a = decodedAttrs(def.node);
|
|
224
|
+
const stops = elements(def.node)
|
|
225
|
+
.filter((stop) => stop.name === "stop")
|
|
226
|
+
.map((stop) => {
|
|
227
|
+
const s = contextAt([...def.chain, def.node], stop);
|
|
228
|
+
const offset = decodedAttrs(stop).offset ?? "0";
|
|
229
|
+
return { offset: unit(offset.endsWith("%") ? String(Number(offset.slice(0, -1)) / 100) : offset, 0), color: s.own.stopColor ?? "#000000", opacity: s.own.stopOpacity };
|
|
230
|
+
});
|
|
231
|
+
gradients.push({ id, x1: n(a, "x1"), y1: n(a, "y1"), x2: n(a, "x2", 1), y2: n(a, "y2"), stops });
|
|
232
|
+
}
|
|
233
|
+
return { gradient: id };
|
|
234
|
+
}
|
|
235
|
+
if (def.node.name === "pattern") {
|
|
236
|
+
if (!patternDone.has(id)) {
|
|
237
|
+
patternDone.add(id);
|
|
238
|
+
const a = decodedAttrs(def.node);
|
|
239
|
+
const at = contextAt(def.chain, def.node).ctx;
|
|
240
|
+
const found = [];
|
|
241
|
+
for (const child of elements(def.node))
|
|
242
|
+
collect(child, { ...at, matrix: IDENTITY, clips: [] }, found);
|
|
243
|
+
patterns.push({ id, width: n(a, "width"), height: n(a, "height"), shapes: found.filter(isShape) });
|
|
244
|
+
}
|
|
245
|
+
return { pattern: id };
|
|
246
|
+
}
|
|
247
|
+
return "none";
|
|
248
|
+
};
|
|
249
|
+
/** Clip index for a `clip-path` attribute on a node whose frame is `matrix`. */
|
|
250
|
+
const clipFor = (value, matrix) => {
|
|
251
|
+
const id = value === undefined ? undefined : urlId(value);
|
|
252
|
+
if (id === undefined)
|
|
253
|
+
return undefined;
|
|
254
|
+
const def = definitions.get(id);
|
|
255
|
+
if (def === undefined || def.node.name !== "clipPath")
|
|
256
|
+
return undefined;
|
|
257
|
+
const key = `${id}|${matrix.join(",")}`;
|
|
258
|
+
const known = clipIndex.get(key);
|
|
259
|
+
if (known !== undefined)
|
|
260
|
+
return known;
|
|
261
|
+
const at = contextAt(def.chain, def.node).ctx;
|
|
262
|
+
const found = [];
|
|
263
|
+
for (const child of elements(def.node))
|
|
264
|
+
collect(child, { ...at, matrix: IDENTITY, clips: [] }, found);
|
|
265
|
+
clips.push({ id, matrix, shapes: found.filter(isShape) });
|
|
266
|
+
clipIndex.set(key, clips.length - 1);
|
|
267
|
+
return clips.length - 1;
|
|
268
|
+
};
|
|
269
|
+
const styleOf = (ctx) => ({
|
|
270
|
+
fill: paintOf(ctx.inherited.fill),
|
|
271
|
+
stroke: paintOf(ctx.inherited.stroke),
|
|
272
|
+
strokeWidth: ctx.inherited.strokeWidth,
|
|
273
|
+
opacity: ctx.opacity,
|
|
274
|
+
fillOpacity: ctx.inherited.fillOpacity,
|
|
275
|
+
strokeOpacity: ctx.inherited.strokeOpacity,
|
|
276
|
+
lineJoin: ctx.inherited.lineJoin,
|
|
277
|
+
lineCap: ctx.inherited.lineCap,
|
|
278
|
+
...(ctx.inherited.dash === undefined ? {} : { dash: ctx.inherited.dash }),
|
|
279
|
+
fillRule: ctx.inherited.fillRule,
|
|
280
|
+
...(ctx.inherited.paintOrder === undefined ? {} : { paintOrder: ctx.inherited.paintOrder }),
|
|
281
|
+
...(ctx.filter === undefined ? {} : { filter: ctx.filter }),
|
|
282
|
+
});
|
|
283
|
+
const baseOf = (node, attrs, ctx) => {
|
|
284
|
+
const title = elements(node).find((child) => child.name === "title");
|
|
285
|
+
return {
|
|
286
|
+
class: attrs.class ?? "",
|
|
287
|
+
...(attrs.id === undefined ? {} : { id: attrs.id }),
|
|
288
|
+
...(Object.keys(ctx.data).length === 0 ? {} : { data: ctx.data }),
|
|
289
|
+
...(title === undefined ? {} : { label: textOf(title) }),
|
|
290
|
+
matrix: ctx.matrix,
|
|
291
|
+
clips: ctx.clips,
|
|
292
|
+
...(ctx.surface ? { surface: true } : {}),
|
|
293
|
+
style: styleOf(ctx),
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
/** Shapes (and text and images when `out` takes them) of a node and below, in paint order. */
|
|
297
|
+
function collect(node, parent, out) {
|
|
298
|
+
if (SKIPPED.has(node.name))
|
|
299
|
+
return;
|
|
300
|
+
const { ctx: made, own } = contextOf(parent, node, sheet);
|
|
301
|
+
if (own.hidden)
|
|
302
|
+
return;
|
|
303
|
+
let ctx = made;
|
|
304
|
+
const attrs = decodedAttrs(node);
|
|
305
|
+
const clip = clipFor(attrs["clip-path"], ctx.matrix);
|
|
306
|
+
if (clip !== undefined)
|
|
307
|
+
ctx = { ...ctx, clips: [...ctx.clips, clip] };
|
|
308
|
+
if (SHAPES.has(node.name)) {
|
|
309
|
+
const base = baseOf(node, attrs, ctx);
|
|
310
|
+
switch (node.name) {
|
|
311
|
+
case "rect":
|
|
312
|
+
out.push({ ...base, kind: "rect", x: n(attrs, "x"), y: n(attrs, "y"), width: n(attrs, "width"), height: n(attrs, "height"), rx: n(attrs, "rx", n(attrs, "ry")) });
|
|
313
|
+
break;
|
|
314
|
+
case "circle":
|
|
315
|
+
out.push({ ...base, kind: "circle", cx: n(attrs, "cx"), cy: n(attrs, "cy"), r: n(attrs, "r") });
|
|
316
|
+
break;
|
|
317
|
+
case "ellipse":
|
|
318
|
+
out.push({ ...base, kind: "ellipse", cx: n(attrs, "cx"), cy: n(attrs, "cy"), rx: n(attrs, "rx"), ry: n(attrs, "ry") });
|
|
319
|
+
break;
|
|
320
|
+
case "line":
|
|
321
|
+
out.push({ ...base, kind: "line", x1: n(attrs, "x1"), y1: n(attrs, "y1"), x2: n(attrs, "x2"), y2: n(attrs, "y2") });
|
|
322
|
+
break;
|
|
323
|
+
case "polygon":
|
|
324
|
+
case "polyline": {
|
|
325
|
+
const v = (attrs.points ?? "").split(/[\s,]+/).filter((p) => p !== "").map(Number);
|
|
326
|
+
const points = [];
|
|
327
|
+
for (let i = 0; i + 1 < v.length; i += 2)
|
|
328
|
+
points.push([v[i], v[i + 1]]);
|
|
329
|
+
out.push({ ...base, kind: node.name, points });
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
default:
|
|
333
|
+
out.push({ ...base, kind: "path", d: attrs.d ?? "", commands: pathCommands(attrs.d ?? "") });
|
|
334
|
+
}
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (node.name === "text") {
|
|
338
|
+
// A run of text is one item; a `<tspan>` that sets a place (x or y, dx or dy) is another, at that place, with the style it has.
|
|
339
|
+
let x = n(attrs, "x");
|
|
340
|
+
let y = n(attrs, "y");
|
|
341
|
+
let run = "";
|
|
342
|
+
const flush = (at, from, source) => {
|
|
343
|
+
if (run === "")
|
|
344
|
+
return;
|
|
345
|
+
const i = at.inherited;
|
|
346
|
+
out.push({ ...baseOf(source, from, at), kind: "text", x, y, text: run, fontSize: i.fontSize, fontWeight: i.fontWeight, fontFamily: i.fontFamily, anchor: i.anchor, letterSpacing: i.letterSpacing });
|
|
347
|
+
run = "";
|
|
348
|
+
};
|
|
349
|
+
const words = (from, at, fromAttrs) => {
|
|
350
|
+
for (const child of from.children) {
|
|
351
|
+
if (typeof child === "string")
|
|
352
|
+
run += decode(child);
|
|
353
|
+
else if (child.name === "tspan") {
|
|
354
|
+
flush(at, fromAttrs, from);
|
|
355
|
+
const sub = contextOf(at, child, sheet);
|
|
356
|
+
const subAttrs = decodedAttrs(child);
|
|
357
|
+
x = subAttrs.x === undefined ? x + n(subAttrs, "dx") : n(subAttrs, "x") + n(subAttrs, "dx");
|
|
358
|
+
y = subAttrs.y === undefined ? y + n(subAttrs, "dy") : n(subAttrs, "y") + n(subAttrs, "dy");
|
|
359
|
+
words(child, sub.ctx, subAttrs);
|
|
360
|
+
flush(sub.ctx, { ...fromAttrs, ...subAttrs, class: [fromAttrs.class, subAttrs.class].filter((c) => c !== undefined && c !== "").join(" ") }, child);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
words(node, ctx, attrs);
|
|
365
|
+
flush(ctx, attrs, node);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (node.name === "image") {
|
|
369
|
+
const item = { ...baseOf(node, attrs, ctx), kind: "image", href: attrs.href ?? attrs["xlink:href"] ?? "", x: n(attrs, "x"), y: n(attrs, "y"), width: n(attrs, "width"), height: n(attrs, "height"), ...(attrs.preserveAspectRatio === undefined ? {} : { preserveAspectRatio: attrs.preserveAspectRatio }) };
|
|
370
|
+
out.push(item);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
for (const child of elements(node))
|
|
374
|
+
collect(child, ctx, out);
|
|
375
|
+
}
|
|
376
|
+
const items = [];
|
|
377
|
+
const rootContext = contextOf(top, root, sheet);
|
|
378
|
+
contexts.set(root, rootContext);
|
|
379
|
+
// The root's own `data-*` (the sport, the size, the version) are the scene's; a shape's are those of the groups it is in.
|
|
380
|
+
for (const child of elements(root))
|
|
381
|
+
collect(child, { ...rootContext.ctx, data: {} }, items);
|
|
382
|
+
const box = (rootAttrs.viewBox ?? "0 0 0 0").split(/[\s,]+/).map(Number);
|
|
383
|
+
const title = elements(root).find((child) => child.name === "title");
|
|
384
|
+
const desc = elements(root).find((child) => child.name === "desc");
|
|
385
|
+
const group = [...walkAll(root)].find((node) => (node.attrs.class ?? "").split(/\s+/).includes("ky-surface-group"));
|
|
386
|
+
const turn = group?.attrs.transform === undefined ? undefined : parseTransform(decode(group.attrs.transform));
|
|
387
|
+
const label = (rootAttrs["aria-labelledby"] ?? "").split(/\s+/)[0] ?? "";
|
|
388
|
+
return {
|
|
389
|
+
theme,
|
|
390
|
+
...(rootAttrs["data-sport"] === undefined ? {} : { sport: rootAttrs["data-sport"] }),
|
|
391
|
+
id: label.replace(/-t$/, "") || "ky",
|
|
392
|
+
title: title === undefined ? "" : textOf(title),
|
|
393
|
+
description: desc === undefined ? "" : textOf(desc),
|
|
394
|
+
orientation: rootAttrs["data-orientation"] === "portrait" ? "portrait" : "landscape",
|
|
395
|
+
...(turn === undefined ? {} : { turn }),
|
|
396
|
+
viewBox: { x: box[0] ?? 0, y: box[1] ?? 0, w: box[2] ?? 0, h: box[3] ?? 0 },
|
|
397
|
+
items,
|
|
398
|
+
clips,
|
|
399
|
+
gradients,
|
|
400
|
+
patterns,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
const isScene = (source) => typeof source === "object" && "items" in source && "viewBox" in source;
|
|
404
|
+
/**
|
|
405
|
+
* A drawing as plain data: the options of `drawKyogijo` (or the SVG of a chart or a replay frame, or a scene already made) as the shapes it is painted
|
|
406
|
+
* with, in order, each with its colours worked out for light or dark.
|
|
407
|
+
* @example
|
|
408
|
+
* ```ts
|
|
409
|
+
* import { sceneOf } from "@johnmorrisdotca/kyogijo-stadiums/scene";
|
|
410
|
+
* const scene = sceneOf({ sport: "nhl", id: "rink" });
|
|
411
|
+
* console.log(scene.viewBox, scene.items.length > 30, scene.items[0].kind);
|
|
412
|
+
* // { x: -8, y: -8, w: 216, h: 101 } true rect
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
export function sceneOf(source, options = {}) {
|
|
416
|
+
if (typeof source !== "string" && isScene(source))
|
|
417
|
+
return source;
|
|
418
|
+
const svg = typeof source === "string" ? source : drawKyogijo(source);
|
|
419
|
+
return sceneFromSvg(svg, options);
|
|
420
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Matrix, PathCommand, SceneShape } from "./scene.types.ts";
|
|
2
|
+
export declare const IDENTITY: Matrix;
|
|
3
|
+
/** The matrix that does `b` first and then `a`'s frame: the transform of a child inside a parent. */
|
|
4
|
+
export declare const multiply: (a: Matrix, b: Matrix) => Matrix;
|
|
5
|
+
/** The matrix that undoes `m`, or the identity when `m` flattens the plane. */
|
|
6
|
+
export declare function invert(m: Matrix): Matrix;
|
|
7
|
+
export declare const isIdentity: (m: Matrix) => boolean;
|
|
8
|
+
/** A point moved by a matrix. */
|
|
9
|
+
export declare const apply: (m: Matrix, x: number, y: number) => [number, number];
|
|
10
|
+
/** A `transform` attribute (`matrix`, `translate`, `scale`, `rotate`, in any run) as one matrix. */
|
|
11
|
+
export declare function parseTransform(text: string | undefined): Matrix;
|
|
12
|
+
/**
|
|
13
|
+
* Path data as absolute segments: `M`, `L`, `C`, `Q` and `Z`. Relative commands, `H`, `V`, `S` and `T` are worked out, and an arc is cubic curves.
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { pathCommands } from "@johnmorrisdotca/kyogijo-stadiums/scene";
|
|
17
|
+
* console.log(pathCommands("M0 0h10v5H0Z").map((c) => c.join(" ")));
|
|
18
|
+
* // [ 'M 0 0', 'L 10 0', 'L 10 5', 'L 0 5', 'Z' ]
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function pathCommands(d: string): PathCommand[];
|
|
22
|
+
/** The segments of a shape in its own frame: a rectangle, circle, ellipse, line, polygon, polyline or path as path commands. */
|
|
23
|
+
export declare function shapeCommands(shape: SceneShape): PathCommand[];
|
|
24
|
+
/** A run of points, and whether it was closed. */
|
|
25
|
+
export interface Ring {
|
|
26
|
+
points: [number, number][];
|
|
27
|
+
closed: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Segments as runs of points, curves cut into straight pieces no further than `tolerance` (in the same units) from the curve. `matrix` moves every
|
|
31
|
+
* point first.
|
|
32
|
+
*/
|
|
33
|
+
export declare function flatten(commands: readonly PathCommand[], matrix?: Matrix, tolerance?: number): Ring[];
|
|
34
|
+
/** The box round a shape's outline (not its stroke) after `matrix`, or undefined for a shape with no points. */
|
|
35
|
+
export declare function boundsOf(shape: SceneShape, matrix?: Matrix): {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
w: number;
|
|
39
|
+
h: number;
|
|
40
|
+
} | undefined;
|