@johnmorrisdotca/kyogijo-stadiums 0.5.0 → 0.6.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.
Files changed (193) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/NOTICE.md +179 -3
  3. package/README.md +43 -11
  4. package/dist/arenas-entry.d.ts +4 -0
  5. package/dist/arenas-entry.js +3 -0
  6. package/dist/arenas.d.ts +125 -0
  7. package/dist/arenas.data.d.ts +2 -0
  8. package/dist/arenas.data.js +3763 -0
  9. package/dist/arenas.js +72 -0
  10. package/dist/atlas-entry.d.ts +3 -1
  11. package/dist/atlas-entry.js +3 -2
  12. package/dist/atlas.d.ts +37 -19
  13. package/dist/atlas.js +92 -7
  14. package/dist/background.js +2 -1
  15. package/dist/budo/describe.d.ts +13 -0
  16. package/dist/budo/describe.js +120 -0
  17. package/dist/budo/dojo.api.d.ts +64 -0
  18. package/dist/budo/dojo.api.js +46 -0
  19. package/dist/budo/dojo.d.ts +72 -0
  20. package/dist/budo/dojo.js +300 -0
  21. package/dist/budo/judo.d.ts +61 -0
  22. package/dist/budo/judo.js +109 -0
  23. package/dist/budo/karate.d.ts +29 -0
  24. package/dist/budo/karate.js +80 -0
  25. package/dist/budo/kendo.d.ts +18 -0
  26. package/dist/budo/kendo.js +44 -0
  27. package/dist/budo/kimarite.d.ts +19 -0
  28. package/dist/budo/kimarite.js +33 -0
  29. package/dist/budo/kit.d.ts +16 -0
  30. package/dist/budo/kit.js +24 -0
  31. package/dist/budo/kyudo.d.ts +28 -0
  32. package/dist/budo/kyudo.js +66 -0
  33. package/dist/budo/looks.d.ts +28 -0
  34. package/dist/budo/looks.js +58 -0
  35. package/dist/budo/parts.d.ts +32 -0
  36. package/dist/budo/parts.js +10 -0
  37. package/dist/budo/plan.d.ts +64 -0
  38. package/dist/budo/plan.js +139 -0
  39. package/dist/budo/room.d.ts +133 -0
  40. package/dist/budo/room.js +111 -0
  41. package/dist/budo/sumo.d.ts +31 -0
  42. package/dist/budo/sumo.js +71 -0
  43. package/dist/budo/tatami.d.ts +55 -0
  44. package/dist/budo/tatami.js +156 -0
  45. package/dist/budo/types.d.ts +192 -0
  46. package/dist/budo/types.js +2 -0
  47. package/dist/budo-entry.d.ts +22 -0
  48. package/dist/budo-entry.js +13 -0
  49. package/dist/budo.board.d.ts +28 -0
  50. package/dist/budo.board.js +81 -0
  51. package/dist/budo.score.d.ts +18 -0
  52. package/dist/budo.score.js +52 -0
  53. package/dist/budo.style.d.ts +6 -0
  54. package/dist/budo.style.js +67 -0
  55. package/dist/charts.baseball.js +1 -1
  56. package/dist/charts.js +4 -4
  57. package/dist/charts.soccer.js +3 -3
  58. package/dist/charts.types.d.ts +10 -0
  59. package/dist/colour.d.ts +4 -4
  60. package/dist/colour.js +4 -4
  61. package/dist/crowd.d.ts +11 -1
  62. package/dist/crowd.draw.js +15 -9
  63. package/dist/crowd.js +17 -2
  64. package/dist/draw.js +156 -31
  65. package/dist/element-entry.d.ts +2 -0
  66. package/dist/element-entry.js +1 -0
  67. package/dist/element.d.ts +2 -2
  68. package/dist/element.js +2 -1
  69. package/dist/emblems-source.d.ts +50 -0
  70. package/dist/emblems-source.js +56 -0
  71. package/dist/eras.d.ts +3 -1
  72. package/dist/eras.js +8 -3
  73. package/dist/features.js +73 -0
  74. package/dist/field.data.d.ts +16 -0
  75. package/dist/field.data.js +50 -0
  76. package/dist/fields.kit.d.ts +2 -2
  77. package/dist/fields.types.d.ts +9 -3
  78. package/dist/formations.js +18 -7
  79. package/dist/formations.sports.d.ts +8 -0
  80. package/dist/formations.sports.js +251 -0
  81. package/dist/frames.providers.js +9 -1
  82. package/dist/halls.d.ts +65 -0
  83. package/dist/halls.js +122 -0
  84. package/dist/heat.js +17 -10
  85. package/dist/heat.zones.d.ts +4 -2
  86. package/dist/heat.zones.js +55 -3
  87. package/dist/homeside.js +7 -0
  88. package/dist/index.d.ts +15 -3
  89. package/dist/index.js +8 -1
  90. package/dist/layers-entry.d.ts +2 -1
  91. package/dist/layers-entry.js +1 -0
  92. package/dist/layers.d.ts +2 -1
  93. package/dist/layers.env.d.ts +10 -1
  94. package/dist/layers.env.js +33 -3
  95. package/dist/layers.js +30 -11
  96. package/dist/layers.types.d.ts +9 -2
  97. package/dist/leagues/budo.d.ts +196 -0
  98. package/dist/leagues/budo.js +105 -0
  99. package/dist/leagues/hockey.js +41 -19
  100. package/dist/leagues/sources.js +21 -0
  101. package/dist/leagues/types.d.ts +1 -1
  102. package/dist/leagues.d.ts +4 -1
  103. package/dist/leagues.js +102 -2
  104. package/dist/lineup.codes.d.ts +23 -0
  105. package/dist/lineup.codes.js +122 -0
  106. package/dist/logo.d.ts +25 -0
  107. package/dist/logo.js +47 -0
  108. package/dist/outline.d.ts +1 -1
  109. package/dist/outline.js +17 -3
  110. package/dist/pageant.js +7 -3
  111. package/dist/pointer-entry.d.ts +2 -0
  112. package/dist/pointer-entry.js +2 -0
  113. package/dist/pointer.d.ts +113 -0
  114. package/dist/pointer.js +151 -0
  115. package/dist/pool.d.ts +7 -7
  116. package/dist/pool.figures.d.ts +57 -55
  117. package/dist/pool.figures.js +59 -55
  118. package/dist/pool.js +7 -7
  119. package/dist/pool.labels.js +2 -1
  120. package/dist/ranges.names.js +6 -0
  121. package/dist/road.draw.js +3 -2
  122. package/dist/road.js +2 -1
  123. package/dist/road.types.d.ts +3 -0
  124. package/dist/rules.d.ts +138 -0
  125. package/dist/rules.js +511 -0
  126. package/dist/rules.types.d.ts +4 -0
  127. package/dist/rules.types.js +64 -0
  128. package/dist/score.d.ts +3 -0
  129. package/dist/score.js +23 -1
  130. package/dist/situation.d.ts +135 -0
  131. package/dist/situation.draw.d.ts +34 -0
  132. package/dist/situation.draw.js +202 -0
  133. package/dist/situation.js +337 -0
  134. package/dist/situation.style.d.ts +2 -0
  135. package/dist/situation.style.js +45 -0
  136. package/dist/situation.types.d.ts +77 -0
  137. package/dist/situation.types.js +3 -0
  138. package/dist/situation.widgets.d.ts +39 -0
  139. package/dist/situation.widgets.js +129 -0
  140. package/dist/strings.d.ts +175 -1
  141. package/dist/strings.js +364 -20
  142. package/dist/style.js +9 -2
  143. package/dist/surfaces/artistic.d.ts +3 -3
  144. package/dist/surfaces/artistic.js +3 -3
  145. package/dist/surfaces/baseball.js +2 -0
  146. package/dist/surfaces/basketball.js +1 -1
  147. package/dist/surfaces/budo.d.ts +6 -0
  148. package/dist/surfaces/budo.js +104 -0
  149. package/dist/surfaces/dive.d.ts +3 -3
  150. package/dist/surfaces/dive.js +3 -3
  151. package/dist/surfaces/football.js +7 -0
  152. package/dist/surfaces/hockey.js +3 -2
  153. package/dist/surfaces/polo.d.ts +4 -4
  154. package/dist/surfaces/polo.js +5 -5
  155. package/dist/surfaces/pool.d.ts +1 -1
  156. package/dist/surfaces/pool.js +3 -3
  157. package/dist/surfaces/surface.d.ts +32 -0
  158. package/dist/surfaces/venue.d.ts +1 -1
  159. package/dist/surfaces/venue.js +1 -1
  160. package/dist/target-entry.d.ts +2 -0
  161. package/dist/target-entry.js +2 -0
  162. package/dist/target.d.ts +200 -0
  163. package/dist/target.js +525 -0
  164. package/dist/team.common.d.ts +1 -1
  165. package/dist/team.common.js +3 -3
  166. package/dist/team.formation.d.ts +3 -1
  167. package/dist/team.formation.js +3 -3
  168. package/dist/team.layers.d.ts +2 -2
  169. package/dist/team.layers.js +4 -1
  170. package/dist/team.lineup.d.ts +27 -0
  171. package/dist/team.lineup.js +207 -0
  172. package/dist/team.network.js +6 -5
  173. package/dist/team.shape.js +5 -5
  174. package/dist/team.types.d.ts +40 -1
  175. package/dist/tenants.data.js +848 -0
  176. package/dist/turn-entry.d.ts +2 -2
  177. package/dist/turn-entry.js +1 -1
  178. package/dist/turn.camera.d.ts +12 -1
  179. package/dist/turn.camera.js +18 -2
  180. package/dist/turn.dom.js +11 -9
  181. package/dist/turn.kit.d.ts +2 -2
  182. package/dist/turn.style.js +4 -3
  183. package/dist/types.d.ts +43 -7
  184. package/dist/units.js +7 -0
  185. package/dist/uprights.budo.d.ts +17 -0
  186. package/dist/uprights.budo.js +87 -0
  187. package/dist/uprights.js +10 -1
  188. package/dist/uprights.pools.js +4 -4
  189. package/dist/version.d.ts +1 -1
  190. package/dist/version.js +1 -1
  191. package/dist/zones-entry.d.ts +2 -0
  192. package/dist/zones-entry.js +2 -0
  193. package/package.json +39 -3
