@johnmorrisdotca/kyogijo-stadiums 0.35.0 → 0.36.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,13 @@ All notable changes to this project are written here. The format follows
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.36.0] - 2026-10-20
11
+
12
+ ### Added
13
+
14
+ - **A diamond mowing pattern** (`stripePattern: "diamonds"`, `菱形` in the demo): the crosshatch of a ballpark's outfield, as in a photograph of T-Mobile Park. On a ballpark it is squares of 30 feet with their sides along the foul lines, from home plate out, lighter and darker alternately (two passes of 45-degree stripes crossing); on a football field and a soccer pitch it is squares of a band turned 45 degrees, one of them centred on the turf so that both ends are mown alike, whatever the size. It is cut to the fair ground or to the turf by a clip path. The infield grass stays one tone, and a separate infield pattern is not offered: the pattern is the outfield's. [Theming](./docs/theming.md), the gallery's mowing sections.
15
+ - **The demo's Mowing choice is for every grass sport**: a football field and a ballpark have it too (it was the soccer pitch's alone), with Diamonds among the choices; the soccer section of the home page shows the diamonds as one more card.
16
+
10
17
  ## [0.35.0] - 2026-10-20
11
18
 
12
19
  ### Added
package/dist/draw.js CHANGED
@@ -222,7 +222,7 @@ const safeLength = (value) => {
222
222
  return Number.isFinite(value) && value > 0 ? num(value) : undefined;
223
223
  return typeof value === "string" && /^\d+(?:\.\d+)?(?:px|%|em|rem|vw|vh)?$/.test(value.trim()) ? value.trim() : undefined;
224
224
  };
225
- const PATTERNS = ["bands", "wide", "narrow", "lengthwise", "checks", "diagonal", "circles", "rings", "fan", "none"];
225
+ const PATTERNS = ["bands", "wide", "narrow", "lengthwise", "checks", "diagonal", "diamonds", "circles", "rings", "fan", "none"];
226
226
  const GOALS = ["box", "line", "strip", "none"];
227
227
  /** The pitch the options ask for: a `pitch` of the caller's, else the size a `ground` publishes, else none (the international pitch). */
