@johnmorrisdotca/kyogijo-stadiums 0.5.0 → 0.6.1

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 +64 -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 +16 -3
  179. package/dist/turn.camera.js +53 -7
  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
@@ -5,8 +5,8 @@ export type { TurnModel, TurnSettings } from "./turn.model.ts";
5
5
  export { layoutOf, planeMarkup, planeMatrix, turnMarkup, turnMarkupOf } from "./turn.markup.ts";
6
6
  export type { Layout } from "./turn.markup.ts";
7
7
  export { KYOGIJO_TURN_STYLE } from "./turn.style.ts";
8
- export { CURLING_PRESETS, LONG_PRESETS, NET_PRESETS, PITCH_MAX, STEP, TURN_DEFAULT, TURN_PRESETS, TURN_PRESET_NAMES, ZOOM_MAX, ZOOM_MIN, cameraMatrix, clampView, depthOf, drag, eyeDistance, fit, matrix3d, multiply, presetOf, presetView, presetsFor, project, step } from "./turn.camera.ts";
9
- export type { Box3, Mat4, Placement, TurnPreset, TurnView } from "./turn.camera.ts";
8
+ export { CURLING_PRESETS, LONG_PRESETS, NET_PRESETS, PITCH_MAX, STEP, TURN_DEFAULT, TURN_PRESETS, TURN_PRESET_NAMES, ZOOM_MAX, ZOOM_MIN, cameraMatrix, clampView, depthOf, drag, eyeDistance, fit, matrix3d, multiply, nudge, presetOf, presetView, presetsFor, project, step } from "./turn.camera.ts";
9
+ export type { Box3, Mat4, Placement, TurnPreset, TurnToward, TurnView } from "./turn.camera.ts";
10
10
  export { describeUprights, heightUnit, planesOf, uprightsFor } from "./uprights.ts";
11
11
  export type { UprightOptions, Uprights } from "./uprights.ts";
12
12
  export type { Plane, PartKind, Upright, UprightKind, V3 } from "./turn.kit.ts";
@@ -3,5 +3,5 @@ export { mountKyogijoTurn, turnSupported } from "./turn.dom.js";
3
3
  export { buildTurn, floorOptions, openingView } from "./turn.model.js";
4
4
  export { layoutOf, planeMarkup, planeMatrix, turnMarkup, turnMarkupOf } from "./turn.markup.js";
5
5
  export { KYOGIJO_TURN_STYLE } from "./turn.style.js";
6
- export { CURLING_PRESETS, LONG_PRESETS, NET_PRESETS, PITCH_MAX, STEP, TURN_DEFAULT, TURN_PRESETS, TURN_PRESET_NAMES, ZOOM_MAX, ZOOM_MIN, cameraMatrix, clampView, depthOf, drag, eyeDistance, fit, matrix3d, multiply, presetOf, presetView, presetsFor, project, step } from "./turn.camera.js";
6
+ export { CURLING_PRESETS, LONG_PRESETS, NET_PRESETS, PITCH_MAX, STEP, TURN_DEFAULT, TURN_PRESETS, TURN_PRESET_NAMES, ZOOM_MAX, ZOOM_MIN, cameraMatrix, clampView, depthOf, drag, eyeDistance, fit, matrix3d, multiply, nudge, presetOf, presetView, presetsFor, project, step } from "./turn.camera.js";
7
7
  export { describeUprights, heightUnit, planesOf, uprightsFor } from "./uprights.js";
@@ -56,7 +56,18 @@ export declare function step(view: TurnView, change: {
56
56
  pitch?: number;
57
57
  zoom?: number;
58
58
  }): TurnView;
59
- /** The view after a drag of the pointer by (dx, dy) pixels: across turns the scene, up and down tips it, `degreesPerPixel` apart. */
59
+ /** A side a key, a button or a drag takes the surface toward. */
60
+ export type TurnToward = "left" | "right" | "up" | "down";
61
+ /**
62
+ * The view after a step of `amount` degrees toward a side, the way a drag toward that side takes the view: left and right carry the near side to the left and to the right (the yaw rises and
63
+ * falls), up tips the surface as a drag up does and down as a drag down does (the pitch rises and falls). The arrow keys and the four turn buttons are this, so that a drag, a key and a button
64
+ * toward one side all move the surface the same way.
65
+ */
66
+ export declare function nudge(view: TurnView, toward: TurnToward, amount: number): TurnView;
67
+ /**
68
+ * The view after a drag of the pointer by (dx, dy) pixels, `degreesPerPixel` apart: the surface is taken hold of, so a drag to the right carries the side nearest the eye to the right (the
69
+ * yaw falls) and a drag down tips the top toward the eye (the pitch falls).
70
+ */
60
71
  export declare function drag(view: TurnView, dx: number, dy: number, tilt?: boolean, degreesPerPixel?: number): TurnView;
61
72
  /** A box in the scene, in pixels from the scene's middle, the floor at `z0` and the highest thing at `z1`. */
62
73
  export interface Box3 {
@@ -83,8 +94,10 @@ export declare function covered(b: Box3, view: TurnView, at: Placement, distance
83
94
  y1: number;
84
95
  };
85
96
  /**
86
- * 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
87
- * 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.
88
101
  */
89
102
  export declare function fit(b: Box3, view: TurnView, w: number, h: number, distance: number, pad?: number): Placement;
90
103
  /** A 4 by 4 matrix, as CSS writes it in `matrix3d`: sixteen numbers, a column after a column. */
@@ -77,9 +77,25 @@ export const STEP = { yaw: 6, yawBig: 24, pitch: 4, pitchBig: 12, zoom: 1.2 };
77
77
  export function step(view, change) {
78
78
  return clampView({ yaw: view.yaw + (change.yaw ?? 0), pitch: view.pitch + (change.pitch ?? 0), zoom: view.zoom * (change.zoom ?? 1) });
79
79
  }
80
- /** The view after a drag of the pointer by (dx, dy) pixels: across turns the scene, up and down tips it, `degreesPerPixel` apart. */
80
+ /**
81
+ * The view after a step of `amount` degrees toward a side, the way a drag toward that side takes the view: left and right carry the near side to the left and to the right (the yaw rises and
82
+ * falls), up tips the surface as a drag up does and down as a drag down does (the pitch rises and falls). The arrow keys and the four turn buttons are this, so that a drag, a key and a button
83
+ * toward one side all move the surface the same way.
84
+ */
85
+ export function nudge(view, toward, amount) {
86
+ switch (toward) {
87
+ case "left": return step(view, { yaw: amount });
88
+ case "right": return step(view, { yaw: -amount });
89
+ case "up": return step(view, { pitch: amount });
90
+ default: return step(view, { pitch: -amount });
91
+ }
92
+ }
93
+ /**
94
+ * The view after a drag of the pointer by (dx, dy) pixels, `degreesPerPixel` apart: the surface is taken hold of, so a drag to the right carries the side nearest the eye to the right (the
95
+ * yaw falls) and a drag down tips the top toward the eye (the pitch falls).
96
+ */
81
97
  export function drag(view, dx, dy, tilt = true, degreesPerPixel = 0.4) {
82
- return clampView({ ...view, yaw: view.yaw + dx * degreesPerPixel, pitch: tilt ? view.pitch - dy * degreesPerPixel * 0.75 : view.pitch });
98
+ return clampView({ ...view, yaw: view.yaw - dx * degreesPerPixel, pitch: tilt ? view.pitch - dy * degreesPerPixel * 0.75 : view.pitch });
83
99
  }
84
100
  const RAD = Math.PI / 180;
85
101
  /** Where a point of the scene falls on the screen, in pixels from the middle of the container, when the scene is placed as given and the eye is `distance` pixels away. */
@@ -105,24 +121,54 @@ export function covered(b, view, at, distance) {
105
121
  const shown = corners(b).map((c) => project(c, view, at, distance));
106
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])) };
107
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;
108
149
  /**
109
- * 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
110
- * 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.
111
154
  */