@@ -0,0 +1,139 @@
1
+ import { chooseLeague } from "../leagues.js";
2
+ import { BUDO_LEAGUES, DOJO_LEAGUES, JUDO_LEAGUES, KARATE_LEAGUES, KENDO_LEAGUES, KYUDO_LEAGUES, SUMO_LEAGUES } from "../leagues/budo.js";
3
+ import { dojoLayout, dojoLeague, dojoSpecOf } from "./dojo.js";
4
+ import { contestOf, gridOf, judoParts, matOf, safetyOf, snapTo } from "./judo.js";
5
+ import { karateParts } from "./karate.js";
6
+ import { kendoParts, kendoSide } from "./kendo.js";
7
+ import { kyudoParts, shootersOf } from "./kyudo.js";
8
+ import { boxD } from "./kit.js";
9
+ import { emptyParts, grow, union } from "./parts.js";
10
+ import { dohyoHeight, sumoParts } from "./sumo.js";
11
+ import { lookOf } from "./looks.js";
12
+ /** The apothem of the Nippon Budokan's arena, an octagon 55 m across the flats (the Budokan's leaflet). */
13
+ export const BUDOKAN_ARENA = 27.5;
14
+ const isBudoLeague = (l) => typeof l === "object" && l !== null && BUDO_LEAGUES.includes(l);
15
+ /** The sports of the budō family. */
16
+ export const BUDO_SPORTS = Object.freeze(["judo", "karate", "kendo", "sumo", "kyudo", "dojo"]);
17
+ /** True for a sport of the budō family. */
18
+ export const isBudoSport = (sport) => BUDO_SPORTS.includes(sport);
19
+ const OWN = { judo: JUDO_LEAGUES, karate: KARATE_LEAGUES, kendo: KENDO_LEAGUES, sumo: SUMO_LEAGUES, kyudo: KYUDO_LEAGUES, dojo: DOJO_LEAGUES };
20
+ /** The league a set of options asks for: a rulebook's, else the sport's own. */
21
+ export function budoLeague(o) {
22
+ const sport = o.sport;
23
+ // `budo.range` names a kyūdōjō's kind, and `dojo.standard` a dojo's mats, where `league` does not.
24
+ const asked = o.league === undefined || o.league === "" ? (sport === "kyudo" ? o.budo?.range : sport === "dojo" ? dojoSpecOf(o.dojo).standard : undefined) : o.league;
25
+ if (asked !== undefined && asked !== "") {
26
+ const found = OWN[sport].find((l) => l.id === asked);
27
+ if (found === undefined)
28
+ throw new Error(`Unknown league "${String(asked)}" for "${sport}": use one of ${OWN[sport].map((l) => l.id).join(", ")}`);
29
+ return found;
30
+ }
31
+ const chosen = chooseLeague({ sport: o.sport, league: o.league }).league;
32
+ return isBudoLeague(chosen) ? chosen : OWN[sport][0];
33
+ }
34
+ /** The width a line is drawn at unless the drawing is at true widths: a little under two thousandths of the surface, so that a line stays in sight in a drawing that holds the whole of it. */
35
+ const drawn = (box) => Math.max(0.05, Math.round((box.x1 - box.x0) * 0.0018 * 100) / 100);
36
+ const halves = (b) => ({ left: { x0: b.x0, y0: b.y0, x1: (b.x0 + b.x1) / 2, y1: b.y1 }, right: { x0: (b.x0 + b.x1) / 2, y0: b.y0, x1: b.x1, y1: b.y1 } });
37
+ /** The octagon of the Budokan's arena as a path, centred on (cx, cy). */
38
+ export function octagonD(cx, cy, a) {
39
+ const s = a * Math.tan(Math.PI / 8);
40
+ const pts = [[cx - s, cy - a], [cx + s, cy - a], [cx + a, cy - s], [cx + a, cy + s], [cx + s, cy + a], [cx - s, cy + a], [cx - a, cy + s], [cx - a, cy - s]];
41
+ return `M${pts.map(([x, y]) => `${Math.round(x * 100) / 100} ${Math.round(y * 100) / 100}`).join("L")}Z`;
42
+ }
43
+ /** True when a point lies inside the Budokan's octagon about (cx, cy). */
44
+ export const inOctagon = (cx, cy, a, x, y) => Math.abs(x - cx) <= a + 1e-9 && Math.abs(y - cy) <= a + 1e-9 && Math.abs(x - cx) + Math.abs(y - cy) <= a * Math.SQRT2 + 1e-9;
45
+ function flag(v, def, label) {
46
+ if (v === undefined)
47
+ return def;
48
+ if (typeof v !== "boolean")
49
+ throw new TypeError(`${label} is true or false`);
50
+ return v;
51
+ }
52
+ /** The budō plan the options ask for. */
53
+ export function budoPlan(o) {
54
+ const sport = o.sport;
55
+ const league = budoLeague(o);
56
+ const fig = league.dimensions;
57
+ const style = o.budo ?? {};
58
+ const look = lookOf(sport, style.look ?? (sport === "dojo" ? dojoSpecOf(o.dojo).look : undefined));
59
+ const seams = flag(style.seams, true, "budo.seams");
60
+ const dspec = sport === "dojo" ? dojoSpecOf(o.dojo) : undefined;
61
+ // The weave of the mats: asked for by `texture`, and a traditional dojo's by default.
62
+ const texture = dspec !== undefined ? dspec.texture === "weave" || (dspec.texture === undefined && (dspec.pattern === undefined || dspec.pattern === "traditional" || dspec.pattern === "border")) : sport === "judo" && style.texture === "weave";
63
+ const base = { sport, league: league.id, figures: fig, look, texture };
64
+ const finish = (p) => ({ ...base, hall: undefined, ...p, drawnLine: drawn(p.box) });
65
+ switch (sport) {
66
+ case "judo": {
67
+ const f = fig;
68
+ const mat = matOf(style.tatami, f);
69
+ const contest = snapTo(contestOf(style.size, f), mat.width, f.contest.min, f.contest.max);
70
+ const safety = snapTo(safetyOf(style.safety, f), mat.width, f.safety.min, f.safety.max);
71
+ const { across, down } = gridOf(style.areas);
72
+ const tape = league.id === "ajjf" ? ["red", "white"] : league.id === "ijf-kata" ? ["white", "white"] : ["blue", "white"];
73
+ const L = judoParts({ fig: f, contest, safety, across, down, mat, platform: flag(style.platform, false, "budo.platform"), officials: flag(style.officials, false, "budo.officials"), start: flag(style.start, true, "budo.start"), seams, tape });
74
+ const first = L.contests[0];
75
+ const lastC = L.contests[L.contests.length - 1];
76
+ const ranges = { "contest-area": { left: first, right: lastC }, half: halves(L.tatami) };
77
+ const fm = f.marks;
78
+ ranges.start = fm === null ? first : { x0: (first.x0 + first.x1) / 2 - fm.fromCentre - 1.5, y0: (first.y0 + first.y1) / 2 - 2, x1: (first.x0 + first.x1) / 2 + fm.fromCentre + 1.5, y1: (first.y0 + first.y1) / 2 + 2 };
79
+ return withHall(style, finish({ box: L.tatami, extent: L.extent, areas: L.parts.areas, shapes: L.parts.shapes, lineWidth: 0.05, ranges, marks: L.marks, centre: { x: (first.x0 + first.x1) / 2, y: (first.y0 + first.y1) / 2, r: contest / 2 }, judo: L }), "judo");
80
+ }
81
+ case "karate": {
82
+ const f = fig;
83
+ const { across, down } = gridOf(style.areas);
84
+ const officials = flag(style.officials, false, "budo.officials");
85
+ const L = karateParts({ fig: f, across, down, officials, judges: flag(style.judges, true, "budo.judges"), seams });
86
+ const first = L.areas[0];
87
+ const lastA = L.areas[L.areas.length - 1];
88
+ const ranges = { "contest-area": { left: first, right: lastA }, half: halves(L.mats), start: { x0: (first.x0 + first.x1) / 2 - 3, y0: (first.y0 + first.y1) / 2 - 2, x1: (first.x0 + first.x1) / 2 + 3, y1: (first.y0 + first.y1) / 2 + 2 } };
89
+ return withHall(style, finish({ box: L.mats, extent: L.extent, areas: L.parts.areas, shapes: L.parts.shapes, lineWidth: 0.05, ranges, marks: L.marks, centre: { x: (first.x0 + first.x1) / 2, y: (first.y0 + first.y1) / 2, r: f.area * 0.1 }, karate: L }), "karate");
90
+ }
91
+ case "kendo": {
92
+ const f = fig;
93
+ const side = kendoSide(style.size, f);
94
+ const L = kendoParts(f, side, seams);
95
+ const ranges = { half: halves(L.floor), start: { x0: L.centre[0] - 3, y0: L.centre[1] - 2, x1: L.centre[0] + 3, y1: L.centre[1] + 2 } };
96
+ return withHall(style, finish({ box: L.floor, extent: L.extent, areas: L.parts.areas, shapes: L.parts.shapes, lineWidth: f.lineWidth.min, ranges, marks: L.marks, centre: { x: L.centre[0], y: L.centre[1], r: side * 0.08 }, kendo: L }), "kendo");
97
+ }
98
+ case "sumo": {
99
+ const f = fig;
100
+ const L = sumoParts(f, { height: dohyoHeight(style.height, f), tawara: flag(style.tawara, true, "budo.tawara"), tassels: flag(style.tassels, false, "budo.tassels") });
101
+ const ranges = { ...L.ranges, half: halves(L.platform) };
102
+ return finish({ box: L.platform, extent: L.extent, areas: L.parts.areas, shapes: L.parts.shapes, lineWidth: f.shikiri.width, ranges, marks: L.marks, centre: { x: L.centre[0], y: L.centre[1], r: L.ring * 0.4 }, sumo: L });
103
+ }
104
+ case "kyudo": {
105
+ const f = fig;
106
+ const L = kyudoParts(f, { shooters: shootersOf(style.shooters, f), targets: flag(style.targets, true, "budo.targets"), roof: flag(style.roof, true, "budo.roof") });
107
+ const ranges = { shajo: L.shajo, yamichi: L.yamichi, azuchi: grow(L.azuchi, 0.8) };
108
+ return finish({ box: L.range, extent: L.extent, areas: L.parts.areas, shapes: L.parts.shapes, lineWidth: 0.05, ranges, marks: L.marks, centre: { x: (L.shajo.x0 + L.shajo.x1) / 2, y: (L.shajo.y0 + L.shajo.y1) / 2, r: 1.5 }, kyudo: L });
109
+ }
110
+ default: {
111
+ const L = dojoLayout(o.dojo ?? {}, dojoLeague(league.id));
112
+ const f = L.floor;
113
+ const band = Math.max(1.5, (L.kamiza === "north" || L.kamiza === "south" ? L.length : L.width) * 0.22);
114
+ const kamiza = L.kamiza === "north" ? { ...f, y1: f.y0 + band } : L.kamiza === "south" ? { ...f, y0: f.y1 - band } : L.kamiza === "west" ? { ...f, x1: f.x0 + band } : { ...f, x0: f.x1 - band };
115
+ const ranges = { kamiza, half: halves(f) };
116
+ return finish({ box: f, extent: L.extent, areas: L.parts.areas, shapes: L.parts.shapes, lineWidth: 0.05, ranges, marks: L.marks, centre: { x: L.width / 2, y: L.length / 2, r: Math.min(L.width, L.length) * 0.15 }, dojo: L });
117
+ }
118
+ }
119
+ }
120
+ /** The Budokan's arena floor under a surface, when the options ask for it (`budo.hall`): the octagon centred on the surface, which must lie wholly inside it. */
121
+ function withHall(style, p, sport) {
122
+ const hall = style.hall;
123
+ if (hall === undefined)
124
+ return p;
125
+ if (hall !== "budokan")
126
+ throw new Error('budo.hall is "budokan"');
127
+ const cx = (p.box.x0 + p.box.x1) / 2;
128
+ const cy = (p.box.y0 + p.box.y1) / 2;
129
+ const a = BUDOKAN_ARENA;
130
+ const corners = [[p.extent.x0, p.extent.y0], [p.extent.x1, p.extent.y0], [p.extent.x0, p.extent.y1], [p.extent.x1, p.extent.y1]];
131
+ if (!corners.every(([x, y]) => inOctagon(cx, cy, a, x, y)))
132
+ throw new RangeError(`The ${sport} surface with everything round it does not fit in the Budokan's arena, an octagon of ${2 * a} m across the flats`);
133
+ const box = { x0: cx - a, y0: cy - a, x1: cx + a, y1: cy + a };
134
+ const floor = { id: "arena", feature: "floor", cls: "ky-bud-floor", d: octagonD(cx, cy, a) };
135
+ const parts = emptyParts();
136
+ parts.areas.push(floor);
137
+ return { ...p, hall: "budokan", box, extent: union([box, p.extent]), areas: [...parts.areas, ...p.areas], marks: [{ id: "arena", x: cx - a * 0.55, y: cy - a * 0.8, side: "top" }, ...p.marks], ranges: { ...p.ranges, half: halves(box) } };
138
+ }
139
+ export { boxD };
@@ -0,0 +1,133 @@
1
+ import type { SurfaceOptions } from "../types.ts";
2
+ import type { MBox } from "./parts.ts";
3
+ import type { Compass } from "./types.ts";
4
+ /** Where a figure of the room comes from: a `source` (a rule or a published figure), the `plan` (it follows from the drawn layout) or `ours` (a plausible figure of ours, no source gives it). */
5
+ export type RoomBasis = "source" | "plan" | "ours";
6
+ /** A wall: its centre line, thickness and height. */
7
+ export interface RoomWall {
8
+ id: string;
9
+ side: Compass | "octagon";
10
+ from: readonly [number, number];
11
+ to: readonly [number, number];
12
+ thickness: number;
13
+ height: number | undefined;
14
+ basis: RoomBasis;
15
+ }
16
+ /** A door or a window cut in a wall: the footprint in the wall, the sill (the height of its bottom) and the head (the height of its top). */
17
+ export interface RoomOpening {
18
+ id: string;
19
+ wall: Compass;
20
+ box: MBox;
21
+ sill: number;
22
+ head: number;
23
+ }
24
+ /** A balcony: the strip of floor it covers, its height above the mats, and its rows of seats, from the rail outwards, each higher than the one before. */
25
+ export interface RoomBalcony {
26
+ side: Compass;
27
+ box: MBox;
28
+ /** The height of the balcony's front edge above the mats. */
29
+ height: number;
30
+ depth: number;
31
+ rows: number;
32
+ rowDepth: number;
33
+ rowRise: number;
34
+ /** The height of the rail along the front edge. */
35
+ rail: number;
36
+ }
37
+ /** The room round a budō surface. */
38
+ export interface DojoRoom {
39
+ sport: string;
40
+ unit: "m";
41
+ /** The surface's box: the mats of a competition area, the floor of a dojo, the dohyō's platform, the range of a kyūdōjō. */
42
+ floor: MBox;
43
+ /** The mats: `thickness` is the height of a mat's top above the floor under it, and `edge` how its edge is shaped (a competition mat is covered in vinyl and its edge is rounded), where a competition area's mat stands up at the safety area's outer edge. */
44
+ mats: {
45
+ thickness: number;
46
+ edge: "rounded" | "square";
47
+ basis: RoomBasis;
48
+ } | undefined;
49
+ /** The wood round the mats, between them and the walls. */
50
+ surround: {
51
+ outer: MBox;
52
+ inner: MBox;
53
+ width: number;
54
+ material: "wood";
55
+ } | undefined;
56
+ walls: RoomWall[];
57
+ /** The raised platforms: a dojo's seat of honour, the sumo dohyō. */
58
+ platforms: {
59
+ id: string;
60
+ box: MBox;
61
+ height: number;
62
+ basis: RoomBasis;
63
+ }[];
64
+ /** The banners: a plain dark vertical banner on a wall (no emblem is drawn), `bottom` and `top` its heights. */
65
+ banners: {
66
+ id: string;
67
+ wall: Compass;
68
+ box: MBox;
69
+ width: number;
70
+ bottom: number;
71
+ top: number;
72
+ basis: RoomBasis;
73
+ }[];
74
+ balconies: RoomBalcony[];
75
+ doors: RoomOpening[];
76
+ windows: RoomOpening[];
77
+ /** The shrine of a dojo (the shinzen), with its height. */
78
+ shrine: {
79
+ box: MBox;
80
+ height: number;
81
+ } | undefined;
82
+ /** The Budokan's octagon floor: its centre, apothem and corners, going round. */
83
+ hall: {
84
+ id: "budokan";
85
+ centre: readonly [number, number];
86
+ apothem: number;
87
+ corners: readonly (readonly [number, number])[];
88
+ } | undefined;
89
+ /** The figures of this room that are ours, and not a source's: the heights of walls and banners, the dais, the balcony. */
90
+ notes: string[];
91
+ }
92
+ /** The gallery of a dojo for the crowd: where its first row is from the mats, its depth and the heights of its first and last rows, in metres, from the room's balcony data. */
93
+ export interface DojoGallery {
94
+ offset: number;
95
+ depth: number;
96
+ rows: number;
97
+ height: [number, number];
98
+ }
99
+ /** Our heights, in metres, where no source gives one. */
100
+ export declare const ROOM_HEIGHTS: Readonly<{
101
+ wall: 3;
102
+ dais: 0.3;
103
+ bannerBottom: 1;
104
+ bannerTop: 3;
105
+ door: 2;
106
+ windowSill: 1;
107
+ windowHead: 2.4;
108
+ shrine: 1.2;
109
+ rail: 1;
110
+ balcony: 4;
111
+ mat: 0.05;
112
+ rowRise: 0.4;
113
+ rowDepth: 0.8;
114
+ balconyDepth: 2.4;
115
+ balconyRows: 4;
116
+ }>;
117
+ /**
118
+ * The room of a budō surface as data: what a 3D view raises. A dojo has its walls (3 m, ours), wood surround, dais, banner, balconies, doors, windows and shrine as its spec asks for them; the other
119
+ * surfaces have what they have (the mats' thickness, the dohyō's platform at its height, the Budokan's octagon). Heights that no source gives are ours and are listed in `notes`.
120
+ * @example
121
+ * ```ts
122
+ * import { dojoRoom } from "@johnmorrisdotca/kyogijo-stadiums/budo";
123
+ * const room = dojoRoom({ sport: "dojo", dojo: { jo: 20, dais: true, banner: true } });
124
+ * console.log(room?.walls.length, room?.platforms[0]?.height, room?.banners.length);
125
+ * // 4 0.3 1
126
+ * ```
127
+ */
128
+ export declare function dojoRoom(o: Pick<SurfaceOptions, "sport" | "league" | "budo" | "dojo" | "range">): DojoRoom | undefined;
129
+ /**
130
+ * The gallery a dojo's crowd sits in, from the same balcony data as `dojoRoom()`, so that the 2D stands and a 3D view agree: a thin band of 4 rows (ours) close to the walls, just beyond the wood
131
+ * surround. A dojo that asks for no balcony still has the default one when it has a crowd; `balcony: false` asks for none, and the sport's own stands are drawn. Not a dojo: nothing.
132
+ */
133
+ export declare function dojoGallery(o: Pick<SurfaceOptions, "sport" | "league" | "budo" | "dojo" | "range">): DojoGallery | undefined;
@@ -0,0 +1,111 @@
1
+ import { dojoSpecOf } from "./dojo.js";
2
+ import { BUDOKAN_ARENA, budoPlan } from "./plan.js";
3
+ /** Our heights, in metres, where no source gives one. */
4
+ export const ROOM_HEIGHTS = Object.freeze({ wall: 3, dais: 0.3, bannerBottom: 1, bannerTop: 3, door: 2, windowSill: 1, windowHead: 2.4, shrine: 1.2, rail: 1, balcony: 4, mat: 0.05, rowRise: 0.4, rowDepth: 0.8, balconyDepth: 2.4, balconyRows: 4 });
5
+ const SIDES = ["north", "east", "south", "west"];
6
+ const COMPASS = { north: "north", south: "south", east: "east", west: "west", top: "north", bottom: "south", left: "west", right: "east" };
7
+ /**
8
+ * The room of a budō surface as data: what a 3D view raises. A dojo has its walls (3 m, ours), wood surround, dais, banner, balconies, doors, windows and shrine as its spec asks for them; the other
9
+ * surfaces have what they have (the mats' thickness, the dohyō's platform at its height, the Budokan's octagon). Heights that no source gives are ours and are listed in `notes`.
10
+ * @example
11
+ * ```ts
12
+ * import { dojoRoom } from "@johnmorrisdotca/kyogijo-stadiums/budo";
13
+ * const room = dojoRoom({ sport: "dojo", dojo: { jo: 20, dais: true, banner: true } });
14
+ * console.log(room?.walls.length, room?.platforms[0]?.height, room?.banners.length);
15
+ * // 4 0.3 1
16
+ * ```
17
+ */
18
+ export function dojoRoom(o) {
19
+ if (o.sport !== "judo" && o.sport !== "karate" && o.sport !== "kendo" && o.sport !== "sumo" && o.sport !== "kyudo" && o.sport !== "dojo")
20
+ return undefined;
21
+ const plan = budoPlan({ ...o, detail: "full" });
22
+ const ox = plan.box.x0;
23
+ const oy = plan.box.y0;
24
+ const mv = (b) => ({ x0: b.x0 - ox, y0: b.y0 - oy, x1: b.x1 - ox, y1: b.y1 - oy });
25
+ const floor = mv(plan.box);
26
+ const notes = [];
27
+ const room = { sport: o.sport, unit: "m", floor, mats: undefined, surround: undefined, walls: [], platforms: [], banners: [], balconies: [], doors: [], windows: [], shrine: undefined, hall: undefined, notes };
28
+ if (plan.judo !== undefined) {
29
+ const fig = plan.figures;
30
+ room.mats = { thickness: fig.tatami.thickness, edge: "rounded", basis: "source" };
31
+ }
32
+ if (plan.dojo !== undefined) {
33
+ const L = plan.dojo;
34
+ const spec = dojoSpecOf(o.dojo);
35
+ room.mats = { thickness: ROOM_HEIGHTS.mat, edge: "square", basis: "ours" };
36
+ if (L.surround > 0)
37
+ room.surround = { outer: mv(L.room), inner: mv(L.floor), width: L.surround, material: "wood" };
38
+ if (L.wall > 0) {
39
+ const r = mv(L.room);
40
+ const w = L.wall;
41
+ const ring = [
42
+ ["north", [r.x0 - w / 2, r.y0 - w / 2], [r.x1 + w / 2, r.y0 - w / 2]],
43
+ ["east", [r.x1 + w / 2, r.y0 - w / 2], [r.x1 + w / 2, r.y1 + w / 2]],
44
+ ["south", [r.x1 + w / 2, r.y1 + w / 2], [r.x0 - w / 2, r.y1 + w / 2]],
45
+ ["west", [r.x0 - w / 2, r.y1 + w / 2], [r.x0 - w / 2, r.y0 - w / 2]],
46
+ ];
47
+ for (const [side, from, to] of ring)
48
+ room.walls.push({ id: `wall-${side}`, side, from, to, thickness: w, height: ROOM_HEIGHTS.wall, basis: "ours" });
49
+ notes.push(`The walls are ${ROOM_HEIGHTS.wall} m high (ours).`);
50
+ }
51
+ if (L.dais !== undefined) {
52
+ room.platforms.push({ id: "dais", box: mv(L.dais), height: ROOM_HEIGHTS.dais, basis: "ours" });
53
+ notes.push(`The dais is ${ROOM_HEIGHTS.dais} m high and 1.5 m deep at most (ours).`);
54
+ }
55
+ if (L.banner !== undefined) {
56
+ room.banners.push({ id: "banner", wall: L.kamiza, box: mv(L.banner), width: L.banner.x1 - L.banner.x0 > L.banner.y1 - L.banner.y0 ? L.banner.x1 - L.banner.x0 : L.banner.y1 - L.banner.y0, bottom: ROOM_HEIGHTS.bannerBottom, top: ROOM_HEIGHTS.bannerTop, basis: "ours" });
57
+ notes.push("The banner is a plain dark vertical band, from 1 m to 3 m up the wall (ours).");
58
+ }
59
+ if (L.shrine !== undefined)
60
+ room.shrine = { box: mv(L.shrine), height: ROOM_HEIGHTS.shrine };
61
+ L.entrances.forEach((e, i) => room.doors.push({ id: `door-${i + 1}`, wall: e.wall, box: mv(e.box), sill: 0, head: ROOM_HEIGHTS.door }));
62
+ L.windows.forEach((e, i) => room.windows.push({ id: `window-${i + 1}`, wall: e.wall, box: mv(e.box), sill: ROOM_HEIGHTS.windowSill, head: ROOM_HEIGHTS.windowHead }));
63
+ const b = spec.balcony;
64
+ if (b !== undefined && b !== false) {
65
+ const own = typeof b === "object" && b !== null ? b : {};
66
+ const sides = own.sides === undefined ? SIDES : own.sides.map((s) => COMPASS[String(s)]).filter((s) => s !== undefined);
67
+ const depth = own.depth ?? ROOM_HEIGHTS.balconyDepth;
68
+ const r = mv(L.room);
69
+ const outer = { x0: r.x0 - L.wall, y0: r.y0 - L.wall, x1: r.x1 + L.wall, y1: r.y1 + L.wall };
70
+ for (const side of sides) {
71
+ const box = side === "north" ? { x0: outer.x0 - depth, y0: outer.y0 - depth, x1: outer.x1 + depth, y1: outer.y0 } : side === "south" ? { x0: outer.x0 - depth, y0: outer.y1, x1: outer.x1 + depth, y1: outer.y1 + depth } : side === "west" ? { x0: outer.x0 - depth, y0: outer.y0, x1: outer.x0, y1: outer.y1 } : { x0: outer.x1, y0: outer.y0, x1: outer.x1 + depth, y1: outer.y1 };
72
+ const rows = own.rows ?? ROOM_HEIGHTS.balconyRows;
73
+ room.balconies.push({ side, box, height: own.height ?? ROOM_HEIGHTS.balcony, depth, rows, rowDepth: depth / rows, rowRise: ROOM_HEIGHTS.rowRise, rail: ROOM_HEIGHTS.rail });
74
+ }
75
+ notes.push("The balcony's height, depth and rows are ours; where a source gives its seats (the Kodokan's gallery, about 900) the atlas hall says so.");
76
+ }
77
+ }
78
+ if (plan.sumo !== undefined)
79
+ room.platforms.push({ id: "dohyo", box: floor, height: plan.sumo.height, basis: "source" });
80
+ if (plan.hall === "budokan") {
81
+ const cx = (plan.box.x0 + plan.box.x1) / 2 - ox;
82
+ const cy = (plan.box.y0 + plan.box.y1) / 2 - oy;
83
+ const a = BUDOKAN_ARENA;
84
+ const k = a * (Math.SQRT2 - 1);
85
+ const pts = [[cx - k, cy - a], [cx + k, cy - a], [cx + a, cy - k], [cx + a, cy + k], [cx + k, cy + a], [cx - k, cy + a], [cx - a, cy + k], [cx - a, cy - k]];
86
+ room.hall = { id: "budokan", centre: [cx, cy], apothem: a, corners: pts };
87
+ pts.forEach((p, i) => room.walls.push({ id: `arena-${i + 1}`, side: "octagon", from: p, to: pts[(i + 1) % pts.length], thickness: 0, height: undefined, basis: "plan" }));
88
+ notes.push("The octagon is 55 m across the flats (the Budokan's leaflet); no height of its wall is given.");
89
+ }
90
+ if (room.mats !== undefined && plan.judo !== undefined)
91
+ notes.push("A competition mat's top stands 5 cm above the floor (the IJF's tatami): at the outer edge of the safety area its thickness shows.");
92
+ return room;
93
+ }
94
+ /**
95
+ * The gallery a dojo's crowd sits in, from the same balcony data as `dojoRoom()`, so that the 2D stands and a 3D view agree: a thin band of 4 rows (ours) close to the walls, just beyond the wood
96
+ * surround. A dojo that asks for no balcony still has the default one when it has a crowd; `balcony: false` asks for none, and the sport's own stands are drawn. Not a dojo: nothing.
97
+ */
98
+ export function dojoGallery(o) {
99
+ if (o.sport !== "dojo")
100
+ return undefined;
101
+ const spec = dojoSpecOf(o.dojo);
102
+ if (spec.balcony === false)
103
+ return undefined;
104
+ const room = dojoRoom({ ...o, dojo: { ...spec, eras: undefined, era: undefined, year: undefined, balcony: spec.balcony ?? true } });
105
+ const b = room?.balconies[0];
106
+ if (room === undefined || b === undefined)
107
+ return undefined;
108
+ const f = room.floor;
109
+ const offset = Math.min(...room.balconies.map((x) => (x.side === "north" ? f.y0 - x.box.y1 : x.side === "south" ? x.box.y0 - f.y1 : x.side === "west" ? f.x0 - x.box.x1 : x.box.x0 - f.x1)));
110
+ return { offset, depth: b.depth, rows: b.rows, height: [b.height, b.height + b.rows * b.rowRise] };
111
+ }
@@ -0,0 +1,31 @@
1
+ import type { SumoFigures } from "../leagues/budo.ts";
2
+ import { type MBox, type MMark, type Parts } from "./parts.ts";
3
+ /** What a sumo plan is. */
4
+ export interface SumoLayout {
5
+ parts: Parts;
6
+ /** The clay square: the playing surface. */
7
+ platform: MBox;
8
+ extent: MBox;
9
+ centre: [number, number];
10
+ /** The circle: the radius of its inside, the wrestlers' ring, and of the outer edge of the tawara. */
11
+ ring: number;
12
+ outer: number;
13
+ /** The centres of the 20 tawara, as angles in degrees (0 east, growing clockwise as the eye sees it), the tokudawara at 0, 90, 180 and 270. */
14
+ bales: {
15
+ angle: number;
16
+ tokuda: boolean;
17
+ }[];
18
+ /** The length of a tawara along the circle. */
19
+ baleLength: number;
20
+ height: number;
21
+ marks: MMark[];
22
+ ranges: Record<string, MBox>;
23
+ }
24
+ /** The height of the clay square the options ask for, held to 34 cm to 60 cm. */
25
+ export declare const dohyoHeight: (asked: number | undefined, fig: SumoFigures) => number;
26
+ /** The plan of the dohyō the options ask for. */
27
+ export declare function sumoParts(fig: SumoFigures, o: {
28
+ height: number;
29
+ tawara: boolean;
30
+ tassels: boolean;
31
+ }): SumoLayout;
@@ -0,0 +1,71 @@
1
+ import { annulusD, discArea, rectArea, tapeBar, within } from "./kit.js";
2
+ import { emptyParts, grow } from "./parts.js";
3
+ import { num } from "../format.js";
4
+ /** The height of the clay square the options ask for, held to 34 cm to 60 cm. */
5
+ export const dohyoHeight = (asked, fig) => (asked === undefined ? fig.height.drawn : within(asked, fig.height.min, fig.height.max, "The dohyō's height", "the Sumo Rules (Dohyō Regulations, Article 1)"));
6
+ const rd = (v) => Math.round(v * 1000) / 1000;
7
+ /** The plan of the dohyō the options ask for. */
8
+ export function sumoParts(fig, o) {
9
+ const side = fig.platform;
10
+ const c = side / 2;
11
+ const platform = { x0: 0, y0: 0, x1: side, y1: side };
12
+ const inner = fig.circle / 2;
13
+ const wb = fig.bale.width;
14
+ const rb = inner + wb / 2;
15
+ const outer = inner + wb;
16
+ const parts = emptyParts();
17
+ parts.areas.push(rectArea("platform", "clay", "ky-bud-clay", platform));
18
+ // The ring's inside is the same clay. The band of sand lies just outside the tawara.
19
+ if (o.tawara)
20
+ parts.areas.push({ id: "jano-me", feature: "jano-me", cls: "ky-bud-sand", d: annulusD(c, c, outer, outer + fig.jano), evenodd: true });
21
+ // The 20 tawara are 4 tokudawara and 4 between each of them: one every 18 degrees, laid along the circle; a tokudawara stands a little way out.
22
+ const n = fig.tawara.count;
23
+ const step = 360 / n;
24
+ const bales = [];
25
+ const baleLength = ((2 * Math.PI * rb) / n) * 0.96;
26
+ const perQuarter = fig.tawara.between + 1;
27
+ const polys = [];
28
+ for (let i = 0; i < n; i += 1) {
29
+ const tokuda = i % perQuarter === 0;
30
+ const angle = i * step;
31
+ bales.push({ angle, tokuda });
32
+ const t = (angle * Math.PI) / 180;
33
+ const R = rb + (tokuda ? fig.bale.out : 0);
34
+ const px = c + R * Math.cos(t);
35
+ const py = c + R * Math.sin(t);
36
+ const tx = -Math.sin(t);
37
+ const ty = Math.cos(t);
38
+ const nx = Math.cos(t);
39
+ const ny = Math.sin(t);
40
+ const hl = baleLength / 2;
41
+ const hw = wb / 2;
42
+ const corners = [[-hl, -hw], [hl, -hw], [hl, hw], [-hl, hw]].map(([a, b]) => [rd(px + a * tx + b * nx), rd(py + a * ty + b * ny)]);
43
+ polys.push(`M${corners.map(([x, y]) => `${num(x)} ${num(y)}`).join("L")}Z`);
44
+ }
45
+ if (o.tawara) {
46
+ parts.areas.push({ id: "tawara", feature: "tawara", cls: "ky-bud-tawara", d: polys.join("") });
47
+ // The shikiri-sen, two white lines across the line between the wrestlers, 70 cm apart, 90 cm long and 6 cm wide.
48
+ const s = fig.shikiri;
49
+ for (const [k, dx] of [["east", 1], ["west", -1]]) {
50
+ const x = c + dx * (s.apart / 2 + s.width / 2);
51
+ parts.shapes.push(tapeBar(`shikiri-${k}`, "shikiri-sen", "ky-bud-white-fill", { x0: x - s.width / 2, y0: c - s.length / 2, x1: x + s.width / 2, y1: c + s.length / 2 }));
52
+ }
53
+ }
54
+ // The four tassels, in the colours of the quarters: green east, red south, white west, black north.
55
+ if (o.tassels) {
56
+ const at = 0.22;
57
+ parts.areas.push(discArea("tassel-east", "tassels", "ky-bud-tassel-e", side - at, c, 0.12), discArea("tassel-south", "tassels", "ky-bud-tassel-s", c, side - at, 0.12), discArea("tassel-west", "tassels", "ky-bud-tassel-w", at, c, 0.12), discArea("tassel-north", "tassels", "ky-bud-tassel-n", c, at, 0.12));
58
+ }
59
+ const reach = outer + fig.bale.out;
60
+ const ranges = { ring: { x0: c - reach - 0.3, y0: c - reach - 0.3, x1: c + reach + 0.3, y1: c + reach + 0.3 }, start: { x0: c - 1.2, y0: c - 0.9, x1: c + 1.2, y1: c + 0.9 } };
61
+ const marks = [
62
+ { id: "dohyo", x: side * 0.1, y: side * 0.12, side: "top" },
63
+ { id: "ring", x: c - 0.9, y: c - inner * 0.62, side: "top" },
64
+ { id: "tawara", x: c + rb * Math.cos((1.5 * step * Math.PI) / 180), y: c - rb * Math.sin((1.5 * step * Math.PI) / 180) * -1, side: "bottom" },
65
+ { id: "tokudawara", x: c + rb + fig.bale.out, y: c, side: "bottom" },
66
+ { id: "shikiri-sen", x: c - fig.shikiri.apart / 2 - fig.shikiri.width / 2, y: c + fig.shikiri.length / 2, side: "bottom" },
67
+ ];
68
+ if (o.tawara)
69
+ marks.push({ id: "jano-me", x: c - (outer + fig.jano / 2) * Math.SQRT1_2, y: c - (outer + fig.jano / 2) * Math.SQRT1_2, side: "top" });
70
+ return { parts, platform, extent: grow(platform, 0.5), centre: [c, c], ring: inner, outer, bales, baleLength, height: o.height, marks, ranges };
71
+ }
@@ -0,0 +1,55 @@
1
+ import type { Area, Shape } from "../fields.types.ts";
2
+ import type { MBox } from "./parts.ts";
3
+ /** A mat's size. */
4
+ export interface MatSize {
5
+ length: number;
6
+ width: number;
7
+ }
8
+ /** What a piece of floor is: a whole mat, half of one (a square), or a mat cut to fit. */
9
+ export type PieceKind = "full" | "half" | "cut";
10
+ /** One piece of the floor, a mat or part of one. */
11
+ export interface Piece extends MBox {
12
+ kind: PieceKind;
13
+ /** The axis of its long side: `x` or `y`. */
14
+ along: "x" | "y";
15
+ /** Which colour it is painted: 1 the first, 2 the second, 3 the third (a danger zone or a border). */
16
+ tone: 1 | 2 | 3;
17
+ }
18
+ /** The kind of a piece of a floor laid with mats of a size. */
19
+ export declare function kindOf(w: number, h: number, mat: MatSize, along: "x" | "y"): PieceKind;
20
+ /**
21
+ * Lay a rectangle with mats whose long sides run along `along`, in columns the width of a mat. With `stagger` every second column is set off by half a mat, so that no four
22
+ * corners meet (the traditional lay); without it the mats make a grid. The floor's ends and sides are cut to fit where its size is not a whole number of mats; a column that is
23
+ * set off begins and ends with half a mat.
24
+ */
25
+ export declare function lay(rect: MBox, mat: MatSize, along: "x" | "y", stagger: boolean): Piece[];
26
+ /** The part of a piece inside a box, and the parts of it outside, each a piece of its own (a mat cut where a colour changes). */
27
+ export declare function cutBy(piece: Piece, box: MBox, mat: MatSize): {
28
+ inside: Piece | undefined;
29
+ outside: Piece[];
30
+ };
31
+ /**
32
+ * Give every piece the tone of the zone it lies in: the first of `zones` that holds it, else `rest`. A mat that a boundary runs through is cut there, each part
33
+ * taking its own zone's tone.
34
+ */
35
+ export declare function tint(pieces: readonly Piece[], zones: readonly {
36
+ box: MBox;
37
+ tone: 1 | 2 | 3;
38
+ }[], rest: 1 | 2 | 3, mat: MatSize): Piece[];
39
+ /** The mats of a checker floor: the tone alternates from one mat to the next, as the squares of a chessboard do. */
40
+ export declare function checker(pieces: readonly Piece[], rect: MBox, mat: MatSize): Piece[];
41
+ /**
42
+ * How many points are a corner of four pieces or more: where four mats meet. A floor laid in the traditional way has none. Pieces that only touch along a side are not counted (a T is a point
43
+ * where one piece's side runs past the corners of two others, not a corner of three).
44
+ */
45
+ export declare function fourCorners(pieces: readonly Piece[]): number;
46
+ /** The floor's area in square metres. */
47
+ export declare const areaOf: (pieces: readonly Piece[]) => number;
48
+ /** The number of mats a floor holds, counted as the mats' own area: a half mat is a half, a cut piece the share of a mat it covers. */
49
+ export declare const matCount: (pieces: readonly Piece[], mat: MatSize) => number;
50
+ /** The painted areas of a floor of mats: one path for each tone, in the class the look gives it. */
51
+ export declare function tatamiAreas(id: string, pieces: readonly Piece[], classes: Readonly<Record<1 | 2 | 3, Area["cls"]>>, feature: Readonly<Record<1 | 2 | 3, string>>): Area[];
52
+ /** The seams: the outline of every piece, as one line. */
53
+ export declare function seams(id: string, pieces: readonly Piece[], width: number): Shape[];
54
+ /** The heri (畳縁), the cloth along the two long sides of a traditional mat, as filled strips inside the mat. Cut pieces and the thin ends of a floor have none. */
55
+ export declare function heri(id: string, pieces: readonly Piece[], width: number, mat: MatSize): Area[];