@johnmorrisdotca/kyogijo-stadiums 0.6.0 → 0.6.2

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,18 @@ All notable changes to this project are written here. The format follows
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.2] - 2026-10-14
11
+
12
+ ### Fixed
13
+
14
+ - **A round mark turns with its team's name.** Where a zone's words are turned (along the ends of a wide pitch, and in a tall drawing, where the surface is turned a quarter and the words are turned back) the flag in its circle was turned back to stay upright, so on a wide pitch the flag stood upright beside a name written sideways, and in a tall drawing the flag lay a quarter turn over while the name read upright. The mark's group now lies in the banner's group and takes its turn: the same angle, the same way, as the name, so a flag and its name read as one unit (sideways along the ends of a wide pitch, the right way up in a tall drawing). The clip, the ring, the hairline and the initials of a team with no picture turn with the group, and the clip path's id (`<id>-mk-home-c`, `<id>-mk-away-c`) is unchanged. A flag in a zone that is not turned is as before, and so are the badge and the strip of a compact card, which are not turned. `RoundMark.upright` stays in `mark.ts`, but the score no longer passes it. A test reads the turn of the mark and of the name in every orientation, with logos and with initials, and holds the two equal.
15
+
16
+ ## [0.6.1] - 2026-10-14
17
+
18
+ ### Fixed
19
+
20
+ - **The 3D view zoomed in and out as it turned.** The camera fitted the scene to its box for each angle on its own, so every angle fitted but the scale changed with the yaw: a long floor was drawn up to 1.8 times as large (overhead; 1.5 times at a tip of 30 degrees and 1.15 to 1.65 times from 60 to 80) at the angle where it is seen end-on as side-on, and the field grew and shrank under a drag or the auto-turn. `fit` now fits the scene at every angle of the circle at the view's pitch (so it takes the pitch and not the yaw) and takes the one scale and centre that hold them all: turning never changes the zoom or sways the scene, and no angle is cut off. The floor is the size it had at its tightest angle (down to 0.55 of its old size where it had room). Tipping the view still rescales it, since the pitch changes the extent; zoom is laid on top of the fit as before. `src/turn.camera.test.ts` holds the scale constant over every yaw, pitch and container, for every sport's scene, and measures every degree of the turn against the box.
21
+
10
22
  ## [0.6.0] - 2026-10-14
11
23
 
12
24
  Six budō sports and a dojo builder (39 sports in all), the situation of a game, lineups, zones, a pointer and goal mouths, the arenas of the NHL, the NBA, the WNBA and the PWHL (330 places on the atlas), and Dougu Sports 0.2.0 for an icon on every sport. The 3D view's keys, buttons and drag now turn the surface the same way.
