@johnmorrisdotca/kyogijo-stadiums 0.16.1 → 0.17.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 CHANGED
@@ -7,6 +7,16 @@ All notable changes to this project are written here. The format follows
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.17.0] - 2026-10-20
11
+
12
+ ### Added
13
+
14
+ - **Judging assignments** (`@johnmorrisdotca/kyogijo-stadiums/judging`: `drawJudging`, `judgingChart`, `judgingTable`, `judgingDrawing`, `mountJudgingTurn`, `judgingTurnMarkup`, `buildJudgingTurn`). Who judges where at a judo tournament: `{ style: "kata", mats: [{ label, officials: [{ seat, name, id }] }] }` gives one SVG for a tablet read live: for each of 1 to 6 mats the judo kata area with a badge at each of the five judges' seats (the seats are the plan's own, 1.25 m apart, the central judge at the centre), the name and the ID in a card in the seat where they fit, and beside the plan (landscape) or under it (portrait) a chart of seat, role, name and ID whose rows grow until every name (up to 120 characters) and ID (up to 64) is whole; a seat with nobody in it says so. A name fits a seat only at a type no smaller than 1.2 % of the picture's width; the cards of a mat share one size; `chart` is `auto` (drawn when a name does not fit, the plan first being given the room the chart would take), `always` or `never`; `mat` draws one mat alone (a tab); `focus: "bench"` shows the judges' bench close up so that the names are as large as they can be. In 3D the judges' table and chairs stand on the judo floor, a name card stands on the table in front of each judge and turns to face the eye, and the mat's name is on a sign over it (`focus: "bench"` fits and turns the view about the bench; the chart goes beside it). Light and dark, English and Japanese, a title and a description that name every seat, and `judgingTable` as a table. The numbering of the seats (1 to 5 from the left of the plan, the central judge 3), the cards, the chart and the heights in 3D are ours: no rule gives them. Data stays with the caller: nothing is fetched, stored or sent; every text is made safe and held to its length. Docs: [judging assignments](./docs/judging.md) and [a gallery](https://johnmorrisdotca.github.io/kyogijo-stadiums/docs/gallery-judging.html). Demo: <https://johnmorrisdotca.github.io/kyogijo-stadiums/judging.html> (a tab for each mat, editable names and IDs kept in its address). Shiai (a referee and judges by the IJF's refereeing positions) is the next release.
15
+
16
+ ### Changed
17
+
18
+ - `TurnModel` has an optional `lens`, a lens of the model's own used when the settings give none (a view of several benches at once has a longer one); the helpers `box` and `rect` moved from `multi.turn.ts` to `turn.kit.ts`; the 3D view's stylesheet carries the colours and rules of a judging assignment's cards (`--ky-ju-*`, `u-card*`, `u-sign*`, `u-bench*`); `UprightKind` has `judges` and `PartKind` has `card` and `sign`.
19
+
10
20
  ## [0.16.1] - 2026-10-20
11
21
 
12
22
  ### Added
package/README.md CHANGED
@@ -102,6 +102,7 @@ console.log(svg.startsWith("<svg"), svg.includes("USA 3, Canada 2"));
102
102
  - **Data on the surface.** Points, lines, arrows, text, heat maps, hexagons, zone charts and density contours over any surface, in its feet or a data provider's frame, cut to its outline, with a table for each layer; ready-made shot and pass charts. [Data layers](./docs/layers.md), [charts](./docs/charts.md).
103
103
  - **Small multiples and backgrounds.** `drawGrid` puts many drawings in one SVG, under a title and a subtitle, over a credit line, with a key they share, in a size for a social card, a square post or a phone; `background` lays a quiet pattern (felt, grass, wood, ice, paper, a grid, dots) or a photograph behind a drawing or a grid. [Layouts and backgrounds](./docs/layouts.md).
104
104
  - **Tournament layouts.** Several surfaces laid out together for one large event, to scale, on one floor, as one SVG: thirteen presets from table tennis, pickleball, curling, judo, karate, badminton and tennis, each with its rule and sources, whether it fits a hall, and a real stadium round it. [Tournament layouts](./docs/multi.md).
105
+ - **Judging assignments.** Who judges where. [Judging](./docs/judging.md).
105
106
  - **Replay.** Tracking data played on any surface: the players with their numbers, the ball and its trail, and the passes, shots and goals as they happen, with controls, the keyboard and a live region, steps for a reader who prefers reduced motion, and a pause when the page is hidden. [Replay](./docs/replay.md).
106
107
  - **A 3D view.** Any surface as a floor that turns by mouse, touch, wheel or keys, with four viewpoints (overhead, broadcast, end-on, low corner), and what the rulebook stands on it at its height (nets, baskets, rink boards and glass, goals, a ballpark's fence, the walls of padel and squash, a pool's blocks, a table tennis area's surrounds); and the venue round it (raked stands, roofs on request, a dojo's walls and gallery). Plain DOM and CSS. [The 3D view](./docs/turn.md).
107
108
  - **Renderers.** The same drawing as plain data (a scene), on a canvas (a browser's or a Node canvas), in a d3 selection, as a mark of an Observable Plot, as GeoJSON and as a PNG in Node, each an entry of its own that imports nothing from outside the package. [Renderers](./docs/renderers.md).
@@ -131,6 +132,7 @@ console.log(svg.startsWith("<svg"), svg.includes("USA 3, Canada 2"));
131
132
  | `./turn` | A surface as a 3D view that turns, with the net, basket, goal or posts standing on it. |
132
133
  | `./scene`, `./canvas`, `./d3`, `./plot`, `./geojson`, `./png` | The drawing as data; on a canvas; in a d3 selection; as an Observable Plot mark; as GeoJSON; as a PNG (the optional peer `@resvg/resvg-js`). |
133
134
  | `./multi` | `drawMulti`, `multiFit`: several surfaces laid out for one event, in a hall ([docs](./docs/multi.md), [demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/halls.html)). |
135
+ | `./judging` | `drawJudging`: who judges where ([docs](./docs/judging.md)). |
134
136
  | `./layouts` | `drawGrid` and `GRID_PRESETS`: small multiples with a title, a subtitle, a key and a credit line; the grounds behind a drawing or a grid. |
135
137
  | `./mount`, `./react`, `./element`, `./element/define` | Draw into a container; React props; the `<kyogijo-field>` element and its registration. |
136
138
 
@@ -366,12 +368,12 @@ console.log(items.map((item) => item.kind).join(), html.includes("preserve-3d"))
366
368
  // net,netposts,antennae false
367
369
  ```
368
370
 
369
- The flat drawing is the floor, turned by a drag, a swipe, the keys or four viewpoints (overhead, broadcast, end-on, low corner), and what the rulebook stands on the surface is built up on it at its height. Each part is a flat rectangle placed by one `matrix3d`, so there is no 3D context for a phone to lose; where 3D is not drawn the flat drawing stays. In the element it is `<kyogijo-field sport="nhl" turn turn-view="broadcast">`. [The 3D view](./docs/turn.md).
371
+ The flat drawing is the floor, turned by a drag, a swipe, the keys or four viewpoints, with what the rulebook stands on it at its height; each part is a flat rectangle placed by one `matrix3d`, so there is no 3D context for a phone to lose. In the element it is `<kyogijo-field sport="nhl" turn turn-view="broadcast">`. [The 3D view](./docs/turn.md).
370
372
 
371
373
  <p align="center">
372
374
  <picture>
373
375
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/turn-volleyball-desk-dark.webp">
374
- <img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/turn-volleyball-desk-light.webp" alt="A volleyball court from behind an end line under a spotlight, turned in the demo: the net at 2.43 metres on its posts with the red and white antennae, and under it the buttons for the four viewpoints, turning, raising, zooming and resetting." width="640">
376
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/turn-volleyball-desk-light.webp" alt="A volleyball court from behind an end line, turned in the demo: the net at 2.43 metres on its posts with the antennae, and the viewpoint buttons under it." width="640">
375
377
  </picture>
376
378
  <br><em>A volleyball net at its rulebook height, with its posts and antennae.</em>
377
379
  </p>
@@ -379,7 +381,7 @@ The flat drawing is the floor, turned by a drag, a swipe, the keys or four viewp
379
381
  <p align="center">
380
382
  <picture>
381
383
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/turn-rink-desk-dark.webp">
382
- <img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/turn-rink-desk-light.webp" alt="An ice rink from a low corner on an ice-blue background, turned in the demo: the boards with their glass round the ice, a goal at each end, and the score on the ice." width="640">
384
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/turn-rink-desk-light.webp" alt="An ice rink from a low corner: the boards and glass round the ice, a goal at each end, and the score on the ice." width="640">
383
385
  </picture>
384
386
  <br><em>A rink with its boards, glass and goals.</em>
385
387
  </p>
@@ -387,7 +389,7 @@ The flat drawing is the floor, turned by a drag, a swipe, the keys or four viewp
387
389
  <p align="center">
388
390
  <picture>
389
391
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/turn-court-desk-dark.webp">
390
- <img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/turn-court-desk-light.webp" alt="A basketball court from the main camera in a warm glow, turned in the demo: a basket on its backboard and padded stanchion at each end of the floor." width="640">
392
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/turn-court-desk-light.webp" alt="A basketball court from the main camera: a basket on its backboard and padded stanchion at each end." width="640">
391
393
  </picture>
392
394
  <br><em>A court with a basket at each end, the rim at 10 feet.</em>
393
395
  </p>
@@ -465,7 +467,7 @@ console.log(svg.includes("3 shots"), svg.includes("By outcome: goal 1, shot 1, b
465
467
  // true true
466
468
  ```
467
469
 
468
- Zoom to a part of the surface with `range`: the data, the heat and the labels follow the crop ([display ranges](./docs/ranges.md)).
470
+ Zoom to a part with `range`: the data follows the crop ([display ranges](./docs/ranges.md)).
469
471
 
470
472
  ```ts
471
473
  import { drawKyogijo } from "@johnmorrisdotca/kyogijo-stadiums";
@@ -475,7 +477,7 @@ console.log(zone.includes('data-range="offensive-zone"'), zone.includes("Shows o
475
477
  // true true
476
478
  ```
477
479
 
478
- Lines, arrows, text, heat maps, hexagons, zones and contours are in [data layers](./docs/layers.md); a shot chart, a shot map, a spray chart, a pass chart and a soccer shot map are one call each in [charts](./docs/charts.md). Pass networks, formations, lineups (a team's players placed by position code, as discs or jerseys), Voronoi cells, Delaunay triangles, hulls, flow fields and sonars are layers too: [team shape](./docs/team-shape.md). Every kind is drawn, with its code, in the [gallery](https://johnmorrisdotca.github.io/kyogijo-stadiums/docs/gallery.html).
480
+ Lines, arrows, text, heat maps, hexagons, zones and contours are in [data layers](./docs/layers.md); shot, spray and pass charts are one call each in [charts](./docs/charts.md); pass networks, formations, lineups, Voronoi cells and flow fields are layers too ([team shape](./docs/team-shape.md)).
479
481
 
480
482
  ### Small multiples on a card
481
483
 
@@ -539,7 +541,20 @@ console.log(layout.needs.length, layout.needs.width);
539
541
  <br><em>Without `sample`: the empty places an organiser fills.</em>
540
542
  </p>
541
543
 
542
- The [demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/halls.html) picks a preset, sets it in a hall, switches the stadium on and off, and turns it in 3D.
544
+ The [demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/halls.html) turns it in 3D.
545
+
546
+ ### Who judges where
547
+
548
+ `drawJudging` puts each judo mat's officials by seat in the picture and in a chart ([judging](./docs/judging.md)).
549
+
550
+ <p align="center">
551
+ <picture>
552
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/judging-3d-desk-dark.webp">
553
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/judging-3d-desk-light.webp" alt="A kata area's judges' table in 3D, with a name card before each judge." width="640">
554
+ </picture>
555
+ <br><em>Judges in 3D.</em>
556
+ </p>
557
+
543
558
 
544
559
  ### A table tennis area
545
560
 
@@ -0,0 +1,12 @@
1
+ export { drawJudging, judgingChart, judgingDrawing, judgingTable, judgingText } from "./judging.draw.ts";
2
+ export { JUDGING_HEIGHTS, JUDGING_PRESETS, buildJudgingTurn, judgingTurnMarkup, mountJudgingTurn } from "./judging.turn.ts";
3
+ export { JUDGING_LEAGUE, JUDGING_MATS_MAX, JUDGING_SEATS, JUDGING_SEAT_COUNT, JUDGING_STYLES } from "./judging.data.ts";
4
+ export { JUDGING_LIMITS, judgingPlan, judgingSetting, matLabel, seatOf, seatsOf } from "./judging.ts";
5
+ export { fitCard, fitChart, fitSeats, wrapText } from "./judging.fit.ts";
6
+ export { KYOGIJO_JUDGING_STYLE } from "./judging.style.ts";
7
+ export { JUDGING_WORDS } from "./judging.words.ts";
8
+ export type { JudgingWords } from "./judging.words.ts";
9
+ export type { JudgingAssignment, JudgingMat, JudgingOfficial, JudgingOptions, JudgingResolved, JudgingSeat, JudgingStyle } from "./judging.types.ts";
10
+ export type { JudgingDrawing } from "./judging.draw.ts";
11
+ export type { JudgingPlan, JudgingSetting } from "./judging.ts";
12
+ export type { CardAsk, CardFit } from "./judging.fit.ts";
@@ -0,0 +1,8 @@
1
+ // A judging assignment: the officials of each mat area of a judo tournament, by seat, with their names and IDs, in the seat where they fit and in a chart where they do not; in 2D and in 3D.
2
+ export { drawJudging, judgingChart, judgingDrawing, judgingTable, judgingText } from "./judging.draw.js";
3
+ export { JUDGING_HEIGHTS, JUDGING_PRESETS, buildJudgingTurn, judgingTurnMarkup, mountJudgingTurn } from "./judging.turn.js";
4
+ export { JUDGING_LEAGUE, JUDGING_MATS_MAX, JUDGING_SEATS, JUDGING_SEAT_COUNT, JUDGING_STYLES } from "./judging.data.js";
5
+ export { JUDGING_LIMITS, judgingPlan, judgingSetting, matLabel, seatOf, seatsOf } from "./judging.js";
6
+ export { fitCard, fitChart, fitSeats, wrapText } from "./judging.fit.js";
7
+ export { KYOGIJO_JUDGING_STYLE } from "./judging.style.js";
8
+ export { JUDGING_WORDS } from "./judging.words.js";
@@ -0,0 +1,21 @@
1
+ import type { CardFit } from "./judging.fit.ts";
2
+ /** The class names a card is drawn with. */
3
+ export interface CardClasses {
4
+ card: string;
5
+ name: string;
6
+ id: string;
7
+ badge: string;
8
+ badgeEmpty: string;
9
+ n: string;
10
+ }
11
+ /** The classes of the plan. */
12
+ export declare const PLAN_CARD: CardClasses;
13
+ /** The classes of the 3D view's planes. */
14
+ export declare const TURN_CARD: CardClasses;
15
+ /** The round badge of a seat's number, centred at (cx, cy); hollow when nobody is in the seat. */
16
+ export declare function badge(cls: CardClasses, cx: number, cy: number, r: number, n: number, assigned: boolean): string;
17
+ /**
18
+ * A card with a name and an ID, `w` by `h` with its top left at (x, y): the text centred in the space under the badge, which straddles the card's top edge, and the badge itself. `fit` is what `fitCard` made of the
19
+ * name and the ID for the space inside `pad` and under `r`.
20
+ */
21
+ export declare function card(cls: CardClasses, fit: CardFit, n: number, x: number, y: number, w: number, h: number, pad: number, r: number, round: number): string;
@@ -0,0 +1,34 @@
1
+ // The name card of a seat and the badge of its number, as markup: the same drawing in the plan (in the picture's units, `ky-ju-*` classes) and in the 3D view (in feet, `u-card*` classes), so that a seat reads the same
2
+ // in both. Nothing here decides whether a name fits: `judging.fit.ts` does, and a card is drawn only for a name that does.
3
+ import { el, esc } from "./format.js";
4
+ import { LINE_HEIGHT } from "./text.js";
5
+ /** The classes of the plan. */
6
+ export const PLAN_CARD = { card: "ky-ju-card", name: "ky-ju-card-name", id: "ky-ju-card-id", badge: "ky-ju-badge", badgeEmpty: "ky-ju-badge ky-ju-badge-empty", n: "ky-ju-badge-n" };
7
+ /** The classes of the 3D view's planes. */
8
+ export const TURN_CARD = { card: "u-card", name: "u-card-name", id: "u-card-id", badge: "u-card-badge", badgeEmpty: "u-card-badge u-card-badge-empty", n: "u-card-n" };
9
+ /** The round badge of a seat's number, centred at (cx, cy); hollow when nobody is in the seat. */
10
+ export function badge(cls, cx, cy, r, n, assigned) {
11
+ return el("circle", { class: assigned ? cls.badge : cls.badgeEmpty, cx, cy, r }) + el("text", { class: cls.n, x: cx, y: cy + r * 0.36, "font-size": r * 1.05 }, String(n));
12
+ }
13
+ /**
14
+ * A card with a name and an ID, `w` by `h` with its top left at (x, y): the text centred in the space under the badge, which straddles the card's top edge, and the badge itself. `fit` is what `fitCard` made of the
15
+ * name and the ID for the space inside `pad` and under `r`.
16
+ */
17
+ export function card(cls, fit, n, x, y, w, h, pad, r, round) {
18
+ const cx = x + w / 2;
19
+ const room = h - 2 * pad - r;
20
+ let out = el("rect", { class: cls.card, x, y, width: w, height: h, rx: round });
21
+ let cy = y + pad + r + Math.max(0, (room - fit.height) / 2);
22
+ for (const line of fit.name) {
23
+ out += el("text", { class: cls.name, x: cx, y: cy + fit.nameSize * 0.88, "font-size": fit.nameSize }, esc(line));
24
+ cy += fit.nameSize * LINE_HEIGHT;
25
+ }
26
+ if (fit.id.length > 0) {
27
+ cy += fit.name.length > 0 ? fit.nameSize * 0.28 : 0;
28
+ for (const line of fit.id) {
29
+ out += el("text", { class: cls.id, x: cx, y: cy + fit.idSize * 0.88, "font-size": fit.idSize }, esc(line));
30
+ cy += fit.idSize * LINE_HEIGHT;
31
+ }
32
+ }
33
+ return out + badge(cls, cx, y, r, n, true);
34
+ }
@@ -0,0 +1,55 @@
1
+ import { type BudoPlan, type MBox } from "./budo/plan.ts";
2
+ import type { JudgingMat, JudgingOptions, JudgingResolved, JudgingSeat, JudgingStyle } from "./judging.types.ts";
3
+ import type { Language } from "./types.ts";
4
+ /** The longest name, ID and mat label that are taken, in characters. */
5
+ export declare const JUDGING_LIMITS: Readonly<{
6
+ name: 120;
7
+ id: 64;
8
+ label: 40;
9
+ }>;
10
+ /** The seats of a style. @throws TypeError for a style that is not one. */
11
+ export declare function seatsOf(style: unknown): readonly JudgingSeat[];
12
+ /**
13
+ * The seat a name stands for: its number (`3`, `"3"`), its key (`"judge-3"`, `"Judge 3"`) or `"central"`.
14
+ * @throws RangeError for a seat the style does not have, naming the seats it has.
15
+ */
16
+ export declare function seatOf(style: JudgingStyle, seat: unknown): JudgingSeat;
17
+ /** The label of a mat: the caller's, held to its length, else the language's `Mat {n}`. */
18
+ export declare function matLabel(mat: JudgingMat, n: number, language: Language): string;
19
+ /** What `drawJudging` and the 3D view work from: the options held to what they may be, and every mat with all of its seats. */
20
+ export interface JudgingSetting {
21
+ style: JudgingStyle;
22
+ language: Language;
23
+ /** The mats to draw (all of them, or the one `mat` names), each with every seat of the style. */
24
+ mats: JudgingResolved[];
25
+ /** How many mats the assignment has altogether. */
26
+ total: number;
27
+ chart: "auto" | "always" | "never";
28
+ orientation: "landscape" | "portrait";
29
+ focus: "mat" | "bench";
30
+ width: number;
31
+ }
32
+ /**
33
+ * Hold the options to what they may be and put each official in a seat.
34
+ * @throws TypeError for an option of the wrong kind, RangeError for a number of mats outside 1 to 6, a mat that does not exist, a seat the style does not have, a seat named twice on one mat or a text that is too long.
35
+ */
36
+ export declare function judgingSetting(options: JudgingOptions): JudgingSetting;
37
+ /** Where the seats are on one mat's plan, in metres from the top left of the tatami, and the plan they are on. */
38
+ export interface JudgingPlan {
39
+ plan: BudoPlan;
40
+ /** The chair of each seat, in seat order, and the judges' table behind them. */
41
+ chairs: MBox[];
42
+ table: MBox;
43
+ /** The distance between two seats. */
44
+ pitch: number;
45
+ /** The whole picture of a mat (the free zone, the table, the seats, the coach's chair) and the judges' bench close up. */
46
+ extent: MBox;
47
+ bench: MBox;
48
+ }
49
+ /**
50
+ * The judges' bench close up: the seats and the table with a little round them and a few rows of mats in front, from the seats of one mat or of several side by side. `matTop` is where the mats start (the top of the first
51
+ * contest area), so that the picture shows how the bench sits at the mat's edge; `front` instead says how far in front of the table the picture reaches, in metres (the 3D view, which turns, wants as little of the floor as will do).
52
+ */
53
+ export declare function benchOf(chairs: readonly MBox[], table: MBox, pitch: number, matTop: number, front?: number): MBox;
54
+ /** The plan of one mat of a style, with its seats: the package's own judo area, so that the seats named are the seats drawn. */
55
+ export declare function judgingPlan(style: JudgingStyle): JudgingPlan;
@@ -0,0 +1,14 @@
1
+ import type { JudgingSeat, JudgingStyle } from "./judging.types.ts";
2
+ /** How many officials a style seats. */
3
+ export declare const JUDGING_SEAT_COUNT: Readonly<Record<JudgingStyle, number>>;
4
+ /** The seats of every style, in the order they are drawn. */
5
+ export declare const JUDGING_SEATS: Readonly<Record<JudgingStyle, readonly JudgingSeat[]>>;
6
+ /** The styles that can be asked for. */
7
+ export declare const JUDGING_STYLES: readonly JudgingStyle[];
8
+ /** The league each style is drawn in (`drawKyogijo`'s `league`), and the sources its seats are read from (keys of `LEAGUE_SOURCES`). */
9
+ export declare const JUDGING_LEAGUE: Readonly<Record<JudgingStyle, {
10
+ league: string;
11
+ sources: readonly string[];
12
+ }>>;
13
+ /** The most mat areas an assignment names: the plan's own limit for areas side by side. */
14
+ export declare const JUDGING_MATS_MAX = 6;
@@ -0,0 +1,24 @@
1
+ // The seats of each judging style, as the rulebook has them. A kata area is judged by five judges on the shomen side, 1.25 m apart, the central judge at the centre of the area, and no referee (IJF Sport and
2
+ // Organisation Rules of 21 January 2026, B1.8 and the judges' article; the Kata Commission's Tournament Handbook 2024). The rules do not number the judges: the numbers here are ours, from the left as the plan lies
3
+ // with the shomen at the top (so the central judge is 3), and are said so in the docs. The positions are the plan's own (`budoPlan`), so that what is drawn and what is named cannot differ.
4
+ import { IJF_KATA } from "./leagues/budo.js";
5
+ /** How many officials a style seats. */
6
+ export const JUDGING_SEAT_COUNT = Object.freeze({ kata: IJF_KATA.panel?.count ?? 5 });
7
+ /** The seats of a kata area: five judges, the middle one the central judge. */
8
+ function kata() {
9
+ const count = JUDGING_SEAT_COUNT.kata;
10
+ const middle = Math.ceil(count / 2);
11
+ return Array.from({ length: count }, (_, i) => {
12
+ const n = i + 1;
13
+ const central = n === middle;
14
+ return { id: `judge-${n}`, n, role: central ? "central" : "judge", label: { en: central ? `Judge ${n}, central` : `Judge ${n}`, ja: central ? `審判員${n}(中央)` : `審判員${n}` } };
15
+ });
16
+ }
17
+ /** The seats of every style, in the order they are drawn. */
18
+ export const JUDGING_SEATS = Object.freeze({ kata: Object.freeze(kata()) });
19
+ /** The styles that can be asked for. */
20
+ export const JUDGING_STYLES = Object.freeze(["kata"]);
21
+ /** The league each style is drawn in (`drawKyogijo`'s `league`), and the sources its seats are read from (keys of `LEAGUE_SOURCES`). */
22
+ export const JUDGING_LEAGUE = Object.freeze({ kata: { league: "ijf-kata", sources: ["ijf-sor", "ijf-kata-handbook", "ijf-kata-criteria"] } });
23
+ /** The most mat areas an assignment names: the plan's own limit for areas side by side. */
24
+ export const JUDGING_MATS_MAX = 6;
@@ -0,0 +1,66 @@
1
+ import { type JudgingSetting } from "./judging.ts";
2
+ import type { JudgingOptions } from "./judging.types.ts";
3
+ /** What a drawing of an assignment is, and what was worked out to make it. */
4
+ export interface JudgingDrawing {
5
+ svg: string;
6
+ /** The picture's width and height in its own units (its viewBox). */
7
+ width: number;
8
+ height: number;
9
+ /** Whether the chart is drawn. */
10
+ chart: boolean;
11
+ /** For every seat of every mat drawn: whether its name and ID are in the seat. A seat with nobody in it fits. */
12
+ cards: {
13
+ mat: number;
14
+ seat: string;
15
+ fits: boolean;
16
+ }[];
17
+ title: string;
18
+ description: string;
19
+ }
20
+ /** The title and the description of an assignment: each mat with each seat and who is in it. */
21
+ export declare function judgingText(setting: JudgingSetting, options: Pick<JudgingOptions, "title" | "description">, chart: boolean): {
22
+ title: string;
23
+ description: string;
24
+ };
25
+ /** The picture of an assignment and what was worked out to make it. `mode` `chart` is the chart alone (for beside a 3D view). */
26
+ export declare function judgingDrawing(options: JudgingOptions, mode?: "full" | "chart"): JudgingDrawing;
27
+ /**
28
+ * A judging assignment as one SVG string: for each mat, the judo area with a badge at each judge's seat and, where the name and ID fit in the seat, a card with both, and beside it the chart of seat, name and ID, whose rows
29
+ * grow to hold every name whole. `style` is the style the areas are judged in (`kata`: five judges); `mats` the mat areas, each with its `label` (`Mat 1`, `Mat 2` when missing) and its `officials`, each a `seat`, a `name`
30
+ * and an `id`. `mat` draws one mat alone (a tab); `chart` is `auto`, `always` or `never`; `focus: "bench"` shows the judges' bench close up so that the names in the seats are as large as they can be. Light and dark follow the
31
+ * page; `judgingTable` is the same thing as a table.
32
+ * @example
33
+ * ```ts
34
+ * import { drawJudging } from "@johnmorrisdotca/kyogijo-stadiums/judging";
35
+ * const svg = drawJudging({
36
+ * style: "kata",
37
+ * mats: [{ label: "Mat 1", officials: [{ seat: 1, name: "Aiko Mori", id: "J-1042" }, { seat: "central", name: "Daniel Okafor", id: "J-0377" }] }],
38
+ * });
39
+ * console.log(svg.startsWith("<svg"), svg.includes("Aiko Mori"), svg.includes("J-1042"));
40
+ * // true true true
41
+ * ```
42
+ */
43
+ export declare function drawJudging(options: JudgingOptions): string;
44
+ /**
45
+ * The chart of an assignment alone: for each mat its seats in order, each with its number, its role, its name and, under the name, its ID, in rows that grow so that no name or ID is cut. For a page that draws
46
+ * the 3D view of the mat and puts the chart beside it.
47
+ * @example
48
+ * ```ts
49
+ * import { judgingChart } from "@johnmorrisdotca/kyogijo-stadiums/judging";
50
+ * const svg = judgingChart({ style: "kata", mats: [{ officials: [{ seat: 1, name: "Aiko Mori", id: "J-1042" }] }] });
51
+ * console.log(svg.includes("ky-ju-chart-row"), svg.includes("Not assigned"));
52
+ * // true true
53
+ * ```
54
+ */
55
+ export declare function judgingChart(options: JudgingOptions): string;
56
+ /**
57
+ * The same thing as the picture, as a table: each mat, each seat and who sits in it, with the ID. For a reader who cannot see the drawing, and for a page that wants the data.
58
+ * @example
59
+ * ```ts
60
+ * import { judgingTable } from "@johnmorrisdotca/kyogijo-stadiums/judging";
61
+ * const table = judgingTable({ style: "kata", mats: [{ officials: [{ seat: 3, name: "Daniel Okafor", id: "J-0377" }] }] });
62
+ * console.log(table.startsWith("<table"), table.includes("Daniel Okafor"));
63
+ * // true true
64
+ * ```
65
+ */
66
+ export declare function judgingTable(options: JudgingOptions): string;