112
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 };
113
159
  const at = { k: 0.5, tx: 0, ty: 0 };
114
- const flat = { ...view, zoom: 1 };
115
160
  for (let i = 0; i < 8; i += 1) {
116
- const r = covered(b, flat, at, distance);
161
+ const r = coveredAround(b, view.pitch, at, distance);
117
162
  const cw = r.x1 - r.x0;
118
163
  const ch = r.y1 - r.y0;
119
164
  if (cw <= 0 || ch <= 0)
120
165
  break;
121
166
  at.k *= Math.min((w * (1 - 2 * pad)) / cw, (h * (1 - 2 * pad)) / ch);
122
- const c = covered(b, flat, at, distance);
167
+ const c = coveredAround(b, view.pitch, at, distance);
123
168
  at.tx -= (c.x0 + c.x1) / 2;
124
169
  at.ty -= (c.y0 + c.y1) / 2;
125
170
  }
171
+ lastFit = { key, at: { ...at } };
126
172
  return at;
127
173
  }
128
174
  /** Two matrices as one: `b` first, then `a`. */
package/dist/turn.dom.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // reduced motion gets jumps for glides, and where the browser cannot draw in 3D the flat drawing stays. Nothing is polled and nothing runs while the view is
4
4
  // still: the elements' `transform`s are written when the view changes, and a glide between viewpoints is a short run of animation frames.
5
5
  import { drawKyogijo } from "./draw.js";
6
- import { STEP, TURN_PRESET_NAMES, clampView, drag, nearest, presetOf, presetView, step } from "./turn.camera.js";
6
+ import { STEP, TURN_PRESET_NAMES, clampView, drag, nearest, nudge, presetOf, presetView, step } from "./turn.camera.js";
7
7
  import { buildTurn, floorOptions, openingView, turnWords } from "./turn.model.js";
8
8
  import { controlsMarkup, layoutOf, turnMarkupOf, worldMarkup } from "./turn.markup.js";
9
9
  import { KYOGIJO_TURN_STYLE } from "./turn.style.js";