228
228
  export function pitchOf(o) {
package/dist/mow.d.ts CHANGED
@@ -18,7 +18,8 @@ export declare const oddCells: (length: number, cell: number) => number;
18
18
  /**
19
19
  * The lighter tone of a pattern on a rectangle of turf. `band` is the width of the usual stripe, along x; `wide` and `narrow` are twice and half of it.
20
20
  * `bands` run across the turf (side by side along x), `lengthwise` along it, `checks` are squares of about `band`, `diagonal` are 45-degree stripes
21
- * `band` wide across, and `circles` are rings `band` wide about the middle. A pattern that is not one of these (`none`, `rings`, `fan`) has no tone here.
21
+ * `band` wide across, `diamonds` are the squares of `checks` turned 45 degrees (the crosshatch of a ballpark's outfield: two passes of 45-degree stripes, `band` wide
22
+ * across, crossing), laid so that a diamond is centred on the turf and both ends are mown alike, and `circles` are rings `band` wide about the middle. A pattern that is not one of these (`none`, `rings`, `fan`) has no tone here.
22
23
  */
23
24
  export declare function mowTurf(pattern: StripePattern, turf: Turf, band: number): Mown;
24
25
  /** A ballpark's fair ground, as the field of its stripes: how far it reaches either side of the line to centre field and how far out. */
@@ -30,7 +31,8 @@ export interface Park {
30
31
  /**
31
32
  * The lighter tone of a pattern on a ballpark: coordinates are the field's screen frame (x across, y minus the distance out from home plate). `band` is
32
33
  * 30 feet, the width of a stripe of the usual rings. `bands` run across the field at fixed distances out from home, `lengthwise` along the line to centre
33
- * field, `checks` are squares, `diagonal` run at 45 degrees, `fan` is wedges out from home plate, 6 degrees each. `rings` and `circles` are the park's own
34
+ * field, `checks` are squares, `diagonal` run at 45 degrees, `diamonds` are squares of `band` with their sides along the foul lines (two passes of stripes parallel to the
35
+ * lines, crossing, from home plate out), `fan` is wedges out from home plate, 6 degrees each. `rings` and `circles` are the park's own
34
36
  * arcs about home plate (drawn by the field itself) and `none` is plain, so they have no tone here. The path runs past the fence and must be cut to the fair ground.
35
37
  */
36
38
  export declare function mowPark(pattern: StripePattern, park: Park, band?: number): Mown;
package/dist/mow.js CHANGED
@@ -5,6 +5,10 @@ import { num } from "./format.js";
5
5
  const NONE = { path: "", clip: false, evenodd: false };
6
6
  /** An odd whole number of cells nearest to a length over a cell: odd, so that both ends of the turf are mown alike. */
7
7
  export const oddCells = (length, cell) => Math.max(3, 2 * Math.round((length / cell - 1) / 2) + 1);
8
+ /** A convex polygon from its corners, as a closed subpath. */
9
+ const poly = (corners) => `M${corners.map(([px, py]) => `${num(px)} ${num(py)}`).join("L")}z`;
10
+ /** The corners of the square of a lattice turned a quarter of a right angle: the cell between `a0` and `a1` along the first axis `(ex, ey)` and `b0` and `b1` along the second, from the origin `(ox, oy)`. */
11
+ const lattice = (ox, oy, ex, ey, fx, fy, a0, a1, b0, b1) => [[a0, b0], [a1, b0], [a1, b1], [a0, b1]].map(([a, b]) => [ox + a * ex + b * fx, oy + a * ey + b * fy]);
8
12
  const box = (x, y, w, h) => `M${num(x)} ${num(y)}h${num(w)}v${num(h)}h${num(-w)}z`;
9
13
  /** An annulus between two radii about a centre, as two circles of one path. */
10
14
  const ring = (cx, cy, inner, outer) => {
@@ -14,7 +18,8 @@ const ring = (cx, cy, inner, outer) => {
14
18
  /**
15
19
  * The lighter tone of a pattern on a rectangle of turf. `band` is the width of the usual stripe, along x; `wide` and `narrow` are twice and half of it.
16
20
  * `bands` run across the turf (side by side along x), `lengthwise` along it, `checks` are squares of about `band`, `diagonal` are 45-degree stripes
17
- * `band` wide across, and `circles` are rings `band` wide about the middle. A pattern that is not one of these (`none`, `rings`, `fan`) has no tone here.
21
+ * `band` wide across, `diamonds` are the squares of `checks` turned 45 degrees (the crosshatch of a ballpark's outfield: two passes of 45-degree stripes, `band` wide
22
+ * across, crossing), laid so that a diamond is centred on the turf and both ends are mown alike, and `circles` are rings `band` wide about the middle. A pattern that is not one of these (`none`, `rings`, `fan`) has no tone here.
18
23
  */
19
24
  export function mowTurf(pattern, turf, band) {
20
25
  const { x, y, w, h } = turf;
@@ -49,6 +54,26 @@ export function mowTurf(pattern, turf, band) {
49
54
  parts.push(`M${num(a)} ${num(y + h)}L${num(a + h)} ${num(y)}L${num(a + h + across)} ${num(y)}L${num(a + across)} ${num(y + h)}z`);
50
55
  return { path: parts.join(""), clip: true, evenodd: false };
51
56
  }
57
+ case "diamonds": {
58
+ // A lattice of squares of side `band` turned 45 degrees, one square centred on the turf: both ends of the turf then have the same tone, whatever its size.
59
+ const cx = x + w / 2;
60
+ const cy = y + h / 2;
61
+ const r = Math.SQRT1_2;
62
+ const reach = Math.ceil((Math.hypot(w, h) / 2) / band) + 1;
63
+ for (let i = -reach; i <= reach; i += 1) {
64
+ for (let j = -reach; j <= reach; j += 1) {
65
+ if (Math.abs(i + j) % 2 !== 1)
66
+ continue;
67
+ const corners = lattice(cx, cy, r, r, -r, r, (i - 0.5) * band, (i + 0.5) * band, (j - 0.5) * band, (j + 0.5) * band);
68
+ const xs = corners.map((c) => c[0]);
69
+ const ys = corners.map((c) => c[1]);
70
+ if (Math.max(...xs) <= x || Math.min(...xs) >= x + w || Math.max(...ys) <= y || Math.min(...ys) >= y + h)
71
+ continue;
72
+ parts.push(poly(corners));
73
+ }
74
+ }
75
+ return { path: parts.join(""), clip: true, evenodd: false };
76
+ }
52
77
  case "circles": {
53
78
  const cx = x + w / 2;
54
79
  const cy = y + h / 2;
@@ -64,7 +89,8 @@ export function mowTurf(pattern, turf, band) {
64
89
  /**
65
90
  * The lighter tone of a pattern on a ballpark: coordinates are the field's screen frame (x across, y minus the distance out from home plate). `band` is
66
91
  * 30 feet, the width of a stripe of the usual rings. `bands` run across the field at fixed distances out from home, `lengthwise` along the line to centre
67
- * field, `checks` are squares, `diagonal` run at 45 degrees, `fan` is wedges out from home plate, 6 degrees each. `rings` and `circles` are the park's own
92
+ * field, `checks` are squares, `diagonal` run at 45 degrees, `diamonds` are squares of `band` with their sides along the foul lines (two passes of stripes parallel to the
93
+ * lines, crossing, from home plate out), `fan` is wedges out from home plate, 6 degrees each. `rings` and `circles` are the park's own
68
94
  * arcs about home plate (drawn by the field itself) and `none` is plain, so they have no tone here. The path runs past the fence and must be cut to the fair ground.
69
95
  */
70
96
  export function mowPark(pattern, park, band = 30) {
@@ -96,6 +122,24 @@ export function mowPark(pattern, park, band = 30) {
96
122
  parts.push(`M${num(a)} 0L${num(a + reach)} ${num(-reach)}L${num(a + reach + across)} ${num(-reach)}L${num(a + across)} 0z`);
97
123
  return { path: parts.join(""), clip: true, evenodd: false };
98
124
  }
125
+ case "diamonds": {
126
+ // The foul lines are the axes: a cell is `band` along each, from home plate, and the cells either side of a corner alternate.
127
+ const r = Math.SQRT1_2;
128
+ const last = Math.ceil((reach * Math.SQRT2) / band) + 1;
129
+ for (let i = 0; i < last; i += 1) {
130
+ for (let j = 0; j < last; j += 1) {
131
+ if ((i + j) % 2 !== 1)
132
+ continue;
133
+ const corners = lattice(0, 0, r, -r, -r, -r, i * band, (i + 1) * band, j * band, (j + 1) * band);
134
+ const xs = corners.map((c) => c[0]);
135
+ const ys = corners.map((c) => c[1]);
136
+ if (Math.max(...xs) < umin - band || Math.min(...xs) > umax + band || Math.min(...ys) > band || Math.max(...ys) < -reach)
137
+ continue;
138
+ parts.push(poly(corners));
139
+ }
140
+ }
141
+ return { path: parts.join(""), clip: true, evenodd: false };
142
+ }
99
143
  case "fan": {
100
144
  const step = (6 * Math.PI) / 180;
101
145
  const R = reach * 1.5;
@@ -13,10 +13,10 @@ const BANDS = { bands: 19, wide: 9, narrow: 39 };
13
13
  function stripes(pattern, L, W, id) {
14
14
  if (pattern === "none")
15
15
  return { path: "", defs: "" };
16
- if (pattern === "circles") {
17
- // Rings about the middle, as wide as a usual band, cut to the pitch.
18
- const mown = mowTurf("circles", { x: 0, y: 0, w: L, h: W }, L / BANDS.bands);
19
- return { path: mown.path, defs: `<clipPath id="${id}-turf"><rect x="0" y="0" width="${n(L)}" height="${n(W)}"/></clipPath>`, evenodd: true };
16
+ if (pattern === "circles" || pattern === "diamonds") {
17
+ // Rings about the middle, or squares turned 45 degrees about it, as wide as a usual band, cut to the pitch.
18
+ const mown = mowTurf(pattern, { x: 0, y: 0, w: L, h: W }, L / BANDS.bands);
19
+ return { path: mown.path, defs: `<clipPath id="${id}-turf"><rect x="0" y="0" width="${n(L)}" height="${n(W)}"/></clipPath>`, evenodd: mown.evenodd };
20
20
  }
21
21
  const rect = (x, y, w, h) => `M${n(x)} ${n(y)}h${n(w)}v${n(h)}h${n(-w)}z`;
22
22
  const parts = [];
package/dist/types.d.ts CHANGED
@@ -73,11 +73,11 @@ export interface Placement {
73
73
  /** How much of the surface's markings are drawn. `simple` leaves out the small ones, such as hash marks. */
74
74
  export type Detail = "full" | "simple";
75
75
  /**
76
- * How grass is mown: bands across the surface (the usual), wide ones or narrow ones, along it, in checks, on the diagonal, in rings round the
76
+ * How grass is mown: bands across the surface (the usual), wide ones or narrow ones, along it, in checks, on the diagonal, in diamonds (the checks turned 45 degrees: a ballpark outfield's crosshatch, its sides along the foul lines), in rings round the
77
77
  * middle (`circles`), or `none`. A ballpark is mown in `rings` by default (arcs about home plate) and may also be mown in a `fan` (wedges out from
78
78
  * home plate). A football field and a ballpark take the same options as a soccer pitch; a surface that is not grass ignores them.
79
79
  */
80
- export type StripePattern = "bands" | "wide" | "narrow" | "lengthwise" | "checks" | "diagonal" | "circles" | "rings" | "fan" | "none";
80
+ export type StripePattern = "bands" | "wide" | "narrow" | "lengthwise" | "checks" | "diagonal" | "diamonds" | "circles" | "rings" | "fan" | "none";
81
81
  /**
82
82
  * How a goal is drawn, seen from above. A soccer goal: the net as a box behind the goal line, a line on it, a solid strip, or not at all.
83
83
  * A hockey net: the same four (`box` is the open frame behind the goal line, `strip` a filled net). A basketball basket: `box` is the backboard,
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.35.0";
2
+ export declare const VERSION = "0.36.0";
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.35.0";
2
+ export const VERSION = "0.36.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnmorrisdotca/kyogijo-stadiums",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "description": "Kyogijo Stadiums 競技場, 40 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, the MMA cage, 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",