@@ -152,7 +164,7 @@ Previously `@johnmorrisdotca/kyogijo`; renamed in 0.4.0 to `@johnmorrisdotca/kyo
152
164
  ### Added
153
165
 
154
166
  - **`roadToFinal`: the road to a tournament's final** (`@johnmorrisdotca/kyogijo-stadiums/charts`, [docs/charts.md](./docs/charts.md#the-road-to-the-final)). The two finalists as large round marks with their names; down the middle the rounds, the last at the top, on a dotted road; on each finalist's side its matches, each as the round mark of both teams with the score between them in large type and FT, AET or the shoot-out under it; and the group stage as a row of small marks, three-letter codes and scores at the foot. A finalist is a `Team` with a `path` of `{ round, opponent, score, decidedBy, pens, homeFirst, note }` and an optional `group`; a round is a key with words in English and Japanese (`round-of-32` to `semi-final`) or any key named in `labels`, so a play-off series (`4-2`) draws as well as a knockout. `final` writes the final's score between the large marks and quiets the loser; `winner` makes the side that won each match brighter; `codes`, `endnote`, `groupLabel`, `theme`, `language` and `style: "none"` as in the other drawings. Four sizes: `card` (1080 by 1350), `square` (1080 by 1080), `wide` (1200 by 675, the finalists at the two ends) and `phone` (390 wide, as tall as the rounds need), listed in `ROAD_SIZES`. It gives back a chart's shape without layers: `options`, `title`, `summary`, `totals` (each finalist's record, the rounds, the champion), `draw(extra)` and `table()`, a row for every match. The SVG is `role="img"` with a title and a description, reads in the scene and every renderer, goes in a `drawGrid`, and has ids of its own; a team with no flag or logo is drawn as its initials on its colour. New: `KYOGIJO_ROAD_STYLE`, the types `RoadChart`, `RoadFinalist`, `RoadMatch`, `RoadGroupMatch`, `RoadFinal`, `RoadOptions`, `RoadScore`, `RoadDecided`, `RoadSize`, `RoadSizeInfo`, `RoadRecord` and `RoadTotals`, and the `road` words of `KYOGIJO_STRINGS` (`en` and `ja`; a custom set of words must give them).
155
- - **A round team mark in a score** ([docs/score.md](./docs/score.md#round-marks)). `mark: "round"` on a team, or `markShape: "round"` on the score, draws its flag or logo clipped to a circle with a thin ring, on each side of the surface (the end zones of a pitch, the halves of a court), in the badge and in the strip of a compact card; where the words are turned (along the ends of a wide pitch, in a tall drawing) the mark is turned back and stays upright. A round mark with no logo is the team's initials on its colour; `markFit: "contain"` shows a crest whole on a plain disc. Off by default, and a drawing without it is the same text as before. New: `Team.mark`, `Team.markFit`, `ScoreOptions.markShape`, the type `TeamMark` and `KYOGIJO_MARK_STYLE` (`--ky-mark-ring`, `--ky-mark-edge`, `--ky-mark-plain`); a drawing writes the rules in only when it has a round mark.
167
+ - **A round team mark in a score** ([docs/score.md](./docs/score.md#round-marks)). `mark: "round"` on a team, or `markShape: "round"` on the score, draws its flag or logo clipped to a circle with a thin ring, on each side of the surface (the end zones of a pitch, the halves of a court), in the badge and in the strip of a compact card; where the words are turned (along the ends of a wide pitch, in a tall drawing) the mark turns with them (0.6.1 and before turned it back to stay upright, which left a flag standing beside a name that lay sideways, and sideways in a tall drawing). A round mark with no logo is the team's initials on its colour; `markFit: "contain"` shows a crest whole on a plain disc. Off by default, and a drawing without it is the same text as before. New: `Team.mark`, `Team.markFit`, `ScoreOptions.markShape`, the type `TeamMark` and `KYOGIJO_MARK_STYLE` (`--ky-mark-ring`, `--ky-mark-edge`, `--ky-mark-plain`); a drawing writes the rules in only when it has a round mark.
156
168
  - The demo has a "Road to the final" section with made-up nations (sixteen that do not exist, each with a simple flag drawn in code in `demo/road-data.js`), a play-off series, and the round mark on a pitch, in English and Japanese; the docs gallery has the same drawings, each made from the code beside it.
157
169
 
158
170
  ### Changed
package/dist/mark.d.ts CHANGED
@@ -26,7 +26,7 @@ export interface RoundMark {
26
26
  title?: string;
27
27
  /** The ring's width: nine hundredths of the radius by default. */
28
28
  ring?: number;
29
- /** The mark is set in a group that is turned by this many degrees, so that it is turned back and stays upright. */
29
+ /** The mark's group is turned back by this many degrees: pass the turn of the group the mark is in to hold it upright there. The score does not use it: its mark turns with the zone's words. */
30
30
  upright?: number;
31
31
  /** A class added to the mark's group (the road chart's `ky-r-dim` for a team that did not advance). */
32
32
  className?: string;
package/dist/score.js CHANGED
@@ -14,9 +14,10 @@ export function bannerMarkup(zone, team, o) {
14
14
  inner += el("rect", { class: `ky-panel ky-panel-${team.side}`, x: -w / 2, y: -h / 2, width: w, height: h, rx: Math.min(h * 0.16, 8) });
15
15
  const ink = `ky-${o.onColour || zone.panel ? "ink" : "paint"}-${team.side}`;
16
16
  const logo = team.logo;
17
- // A round mark is a picture even where the team has no logo (its initials on its colour), and stays upright in a zone that is turned.
17
+ // A round mark is a picture even where the team has no logo (its initials on its colour). It sits in the banner's group, so where the zone is turned it turns with the
18
+ // team's name, by the same angle, and the flag and the words read as one.
18
19
  const mark = (size, x, y) => team.round === true
19
- ? roundMarkup({ id: `${o.id ?? "ky"}-mk-${team.role}`, cx: x + size / 2, cy: y + size / 2, r: size / 2, href: logo, fit: team.fit, initials: team.abbr, color: team.color, ink: team.ink, upright: rot })
20
+ ? roundMarkup({ id: `${o.id ?? "ky"}-mk-${team.role}`, cx: x + size / 2, cy: y + size / 2, r: size / 2, href: logo, fit: team.fit, initials: team.abbr, color: team.color, ink: team.ink })
20
21
  : el("image", { class: "ky-logo", href: logo, x, y, width: size, height: size, preserveAspectRatio: "xMidYMid meet" });
21
22
  const pictured = logo !== undefined || team.round === true;
22
23
  const row = pictured && w / h >= 2.6;
@@ -94,8 +94,10 @@ export declare function covered(b: Box3, view: TurnView, at: Placement, distance
94
94
  y1: number;
95
95
  };
96
96
  /**
97
- * The placement that fits the scene in a container of width `w` and height `h` pixels, centred, with `pad` of its width and height left round it. The
98
- * perspective bends the sum, so it is found by a few rounds of scaling and re-centring; six is plenty.
97
+ * The placement that fits the scene in a container of width `w` and height `h` pixels, centred, with `pad` of its width and height left round it. It fits the scene at every turn about the
98
+ * vertical at the view's pitch, not at the view's own turn (its `yaw` is not used): the scale and the place are one for the whole circle, so that turning never zooms in and out or sways, and
99
+ * nothing is cut off at any angle. (A turn used to fit each angle alone, so a long floor was drawn up to 1.8 times as large side-on as end-on.) Tipping the view still rescales it, since the
100
+ * extent of the scene on the screen changes with the pitch. The perspective bends the sum, so it is found by a few rounds of scaling and re-centring; six is plenty.
99
101
  */
100
102
  export declare function fit(b: Box3, view: TurnView, w: number, h: number, distance: number, pad?: number): Placement;
101
103
  /** A 4 by 4 matrix, as CSS writes it in `matrix3d`: sixteen numbers, a column after a column. */
@@ -121,24 +121,54 @@ export function covered(b, view, at, distance) {
121
121
  const shown = corners(b).map((c) => project(c, view, at, distance));
122
122
  return { x0: Math.min(...shown.map((p) => p[0])), x1: Math.max(...shown.map((p) => p[0])), y0: Math.min(...shown.map((p) => p[1])), y1: Math.max(...shown.map((p) => p[1])) };
123
123
  }
124
+ /** The turns the fit looks at: the whole circle in steps of five degrees. The pad round the scene is many times the little the extent can grow between two of them (a tenth of a percent). */
125
+ const FIT_YAWS = Array.from({ length: 72 }, (_, i) => i * 5);
126
+ /** The rectangle on the screen that a box covers over every turn of the circle, at this pitch. */
127
+ function coveredAround(b, pitch, at, distance) {
128
+ const all = corners(b);
129
+ let x0 = Infinity;
130
+ let y0 = Infinity;
131
+ let x1 = -Infinity;
132
+ let y1 = -Infinity;
133
+ for (const yaw of FIT_YAWS) {
134
+ for (const c of all) {
135
+ const [x, y] = project(c, { yaw, pitch, zoom: 1 }, at, distance);
136
+ if (x < x0)
137
+ x0 = x;
138
+ if (x > x1)
139
+ x1 = x;
140
+ if (y < y0)
141
+ y0 = y;
142
+ if (y > y1)
143
+ y1 = y;
144
+ }
145
+ }
146
+ return { x0, y0, x1, y1 };
147
+ }
148
+ let lastFit;
124
149
  /**
125
- * The placement that fits the scene in a container of width `w` and height `h` pixels, centred, with `pad` of its width and height left round it. The
126
- * perspective bends the sum, so it is found by a few rounds of scaling and re-centring; six is plenty.
150
+ * The placement that fits the scene in a container of width `w` and height `h` pixels, centred, with `pad` of its width and height left round it. It fits the scene at every turn about the
151
+ * vertical at the view's pitch, not at the view's own turn (its `yaw` is not used): the scale and the place are one for the whole circle, so that turning never zooms in and out or sways, and
152
+ * nothing is cut off at any angle. (A turn used to fit each angle alone, so a long floor was drawn up to 1.8 times as large side-on as end-on.) Tipping the view still rescales it, since the
153
+ * extent of the scene on the screen changes with the pitch. The perspective bends the sum, so it is found by a few rounds of scaling and re-centring; six is plenty.
127
154
  */
128
155
  export function fit(b, view, w, h, distance, pad = 0.03) {
156
+ const key = `${b.x0},${b.y0},${b.x1},${b.y1},${b.z0},${b.z1}|${view.pitch}|${w}|${h}|${distance}|${pad}`;
157
+ if (lastFit?.key === key)
158
+ return { ...lastFit.at };
129
159
  const at = { k: 0.5, tx: 0, ty: 0 };
130
- const flat = { ...view, zoom: 1 };
131
160
  for (let i = 0; i < 8; i += 1) {
132
- const r = covered(b, flat, at, distance);
161
+ const r = coveredAround(b, view.pitch, at, distance);
133
162
  const cw = r.x1 - r.x0;
134
163
  const ch = r.y1 - r.y0;
135
164
  if (cw <= 0 || ch <= 0)
136
165
  break;
137
166
  at.k *= Math.min((w * (1 - 2 * pad)) / cw, (h * (1 - 2 * pad)) / ch);
138
- const c = covered(b, flat, at, distance);
167
+ const c = coveredAround(b, view.pitch, at, distance);
139
168
  at.tx -= (c.x0 + c.x1) / 2;
140
169
  at.ty -= (c.y0 + c.y1) / 2;
141
170
  }
171
+ lastFit = { key, at: { ...at } };
142
172
  return at;
143
173
  }
144
174
  /** Two matrices as one: `b` first, then `a`. */
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The package's version: the same as package.json's, and a test holds them to each other. */
2
- export declare const VERSION = "0.6.0";
2
+ export declare const VERSION = "0.6.2";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The package's version: the same as package.json's, and a test holds them to each other. */
2
- export const VERSION = "0.6.0";
2
+ export const VERSION = "0.6.2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnmorrisdotca/kyogijo-stadiums",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Kyogijo Stadiums 競技場, 39 sports' surfaces as SVG, drawn to scale from the rulebooks: NFL, CFL and IFAF football fields, an NHL rink, an NBA court, MLB ballparks, soccer pitches, curling sheets, athletics stadiums, speed skating, short track and figure skating rinks, swimming pools, water polo fields, diving wells, artistic swimming areas, and the courts and fields of tennis, volleyball (indoor, beach and sitting), badminton, pickleball, table tennis, padel, squash, racquetball, sepak takraw, teqball, roundnet, netball, lacrosse, rugby, Australian football, Gaelic games, cricket, field hockey and handball, and the budo of judo, karate, kendo, sumo and kyudo with a dojo of tatami laid as real units, with each team's name and logo on its side and the score in the middle, stands and a crowd, a pageant of flags, and a 3D view. Light and dark, accessible, a compact badge for lists, a helper for React and a custom element. Zero dependencies.",
5
5
  "keywords": [
6
6
  "sports",