@@ -182,10 +182,11 @@ export function mountKyogijoTurn(host, options, settings = {}) {
182
182
  return toPreset(name);
183
183
  const big = event.shiftKey === true;
184
184
  switch (target.dataset.act) {
185
- case "turn-left": return go(step(view, { yaw: -(big ? STEP.yawBig : STEP.yaw * 2) }), 180);
186
- case "turn-right": return go(step(view, { yaw: big ? STEP.yawBig : STEP.yaw * 2 }), 180);
187
- case "raise": return go(step(view, { pitch: -(big ? STEP.pitchBig : STEP.pitch * 2) }), 180);
188
- case "lower": return go(step(view, { pitch: big ? STEP.pitchBig : STEP.pitch * 2 }), 180);
185
+ // The buttons take the surface toward their side as a drag and the arrow keys do.
186
+ case "turn-left": return go(nudge(view, "left", big ? STEP.yawBig : STEP.yaw * 2), 180);
187
+ case "turn-right": return go(nudge(view, "right", big ? STEP.yawBig : STEP.yaw * 2), 180);
188
+ case "raise": return go(nudge(view, "up", big ? STEP.pitchBig : STEP.pitch * 2), 180);
189
+ case "lower": return go(nudge(view, "down", big ? STEP.pitchBig : STEP.pitch * 2), 180);
189
190
  case "zoom-in": return go(step(view, { zoom: STEP.zoom }), 180, "none");
190
191
  case "zoom-out": return go(step(view, { zoom: 1 / STEP.zoom }), 180, "none");
191
192
  case "reset": return toOpening();
@@ -202,10 +203,11 @@ export function mountKyogijoTurn(host, options, settings = {}) {
202
203
  const pitch = big ? STEP.pitchBig : STEP.pitch;
203
204
  event.preventDefault();
204
205
  switch (event.key) {
205
- case "ArrowLeft": return go(step(view, { yaw: -yaw }), 120);
206
- case "ArrowRight": return go(step(view, { yaw }), 120);
207
- case "ArrowUp": return go(step(view, { pitch: -pitch }), 120);
208
- case "ArrowDown": return go(step(view, { pitch }), 120);
206
+ // A key takes the surface toward its side as a drag toward that side does: the side nearest the eye goes to the right on ArrowRight, and ArrowUp tips it as a drag up does.
207
+ case "ArrowLeft": return go(nudge(view, "left", yaw), 120);
208
+ case "ArrowRight": return go(nudge(view, "right", yaw), 120);
209
+ case "ArrowUp": return go(nudge(view, "up", pitch), 120);
210
+ case "ArrowDown": return go(nudge(view, "down", pitch), 120);
209
211
  case "+":
210
212
  case "=": return go(step(view, { zoom: STEP.zoom }), 120, "none");
211
213
  case "-":
@@ -1,7 +1,7 @@
1
1
  /** A point or a direction: x, y, z in feet. */
2
2
  export type V3 = readonly [number, number, number];
3
3
  /** What a plane is a part of: it is the class of its element (`ky-up-<kind>`) and what a test finds it by. */
4
- export type PartKind = "post" | "net" | "tape" | "antenna" | "board" | "glass" | "rim" | "stanchion" | "frame" | "goal" | "flag" | "stump" | "leg" | "table" | "pylon" | "wall" | "screen" | "pole" | "ground";
4
+ export type PartKind = "post" | "net" | "tape" | "antenna" | "board" | "glass" | "rim" | "stanchion" | "frame" | "goal" | "flag" | "stump" | "leg" | "table" | "pylon" | "wall" | "screen" | "pole" | "ground" | "target";
5
5
  /** One flat rectangle. Its own x runs along `u` (its whole length) and its own y along `v`; `o` is the corner both start from. `svg` is what is drawn on it, in feet along `u` and `v`. */
6
6
  export interface Plane {
7
7
  /** A name that says what it is and where: `net`, `post-left`, `goal-right-back`. */
@@ -23,7 +23,7 @@ export interface Plane {
23
23
  shy?: readonly [number, number];
24
24
  }
25
25
  /** What stands on the surface as a thing a reader would name: a net, a basket, a goal, a pair of posts. */
26
- export type UprightKind = "scoreboard" | "net" | "antennae" | "goalposts" | "netposts" | "hoop" | "goal" | "boards" | "stumps" | "pylons" | "flags" | "fence" | "backstop" | "foul-poles" | "table-legs" | "walls" | "starting-blocks" | "backstroke-flags" | "false-start-rope" | "springboards" | "platforms" | "judges-stand" | "hurdles" | "steeplechase" | "high-jump" | "pole-vault" | "cage" | "padding" | "rink-boards";
26
+ export type UprightKind = "scoreboard" | "net" | "antennae" | "goalposts" | "netposts" | "hoop" | "goal" | "boards" | "stumps" | "pylons" | "flags" | "fence" | "backstop" | "foul-poles" | "table-legs" | "walls" | "starting-blocks" | "backstroke-flags" | "false-start-rope" | "springboards" | "platforms" | "judges-stand" | "hurdles" | "steeplechase" | "high-jump" | "pole-vault" | "cage" | "padding" | "rink-boards" | "mound" | "azuchi" | "targets";
27
27
  /** A thing that stands on the surface: where, how high, and the planes it is made of. */
28
28
  export interface Upright {
29
29
  id: string;
@@ -3,9 +3,9 @@
3
3
  * defaults are in `:where()`, which has no weight, so a page's own rule always wins; the colours are custom properties (`--ky-up-*`) a page can set.
4
4
  */
5
5
  export const KYOGIJO_TURN_STYLE = `
6
- :where(.ky-turn){--ky-up-sbframe:#1a1f1c;--ky-up-sbedge:#5b645e;--ky-up-sbline:#59615b;--ky-up-pole:#e9e7de;--ky-up-white:#f6f4ec;--ky-up-yellow:#f2c230;--ky-up-red:#c8372d;--ky-up-orange:#f26a1b;--ky-up-wood:#e3d4b4;--ky-up-leg:#474e49;--ky-up-pad:#2f6db0;--ky-up-table:#18243f;--ky-up-foot:rgba(0,0,0,.25);--ky-up-ground:#d9d5c7;--ky-up-wall:#2f4a36;--ky-up-cap:#1d2a22;--ky-up-board:#ece9df;--ky-up-glass:rgba(138,190,232,.26);--ky-up-backboard:rgba(255,255,255,.88);--ky-up-outline:#f2f0e6;--ky-up-rim:#e8591a;--ky-up-mesh:rgba(24,30,27,.7);--ky-up-netfill:rgba(255,255,255,.2);--ky-up-tape:#fbfaf4;--ky-turn-bg:transparent;--ky-turn-ink:#1f2320;--ky-turn-rule:rgba(31,35,32,.22);--ky-turn-chip:rgba(255,255,255,.86);--ky-turn-chip-on:#1f2320;--ky-turn-chip-on-ink:#fff}
7
- @media (prefers-color-scheme:dark){:where(html:not([data-theme=light]) .ky-turn:not([data-theme=light])){--ky-up-sbframe:#0c0f0d;--ky-up-sbedge:#7a847d;--ky-up-sbline:#8a928c;--ky-up-pole:#d6d4ca;--ky-up-white:#e8e6dc;--ky-up-wood:#a89873;--ky-up-leg:#8a928c;--ky-up-ground:#242823;--ky-up-wall:#4d6b55;--ky-up-cap:#c9d2cc;--ky-up-board:#8d928b;--ky-up-glass:rgba(150,200,240,.2);--ky-up-backboard:rgba(255,255,255,.34);--ky-up-outline:#e7e4d8;--ky-up-mesh:rgba(236,236,226,.62);--ky-up-netfill:rgba(255,255,255,.08);--ky-up-tape:#efede2;--ky-turn-ink:#ece8dc;--ky-turn-rule:rgba(236,232,220,.28);--ky-turn-chip:rgba(24,28,25,.82);--ky-turn-chip-on:#ece8dc;--ky-turn-chip-on-ink:#1f2320}}
8
- :where(html[data-theme=dark] .ky-turn,.ky-turn[data-theme=dark]){--ky-up-sbframe:#0c0f0d;--ky-up-sbedge:#7a847d;--ky-up-sbline:#8a928c;--ky-up-pole:#d6d4ca;--ky-up-white:#e8e6dc;--ky-up-wood:#a89873;--ky-up-leg:#8a928c;--ky-up-ground:#242823;--ky-up-wall:#4d6b55;--ky-up-cap:#c9d2cc;--ky-up-board:#8d928b;--ky-up-glass:rgba(150,200,240,.2);--ky-up-backboard:rgba(255,255,255,.34);--ky-up-outline:#e7e4d8;--ky-up-mesh:rgba(236,236,226,.62);--ky-up-netfill:rgba(255,255,255,.08);--ky-up-tape:#efede2;--ky-turn-ink:#ece8dc;--ky-turn-rule:rgba(236,232,220,.28);--ky-turn-chip:rgba(24,28,25,.82);--ky-turn-chip-on:#ece8dc;--ky-turn-chip-on-ink:#1f2320}
6
+ :where(.ky-turn){--ky-up-sbframe:#1a1f1c;--ky-up-sbedge:#5b645e;--ky-up-sbline:#59615b;--ky-up-pole:#e9e7de;--ky-up-white:#f6f4ec;--ky-up-yellow:#f2c230;--ky-up-red:#c8372d;--ky-up-orange:#f26a1b;--ky-up-wood:#e3d4b4;--ky-up-leg:#474e49;--ky-up-pad:#2f6db0;--ky-up-table:#18243f;--ky-up-foot:rgba(0,0,0,.25);--ky-up-ground:#d9d5c7;--ky-up-wall:#2f4a36;--ky-up-clay:#a8703f;--ky-up-cap:#1d2a22;--ky-up-board:#ece9df;--ky-up-glass:rgba(138,190,232,.26);--ky-up-backboard:rgba(255,255,255,.88);--ky-up-outline:#f2f0e6;--ky-up-rim:#e8591a;--ky-up-mesh:rgba(24,30,27,.7);--ky-up-netfill:rgba(255,255,255,.2);--ky-up-tape:#fbfaf4;--ky-turn-bg:transparent;--ky-turn-ink:#1f2320;--ky-turn-rule:rgba(31,35,32,.22);--ky-turn-chip:rgba(255,255,255,.86);--ky-turn-chip-on:#1f2320;--ky-turn-chip-on-ink:#fff}
7
+ @media (prefers-color-scheme:dark){:where(html:not([data-theme=light]) .ky-turn:not([data-theme=light])){--ky-up-sbframe:#0c0f0d;--ky-up-sbedge:#7a847d;--ky-up-sbline:#8a928c;--ky-up-pole:#d6d4ca;--ky-up-white:#e8e6dc;--ky-up-wood:#a89873;--ky-up-leg:#8a928c;--ky-up-ground:#242823;--ky-up-wall:#4d6b55;--ky-up-clay:#7d5231;--ky-up-cap:#c9d2cc;--ky-up-board:#8d928b;--ky-up-glass:rgba(150,200,240,.2);--ky-up-backboard:rgba(255,255,255,.34);--ky-up-outline:#e7e4d8;--ky-up-mesh:rgba(236,236,226,.62);--ky-up-netfill:rgba(255,255,255,.08);--ky-up-tape:#efede2;--ky-turn-ink:#ece8dc;--ky-turn-rule:rgba(236,232,220,.28);--ky-turn-chip:rgba(24,28,25,.82);--ky-turn-chip-on:#ece8dc;--ky-turn-chip-on-ink:#1f2320}}
8
+ :where(html[data-theme=dark] .ky-turn,.ky-turn[data-theme=dark]){--ky-up-sbframe:#0c0f0d;--ky-up-sbedge:#7a847d;--ky-up-sbline:#8a928c;--ky-up-pole:#d6d4ca;--ky-up-white:#e8e6dc;--ky-up-wood:#a89873;--ky-up-leg:#8a928c;--ky-up-ground:#242823;--ky-up-wall:#4d6b55;--ky-up-clay:#7d5231;--ky-up-cap:#c9d2cc;--ky-up-board:#8d928b;--ky-up-glass:rgba(150,200,240,.2);--ky-up-backboard:rgba(255,255,255,.34);--ky-up-outline:#e7e4d8;--ky-up-mesh:rgba(236,236,226,.62);--ky-up-netfill:rgba(255,255,255,.08);--ky-up-tape:#efede2;--ky-turn-ink:#ece8dc;--ky-turn-rule:rgba(236,232,220,.28);--ky-turn-chip:rgba(24,28,25,.82);--ky-turn-chip-on:#ece8dc;--ky-turn-chip-on-ink:#1f2320}
9
9
  .ky-turn{position:relative;display:block;width:100%;color:var(--ky-turn-ink);font-family:var(--ky-font,system-ui,-apple-system,"Segoe UI","Hiragino Sans","Noto Sans JP",sans-serif);background:var(--ky-turn-bg)}
10
10
  .ky-turn-view{position:relative;isolation:isolate;width:100%;aspect-ratio:var(--ky-turn-ratio,16/10);min-height:260px;overflow:hidden;touch-action:pan-y;cursor:grab;user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:transparent;border-radius:12px;outline-offset:2px}
11
11
  .ky-turn[data-touch=turn] .ky-turn-view{touch-action:none}
@@ -30,6 +30,7 @@ export const KYOGIJO_TURN_STYLE = `
30
30
  .ky-turn .u-foot{fill:var(--ky-up-foot)}
31
31
  .ky-turn .u-ground{fill:var(--ky-up-ground)}
32
32
  .ky-turn .u-wall{fill:var(--ky-up-wall)}
33
+ .ky-turn .u-clay{fill:var(--ky-up-clay)}
33
34
  .ky-turn .u-cap{fill:var(--ky-up-cap)}
34
35
  .ky-turn .u-board{fill:var(--ky-up-board)}
35
36
  .ky-turn .u-glass{fill:var(--ky-up-glass)}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Background } from "./background.ts";
2
+ import type { EmblemRef, EmblemRenderer } from "./emblems-source.ts";
2
3
  import type { Layer } from "./layers.types.ts";
3
4
  import type { Unit } from "./units.ts";
4
5
  import type { SoccerFrame, SoccerProviderId } from "./surfaces/soccer-providers.ts";
@@ -7,11 +8,16 @@ import type { Frame } from "./frames.ts";
7
8
  import type { PoolSport, PoolStyle } from "./pool.types.ts";
8
9
  import type { AthleticsStyle } from "./athletics/types.ts";
9
10
  import type { SkatingStyle } from "./skating/types.ts";
11
+ import type { Situation } from "./situation.types.ts";
12
+ import type { BudoStyle, DojoSpec, BudoScore } from "./budo/types.ts";
10
13
  /** The field sports: lacrosse (the field and the box), rugby (union and league), Australian football, Gaelic football and hurling, cricket, field hockey and team handball. */
11
14
  export type FieldSport = "lacrosse" | "rugby" | "afl" | "gaelic" | "cricket" | "fieldhockey" | "handball";
12
15
  export type { PoolSport, PoolStyle };
16
+ /** The budō: the contest areas of judo and karate, the kendo shiai-jo, the sumo dohyō, the kyūdōjō, and a dojo of tatami. */
17
+ export type BudoSport = "judo" | "karate" | "kendo" | "sumo" | "kyudo" | "dojo";
18
+ export type { BudoStyle, DojoSpec, BudoScore };
13
19
  /** The surfaces: three football fields, a rink, a court, a ballpark, a soccer pitch, a curling sheet, twelve net, racket and court sports (tennis, volleyball, badminton, pickleball, table tennis, padel, squash, racquetball, sepak takraw, teqball, roundnet and netball), four pools (swimming, water polo, diving and artistic swimming), seven field sports (lacrosse, rugby, Australian football, Gaelic football and hurling, cricket, field hockey and handball) and the athletics stadium (the 400 m track with its infield, and the covered 200 m track). */
14
- export type Sport = "nfl" | "cfl" | "ifaf" | "nhl" | "nba" | "mlb" | "soccer" | "curling" | "tennis" | "volleyball" | "badminton" | "pickleball" | "tabletennis" | "padel" | "squash" | "racquetball" | "sepaktakraw" | "teqball" | "roundnet" | "netball" | PoolSport | FieldSport | "athletics" | "skating";
20
+ export type Sport = "nfl" | "cfl" | "ifaf" | "nhl" | "nba" | "mlb" | "soccer" | "curling" | "tennis" | "volleyball" | "badminton" | "pickleball" | "tabletennis" | "padel" | "squash" | "racquetball" | "sepaktakraw" | "teqball" | "roundnet" | "netball" | PoolSport | FieldSport | "athletics" | "skating" | BudoSport;
15
21
  /** `auto` follows the device's light or dark setting (and `data-theme` on the page's root), the others force one. */
16
22
  export type Theme = "auto" | "light" | "dark";
17
23
  /** The language of the words the drawing says itself: its title, its description and its status labels. */
@@ -310,9 +316,9 @@ export interface CrowdOptions {
310
316
  * The parts of a surface a drawing can be cropped to (`range`). Football: `red-zone`, `between-the-20s`, `end-zone`, `field-of-play`, `own-half`, `opponent-half`.
311
317
  * Hockey: `offensive-zone`, `defensive-zone`, `neutral-zone`, `half`. Basketball: `half-court`, `paint`. Soccer: `half`, `final-third`, `defensive-third`, `middle-third`,
312
318
  * `penalty-area`. Baseball: `infield`, `outfield`. The net sports: `half-court`. Curling: `sheet`, `half`, `house`, `folded`. Rugby: `half`, `twenty-two` (union) or `twenty` (league), `in-goal`. Lacrosse: `half`, `crease`. Australian football: `half`, `forward-fifty`, `centre-square`.
313
- * Gaelic games: `half`, `large-rectangle`. Cricket: `pitch`, `circle`. Field hockey: `half`, `circle`, `twenty-three`. Handball: `half`, `goal-area`. Swimming: `half`, `pool-end`. Water polo: `half`, `goal-area`. Diving: `springboards`, `platforms`. Artistic swimming: `half`, `centre-stage`. Athletics: `bend`, `home-straight`, `back-straight`, `finish`, `start`, `infield`, a field event (`high-jump`, `pole-vault`, `long-jump`, `triple-jump`, `shot-put`, `discus-throw`, `hammer-throw`, `javelin-throw`) and `water-jump`. `RANGES` lists them for each sport.
319
+ * Gaelic games: `half`, `large-rectangle`. Cricket: `pitch`, `circle`. Field hockey: `half`, `circle`, `twenty-three`. Handball: `half`, `goal-area`. Swimming: `half`, `pool-end`. Water polo: `half`, `goal-area`. Diving: `springboards`, `platforms`. Artistic swimming: `half`, `centre-stage`. Judo and karate: `contest-area`, `start`. Sumo: `ring`. Kyudo: `shajo`, `yamichi`, `azuchi`. Dojo: `kamiza`, `half`. Athletics: `bend`, `home-straight`, `back-straight`, `finish`, `start`, `infield`, a field event (`high-jump`, `pole-vault`, `long-jump`, `triple-jump`, `shot-put`, `discus-throw`, `hammer-throw`, `javelin-throw`) and `water-jump`. `RANGES` lists them for each sport.
314
320
  */
315
- export type RangeName = "red-zone" | "between-the-20s" | "end-zone" | "field-of-play" | "own-half" | "opponent-half" | "offensive-zone" | "defensive-zone" | "neutral-zone" | "half" | "half-court" | "paint" | "final-third" | "defensive-third" | "middle-third" | "penalty-area" | "infield" | "outfield" | "sheet" | "house" | "folded" | "in-goal" | "twenty-two" | "twenty" | "crease" | "forward-fifty" | "centre-square" | "large-rectangle" | "pitch" | "circle" | "twenty-three" | "goal-area" | "pool-end" | "springboards" | "platforms" | "centre-stage" | "bend" | "home-straight" | "back-straight" | "finish" | "start" | "infield" | "high-jump" | "pole-vault" | "long-jump" | "triple-jump" | "shot-put" | "discus-throw" | "hammer-throw" | "javelin-throw" | "water-jump" | "crossing-straight" | "first-bend" | "second-bend" | "judges" | "kiss-and-cry";
321
+ export type RangeName = "red-zone" | "between-the-20s" | "end-zone" | "field-of-play" | "own-half" | "opponent-half" | "offensive-zone" | "defensive-zone" | "neutral-zone" | "half" | "half-court" | "paint" | "final-third" | "defensive-third" | "middle-third" | "penalty-area" | "infield" | "outfield" | "sheet" | "house" | "folded" | "in-goal" | "twenty-two" | "twenty" | "crease" | "forward-fifty" | "centre-square" | "large-rectangle" | "pitch" | "circle" | "twenty-three" | "goal-area" | "pool-end" | "springboards" | "platforms" | "centre-stage" | "bend" | "home-straight" | "back-straight" | "finish" | "start" | "infield" | "high-jump" | "pole-vault" | "long-jump" | "triple-jump" | "shot-put" | "discus-throw" | "hammer-throw" | "javelin-throw" | "water-jump" | "crossing-straight" | "first-bend" | "second-bend" | "judges" | "kiss-and-cry" | "contest-area" | "ring" | "shajo" | "yamichi" | "azuchi" | "kamiza";
316
322
  /** A named range at the end of your choice: `{ name: "offensive-zone", end: "left" }` is the offensive zone of a team that attacks the left. */
317
323
  export interface RangeNamed {
318
324
  name: RangeName;
@@ -447,8 +453,8 @@ export interface PageantOptions {
447
453
  lines?: "over" | "under" | "through";
448
454
  /** A stage's ring: `gold` (the default), `team` (each team's colour), or a colour. */
449
455
  ring?: "gold" | "team" | (string & {});
450
- /** The picture in the centre circle, centre ice, centre court or at the mound: a competition's emblem or a trophy, as a logo is given (an address, an image `data:` address or an SVG document as text). Yours; none is supplied. */
451
- emblem?: string;
456
+ /** The picture in the centre circle, centre ice, centre court or at the mound: a competition's emblem or a trophy, as a logo is given (an address, an image `data:` address, an SVG document as text, or, with a renderer of emblems plugged in, the id of one: `"trophy:hockey-cup"`). Yours; none is supplied. */
457
+ emblem?: EmblemRef;
452
458
  /** `round` (the default): the emblem on a disc with a ring, as a team's mark. `plain`: the picture as it is, for a trophy with a cut-out outline. */
453
459
  emblemShape?: "round" | "plain";
454
460
  /** An emblem's fit on its disc: `contain` (the default: shown whole) or `cover` (it fills the disc). */
@@ -478,8 +484,8 @@ export interface Team {
478
484
  abbr?: string;
479
485
  /** The score. A number, or text such as "–" before the game. */
480
486
  score?: number | string;
481
- /** The logo: an `https:` or relative address, an image `data:` address, or an SVG document as text. */
482
- logo?: string;
487
+ /** The logo: an `https:` or relative address, an image `data:` address, an SVG document as text, or, with a renderer of emblems plugged in (`useEmblems`), the id of a ready-made emblem: `"crest:shield"` takes the team's `color`, `textColor` and `abbr`. */
488
+ logo?: EmblemRef;
483
489
  /**
484
490
  * How the logo is drawn: `round` clips it to a circle with a thin ring (a flag, or a crest) and, for a team with no logo, draws its initials on its colour; `plain` is the
485
491
  * logo as it is, in a box. The score's `markShape` is the default for both teams.
@@ -510,6 +516,8 @@ export interface Team {
510
516
  wickets?: number | string;
511
517
  overs?: number | string;
512
518
  innings?: readonly (number | string | null | undefined)[];
519
+ /** Judo, karate, kendo and sumo: what the team has scored, as the sport counts it (see `BudoScore`). */
520
+ budo?: BudoScore;
513
521
  /** The team's colour, for its end, key or dugout. */
514
522
  color?: string;
515
523
  /** The colour of the words on that colour. Black or white by the colour's lightness when missing. */
@@ -558,6 +566,8 @@ export interface SurfaceOptions {
558
566
  description?: string;
559
567
  /** A prefix for the ids inside the drawing. Made from the options when missing, so that the same call gives the same drawing. */
560
568
  id?: string;
569
+ /** A renderer of ready-made emblems for this drawing alone, so that a `logo`, an `emblem` or a mark may be an emblem's id: `emblems: renderEmblem` from `@johnmorrisdotca/dougu-sports/emblems`. Over the one the page plugged in with `useEmblems`. */
570
+ emblems?: EmblemRenderer;
561
571
  /** The ground to draw round the playing surface, in `units` (feet by default). A default for each sport. */
562
572
  margin?: number;
563
573
  /**
@@ -635,10 +645,16 @@ export interface SurfaceOptions {
635
645
  athletics?: AthleticsStyle;
636
646
  /** The skating rink: the long track's radius and lanes, the short track's tracks, the figure skating rink's size and furniture, the races marked. See `SkatingStyle`; the discipline is the `league`. */
637
647
  skating?: SkatingStyle;
648
+ /** The budō surfaces (judo, karate, kendo, sumo, kyudo): the size of the area, the marks, the number of areas, the look. See `BudoStyle`; the rulebook is the `league`. */
649
+ budo?: BudoStyle;
650
+ /** A dojo of tatami (`sport: "dojo"`): the mats, the pattern, the walls, the kamiza. See `DojoSpec` and `dojo()`. */
651
+ dojo?: DojoSpec;
638
652
  /** The soccer pitch: a preset (`fifa`, `uefa`, `mls`, `nwsl`, `ncaa`, `law-min`, `law-max`, `9-a-side`, `7-a-side`, `5-a-side`, `futsal`) or a size, `{ length, width, unit }`. The international 105 by 68 m by default. */
639
653
  pitch?: SoccerPitchId | SoccerSize;
640
654
  /** The soccer ground to draw: a key of `GROUNDS`. The pitch has the size its article publishes (the international 105 by 68 m where none is), and the drawing carries the ground's name for `year`. A `pitch` of your own wins over the ground's size. */
641
655
  ground?: string;
656
+ /** The arena to draw an NHL rink or an NBA court of: a key of `ARENAS`. The rink or the court has the size its sources publish where that differs from the league's (the league's where none does), the markings are those of `year`, and the drawing carries the arena's name for `year`. A `league` of your own wins over the arena's size. */
657
+ arena?: string;
642
658
  /** Draw a soccer pitch in a data provider's own coordinates (`opta`, `statsbomb`, `wyscout`, `tracab`, `metrica`, `skillcorner`, `secondspectrum`, `impect`, `uefa`, `international`), or in a frame of your own: the lines stand where the provider puts them, and an empty `ky-data` group in its units is put in the drawing for raw data. */
643
659
  pitchType?: SoccerProviderId | SoccerFrame;
644
660
  /** Only half of a soccer pitch: the `left` half or the `right` one. Ignored when the drawing has teams. `range: "half"` does the same for every sport and for a score too. */
@@ -689,6 +705,20 @@ export interface SurfaceOptions {
689
705
  * under a score's panel), cut to the surface's outline, and described in the drawing's description. See `drawLayers` and `layerTable`.
690
706
  */
691
707
  layers?: readonly Layer[];
708
+ /**
709
+ * Write what `clientPointToField` (the `pointer` entry) needs on the drawing's surface group: the box of the playing surface and the options that decide its rules (the league, the
710
+ * season, the park, the pitch). Off by default, so that a drawing without data is the same text as ever; a page that asks where a click is turns it on.
711
+ */
712
+ pointer?: boolean;
713
+ /** Write `data-mark-id`, `data-index` and `data-zone` on every mark of every data layer, for one listener to link a mark to a table (about 55 bytes a mark). Off by default; a layer whose data has an `id` has them anyway, and `pointer: true` turns them on. */
714
+ marks?: boolean;
715
+ /**
716
+ * The situation of the game, drawn on the surface, in the badge and in the compact card: hockey's skaters (a power play or an empty net as a pill, the attacking zone tinted) and its
717
+ * shootout as a strip of dots across centre ice; baseball's runners (the bases lit), outs, inning and count (a scorebug in the badge); football's down and distance (the line of scrimmage
718
+ * in blue, the first-down line in yellow, the red zone tinted, a tag) and a drive chart. For `nhl`, `mlb`, and the football fields; any other sport, and a special game, draws none.
719
+ * Off by default, and then the drawing is byte for byte as it was. See `docs/situation.md`.
720
+ */
721
+ situation?: Situation;
692
722
  /** `embed` (the default) writes the stylesheet into the drawing so that it stands alone; `none` leaves it to the page. */
693
723
  style?: "embed" | "none";
694
724
  /** An extra class on the `svg`. */
@@ -725,6 +755,12 @@ export interface ScoreOptions extends SurfaceOptions {
725
755
  uppercase?: boolean;
726
756
  /** The shape of both teams' marks, unless a team's own `mark` says otherwise: `plain` (the default) or `round`, a flag or logo in a circle with a ring. */
727
757
  markShape?: TeamMark;
758
+ /**
759
+ * Where the score goes: `centre` (the panel stands on the middle of the surface), `strip` (the compact strip under the drawing, as a pageant's `score: "strip"` does) or `auto` (the default:
760
+ * the centre, unless the drawing has a `lineup` layer, whose players the panel would hide, and then the strip). The strip also carries the situation's pill, scorebug or tag, which a drawing
761
+ * with a lineup then leaves off the surface. A pageant has its own `pageant.score`, which says it for the drawing.
762
+ */
763
+ score?: "auto" | "centre" | "strip";
728
764
  /** Draw the score's panel (or board) on the surface. On by default; `false` leaves the teams' names and the rest and no panel, as the 3D view does when it hangs the score up as a scoreboard. */
729
765
  plate?: boolean;
730
766
  }
package/dist/units.js CHANGED
@@ -49,6 +49,13 @@ export const SURFACE_UNITS = Object.freeze({
49
49
  artisticswimming: "m",
50
50
  athletics: "m",
51
51
  skating: "m",
52
+ // The budō: the federations print their figures in metres (the kyūdōjō's and the dohyō's too), and a dojo is counted in mats of metres.
53
+ judo: "m",
54
+ karate: "m",
55
+ kendo: "m",
56
+ sumo: "m",
57
+ kyudo: "m",
58
+ dojo: "m",
52
59
  });
53
60
  /** True when `unit` is one of the six. */
54
61
  export const isUnit = (unit) => typeof unit === "string" && Object.prototype.hasOwnProperty.call(FEET_PER, unit);
@@ -0,0 +1,17 @@
1
+ import { type Upright } from "./turn.kit.ts";
2
+ import type { SurfaceOptions } from "./types.ts";
3
+ /** What the 3D view makes of a budō surface: the things that stand, how high the surface itself stands, and the box the view is fitted to. */
4
+ export interface BudoUprights {
5
+ items: Upright[];
6
+ lift: number;
7
+ area: {
8
+ x0: number;
9
+ y0: number;
10
+ x1: number;
11
+ y1: number;
12
+ };
13
+ }
14
+ /** The walls of a dojo, 3 m high (ours). */
15
+ export declare const DOJO_WALL = 3;
16
+ /** What stands on the budō surface the options draw. */
17
+ export declare function budoUprights(o: Pick<SurfaceOptions, "sport" | "league" | "budo" | "dojo" | "detail">, thin: number): BudoUprights;
@@ -0,0 +1,87 @@
1
+ // What stands on a budō surface, in 3D: the dohyō's clay mound (the clay square stands 34 cm to 60 cm over the floor of the hall, Sumo Rules, Dohyō Regulations, Article 1), the azuchi of a kyūdōjō with the
2
+ // targets on it (36 cm across and 27 cm over the mound's base, tilted 5 degrees; 100 cm, 97 cm and 15 degrees for enteki), and the walls of a dojo. A judo, karate or kendo surface lies flat: nothing stands on it
3
+ // (a judo platform's height is not in the rules: "it depends on the sightlines"). The plans are in metres in the frame of their own origin, and the drawing's frame is feet from the corner of the plan's
4
+ // box, so every point goes through `at`. The mound's height is the option's; the azuchi's (1.5 m or 1.8 m) and a dojo wall's (3 m) are ours: no rule gives them.
5
+ import { budoPlan } from "./budo/plan.js";
6
+ import { n, plane, shy, wall } from "./turn.kit.js";
7
+ const K = 1 / 0.3048;
8
+ /** The walls of a dojo, 3 m high (ours). */
9
+ export const DOJO_WALL = 3;
10
+ /** The planes of the dohyō's clay mound: its four sides, from the floor of the hall up to the clay. */
11
+ function moundPlanes(plan, at, height) {
12
+ const L = plan.sumo;
13
+ if (L === undefined)
14
+ return [];
15
+ const b = L.platform;
16
+ const corners = [at(b.x0, b.y0), at(b.x1, b.y0), at(b.x1, b.y1), at(b.x0, b.y1)];
17
+ const planes = [];
18
+ corners.forEach((a, i) => {
19
+ const c = corners[(i + 1) % 4];
20
+ planes.push(wall(`mound-${i + 1}`, "wall", a[0], a[1], c[0], c[1], 0, height, `<rect width="${n(Math.hypot(c[0] - a[0], c[1] - a[1]))}" height="${n(height)}" class="u-clay"/>`));
21
+ });
22
+ return planes;
23
+ }
24
+ /** The azuchi's face, a slope from the foot of the mound up to its top, and the target of each shooter standing against it. */
25
+ function azuchiPlanes(plan, at) {
26
+ const L = plan.kyudo;
27
+ if (L === undefined)
28
+ return { mound: [], targets: [], height: 0 };
29
+ const fig = plan.figures;
30
+ const h = fig.azuchi.height * K;
31
+ const a = L.azuchi;
32
+ // The face runs from the foot, in front of the target's line, up to the top at the target's line: a slope of about 50 degrees reaches 1.5 m in 1.26 m.
33
+ const run = (fig.azuchi.height / Math.tan((50 * Math.PI) / 180)) * K;
34
+ const [fx, fy0] = at(a.x0, a.y0);
35
+ const [, fy1] = at(a.x0, a.y1);
36
+ const topX = fx + run;
37
+ const mound = plane("azuchi-face", "wall", [topX, fy0, h], [0, fy1 - fy0, 0], [-run, 0, -h], `<rect width="${n(fy1 - fy0)}" height="${n(Math.hypot(run, h))}" class="u-clay"/>`);
38
+ const d = fig.target.diameter * K;
39
+ const tilt = (fig.target.tilt * Math.PI) / 180;
40
+ const centre = fig.target.centre * K;
41
+ const targets = L.lanes.map((y, i) => {
42
+ const [tx, ty] = at(L.target, y);
43
+ // The face leans back at the top: its top is further from the shooters than its bottom.
44
+ const top = [tx + (d / 2) * Math.sin(tilt), ty - d / 2, centre + (d / 2) * Math.cos(tilt)];
45
+ return plane(`target-${i + 1}`, "target", top, [0, d, 0], [-d * Math.sin(tilt), 0, -d * Math.cos(tilt)], `<circle cx="${n(d / 2)}" cy="${n(d / 2)}" r="${n(d / 2)}" class="u-white"/><circle cx="${n(d / 2)}" cy="${n(d / 2)}" r="${n(d * 0.33)}" class="u-outline" fill="none"/><circle cx="${n(d / 2)}" cy="${n(d / 2)}" r="${n(d * 0.16)}" class="u-outline" fill="none"/>`);
46
+ });
47
+ return { mound: [mound], targets, height: h };
48
+ }
49
+ /** What stands on the budō surface the options draw. */
50
+ export function budoUprights(o, thin) {
51
+ const plan = budoPlan(o);
52
+ const ox = plan.box.x0;
53
+ const oy = plan.box.y0;
54
+ const at = (x, y) => [(x - ox) * K, (y - oy) * K];
55
+ const area = { x0: 0, y0: 0, x1: (plan.box.x1 - ox) * K, y1: (plan.box.y1 - oy) * K };
56
+ const items = [];
57
+ let lift = 0;
58
+ if (plan.sumo !== undefined) {
59
+ lift = plan.sumo.height * K;
60
+ items.push({ id: "mound", kind: "mound", at: at(plan.sumo.centre[0], plan.sumo.centre[1]), height: lift, planes: moundPlanes(plan, at, lift) });
61
+ }
62
+ if (plan.kyudo !== undefined) {
63
+ const built = azuchiPlanes(plan, at);
64
+ items.push({ id: "azuchi", kind: "azuchi", at: at(plan.kyudo.target, plan.kyudo.lanes[0]), height: built.height, planes: built.mound });
65
+ if (built.targets.length > 0)
66
+ items.push({ id: "targets", kind: "targets", at: at(plan.kyudo.target, plan.kyudo.lanes[0]), height: Math.max(...built.targets.map((p) => p.o[2])), planes: built.targets });
67
+ }
68
+ if (plan.dojo !== undefined && plan.dojo.wall > 0) {
69
+ const f = plan.dojo.room;
70
+ const h = DOJO_WALL * K;
71
+ const w = plan.dojo.wall;
72
+ const centre = [area.x1 / 2, area.y1 / 2];
73
+ const planes = [
74
+ [f.x0 - w / 2, f.y0 - w / 2, f.x1 + w / 2, f.y0 - w / 2],
75
+ [f.x1 + w / 2, f.y0 - w / 2, f.x1 + w / 2, f.y1 + w / 2],
76
+ [f.x1 + w / 2, f.y1 + w / 2, f.x0 - w / 2, f.y1 + w / 2],
77
+ [f.x0 - w / 2, f.y1 + w / 2, f.x0 - w / 2, f.y0 - w / 2],
78
+ ].map(([x0, y0, x1, y1], i) => {
79
+ const [a, b] = [at(x0, y0), at(x1, y1)];
80
+ const run = Math.hypot(b[0] - a[0], b[1] - a[1]);
81
+ return shy(wall(`wall-${i + 1}`, "wall", a[0], a[1], b[0], b[1], 0, h, `<rect width="${n(run)}" height="${n(h)}" class="u-wall"/>`), centre);
82
+ });
83
+ items.push({ id: "walls", kind: "walls", at: centre, height: h, planes });
84
+ }
85
+ void thin;
86
+ return { items, lift, area };
87
+ }
package/dist/uprights.js CHANGED
@@ -22,13 +22,15 @@ import { netUprights } from "./uprights.net.js";
22
22
  import { isPoolSport, poolPlan, feet as poolFeet } from "./pool.js";
23
23
  import { poolUprights } from "./uprights.pools.js";
24
24
  import { athleticsUprights, skatingUprights } from "./uprights.track.js";
25
+ import { budoUprights } from "./uprights.budo.js";
26
+ import { isBudoSport } from "./budo/plan.js";
25
27
  import { courtUprights, rinkUprights } from "./uprights.rinks.js";
26
28
  import { convertLength } from "./units.js";
27
29
  import { sheetFor } from "./sheet.js";
28
30
  /** All the planes of all the things. */
29
31
  export const planesOf = (u) => u.items.flatMap((item) => item.planes);
30
32
  /** The unit each sport's rulebook gives its heights in, when the options name none. */
31
- const RULEBOOK_UNIT = { volleyball: "m", badminton: "m", tabletennis: "m", padel: "m", squash: "m", athletics: "m", skating: "m", swimming: "m", waterpolo: "m", diving: "m", artisticswimming: "m", sepaktakraw: "m", teqball: "m", roundnet: "m", netball: "m", soccer: "m", handball: "m", fieldhockey: "m", rugby: "m", afl: "m", gaelic: "m" };
33
+ const RULEBOOK_UNIT = { volleyball: "m", badminton: "m", tabletennis: "m", padel: "m", squash: "m", athletics: "m", skating: "m", judo: "m", karate: "m", kendo: "m", sumo: "m", kyudo: "m", dojo: "m", swimming: "m", waterpolo: "m", diving: "m", artisticswimming: "m", sepaktakraw: "m", teqball: "m", roundnet: "m", netball: "m", soccer: "m", handball: "m", fieldhockey: "m", rugby: "m", afl: "m", gaelic: "m" };
32
34
  /** What stands on the surface the options draw: nothing on a curling sheet, which is flat. */
33
35
  export function uprightsFor(o, settings = {}) {
34
36
  const { sport, league } = chooseLeague(o);
@@ -102,6 +104,13 @@ export function uprightsFor(o, settings = {}) {
102
104
  thin = thinnest(area.x1, area.y1);
103
105
  items = skatingUprights({ ...o, sport }, thin).items;
104
106
  }
107
+ else if (isBudoSport(sport)) {
108
+ const built = budoUprights({ ...o, sport }, 0.3);
109
+ area = built.area;
110
+ thin = thinnest(area.x1, area.y1);
111
+ items = budoUprights({ ...o, sport }, thin).items;
112
+ lift = built.lift;
113
+ }
105
114
  else if (isFieldSport(sport)) {
106
115
  const plan = fieldPlan({ ...o, sport });
107
116
  thin = thinnest(plan.length, plan.width);
@@ -1,5 +1,5 @@
1
1
  // What stands on the pools, in 3D: a swimming pool's starting blocks, its backstroke flag lines and its false start rope; a diving well's springboards and platforms on their stands; a water
2
- // polo field's two floating goals; an artistic swimming area's judges' stand and starting platform. The heights are World Aquatics' (the Competition Regulations in force from 25 June 2025):
2
+ // polo field's two floating goals; an artistic swimming area's judges' stand and starting platform. The heights are World Aquatics' (the Competition Regulations in force from February 2026, bar the few figures `pool.figures.ts` marks as the June 2025 edition's):
3
3
  // docs/sources.md, "Heights in the 3D view", says which rule each comes from, and `pool.figures.ts` holds the figures. A plan is in metres and the drawing's frame in feet, with the pool's
4
4
  // top left corner for the origin and the water level for the floor, so every length goes through `feet`. What the rules leave to the maker (the thickness of a stand, a rope, the sides of a
5
5
  // box) is ours and is said so there.
@@ -79,7 +79,7 @@ function swimmingUprights(plan, thin) {
79
79
  }
80
80
  return items;
81
81
  }
82
- /** The two goals of a water polo field, floating on the goal lines: 3 m between the posts, the crossbar's underside 0.9 m over the water (CR 18.3.3), a net 0.30 m deep behind the line. */
82
+ /** The two goals of a water polo field, floating on the goal lines: 3 m between the posts, the crossbar's underside 0.9 m over the water (Part Six 21.6.3.2 and 21.6.4.2), a net 0.30 m deep behind the line (21.6.5). */
83
83
  function poloUprights(plan, thin) {
84
84
  const { goal } = WATER_POLO;
85
85
  const g0 = goal.netClear;
@@ -106,7 +106,7 @@ function poloUprights(plan, thin) {
106
106
  return items;
107
107
  }
108
108
  /**
109
- * A diving well: each springboard 4.88 m long and 0.50 m wide at 1 m or 3 m, its plummet over the water, on a stand behind the wall (Appendix 2); each platform at its height of 1, 3, 5, 7.5 or 10 m with
109
+ * A diving well: each springboard 4.88 m long and 0.50 m wide at 1 m or 3 m, its plummet over the water, on a stand behind the wall (Part Four, Appendix 2); each platform at its height of 1, 3, 5, 7.5 or 10 m with
110
110
  * the length and width of the table, on a tower behind the wall. The stands and towers are ours: the rules give where the plummet is and not what holds the board up.
111
111
  */
112
112
  function diveUprights(plan) {
@@ -144,7 +144,7 @@ function diveUprights(plan) {
144
144
  items.push({ id: "platforms", kind: "platforms", at: [0, feet(plan.width) / 2], height: feet(tall("platform")), planes: platformPlanes });
145
145
  return items;
146
146
  }
147
- /** An artistic swimming area: the judges' platform along the top side, 0.6 m high (CR 27.2.8: at least 0.6 m, not more than 2 m from the edge), and the starting platform 0.7 m high (CR 27.2.7). */
147
+ /** An artistic swimming area: the judges' platform along the top side, 0.6 m high (Part Seven 12.2.5: at least 0.6 m, not more than 2 m from the edge), and the starting platform 0.7 m high (Part Seven 12.2.4). */
148
148
  function artisticUprights(plan) {
149
149
  const { judges, start } = ARTISTIC.drawn;
150
150
  const L = plan.length;
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.5.0";
2
+ export declare const VERSION = "0.6.1";
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.5.0";
2
+ export const VERSION = "0.6.1";
@@ -0,0 +1,2 @@
1
+ export { PARKS_WITHOUT_WALL_HEIGHTS, RULE_IDS, betweenHashes, homeRun, inCrease, inPaint, inPenaltyArea, inRedZone, inRestrictedArea, inSixYardBox, inSlot, isFair, isHomeRun, isThreePointer, onWarningTrack, overWall, wallHeightAt, zoneAt, zoneAtSurface, zoneName, zoneResolver } from "./rules.ts";
2
+ export type { HomeRunOptions, HomeRunVerdict, PlaceLike, RuleId, ZoneAnswer, ZoneOptions } from "./rules.ts";
@@ -0,0 +1,2 @@
1
+ // The rulebook as code: where a point lies on a surface in the sport's own terms (the zone, the area in it, every other area that holds), and the yes-or-no questions a coach asks.
2
+ export { PARKS_WITHOUT_WALL_HEIGHTS, RULE_IDS, betweenHashes, homeRun, inCrease, inPaint, inPenaltyArea, inRedZone, inRestrictedArea, inSixYardBox, inSlot, isFair, isHomeRun, isThreePointer, onWarningTrack, overWall, wallHeightAt, zoneAt, zoneAtSurface, zoneName, zoneResolver } from "./rules.js";