@johnmorrisdotca/kyogijo-stadiums 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/NOTICE.md +182 -2
  3. package/README.md +106 -19
  4. package/dist/annotate.js +1 -1
  5. package/dist/arenas-entry.d.ts +4 -0
  6. package/dist/arenas-entry.js +3 -0
  7. package/dist/arenas.d.ts +125 -0
  8. package/dist/arenas.data.d.ts +2 -0
  9. package/dist/arenas.data.js +3763 -0
  10. package/dist/arenas.js +72 -0
  11. package/dist/athletics/describe.d.ts +8 -0
  12. package/dist/athletics/describe.js +41 -0
  13. package/dist/athletics/events.d.ts +17 -0
  14. package/dist/athletics/events.js +48 -0
  15. package/dist/athletics/facilities.d.ts +80 -0
  16. package/dist/athletics/facilities.js +176 -0
  17. package/dist/athletics/infield.d.ts +79 -0
  18. package/dist/athletics/infield.js +166 -0
  19. package/dist/athletics/layout.d.ts +13 -0
  20. package/dist/athletics/layout.js +98 -0
  21. package/dist/athletics/oval.d.ts +57 -0
  22. package/dist/athletics/oval.js +116 -0
  23. package/dist/athletics/plan.d.ts +97 -0
  24. package/dist/athletics/plan.js +355 -0
  25. package/dist/athletics/races.d.ts +102 -0
  26. package/dist/athletics/races.js +355 -0
  27. package/dist/athletics/steeplechase.d.ts +72 -0
  28. package/dist/athletics/steeplechase.js +196 -0
  29. package/dist/athletics/stencil.d.ts +13 -0
  30. package/dist/athletics/stencil.js +85 -0
  31. package/dist/athletics/track.d.ts +54 -0
  32. package/dist/athletics/track.js +105 -0
  33. package/dist/athletics/types.d.ts +77 -0
  34. package/dist/athletics/types.js +1 -0
  35. package/dist/athletics-entry.d.ts +17 -0
  36. package/dist/athletics-entry.js +11 -0
  37. package/dist/athletics.style.d.ts +16 -0
  38. package/dist/athletics.style.js +51 -0
  39. package/dist/atlas-entry.d.ts +6 -2
  40. package/dist/atlas-entry.js +4 -2
  41. package/dist/atlas.d.ts +117 -23
  42. package/dist/atlas.js +197 -12
  43. package/dist/background.js +2 -1
  44. package/dist/budo/describe.d.ts +13 -0
  45. package/dist/budo/describe.js +120 -0
  46. package/dist/budo/dojo.api.d.ts +64 -0
  47. package/dist/budo/dojo.api.js +46 -0
  48. package/dist/budo/dojo.d.ts +72 -0
  49. package/dist/budo/dojo.js +300 -0
  50. package/dist/budo/judo.d.ts +61 -0
  51. package/dist/budo/judo.js +109 -0
  52. package/dist/budo/karate.d.ts +29 -0
  53. package/dist/budo/karate.js +80 -0
  54. package/dist/budo/kendo.d.ts +18 -0
  55. package/dist/budo/kendo.js +44 -0
  56. package/dist/budo/kimarite.d.ts +19 -0
  57. package/dist/budo/kimarite.js +33 -0
  58. package/dist/budo/kit.d.ts +16 -0
  59. package/dist/budo/kit.js +24 -0
  60. package/dist/budo/kyudo.d.ts +28 -0
  61. package/dist/budo/kyudo.js +66 -0
  62. package/dist/budo/looks.d.ts +28 -0
  63. package/dist/budo/looks.js +58 -0
  64. package/dist/budo/parts.d.ts +32 -0
  65. package/dist/budo/parts.js +10 -0
  66. package/dist/budo/plan.d.ts +64 -0
  67. package/dist/budo/plan.js +139 -0
  68. package/dist/budo/room.d.ts +133 -0
  69. package/dist/budo/room.js +111 -0
  70. package/dist/budo/sumo.d.ts +31 -0
  71. package/dist/budo/sumo.js +71 -0
  72. package/dist/budo/tatami.d.ts +55 -0
  73. package/dist/budo/tatami.js +156 -0
  74. package/dist/budo/types.d.ts +192 -0
  75. package/dist/budo/types.js +2 -0
  76. package/dist/budo-entry.d.ts +22 -0
  77. package/dist/budo-entry.js +13 -0
  78. package/dist/budo.board.d.ts +28 -0
  79. package/dist/budo.board.js +81 -0
  80. package/dist/budo.score.d.ts +18 -0
  81. package/dist/budo.score.js +52 -0
  82. package/dist/budo.style.d.ts +6 -0
  83. package/dist/budo.style.js +67 -0
  84. package/dist/charts-entry.d.ts +6 -1
  85. package/dist/charts-entry.js +3 -0
  86. package/dist/charts.athletics.d.ts +183 -0
  87. package/dist/charts.athletics.js +455 -0
  88. package/dist/charts.baseball.js +1 -1
  89. package/dist/charts.curling.heat.d.ts +24 -0
  90. package/dist/charts.curling.heat.js +116 -0
  91. package/dist/charts.curling.js +14 -3
  92. package/dist/charts.js +4 -4
  93. package/dist/charts.pool.d.ts +67 -0
  94. package/dist/charts.pool.js +76 -0
  95. package/dist/charts.skating.d.ts +204 -0
  96. package/dist/charts.skating.js +426 -0
  97. package/dist/charts.soccer.js +3 -3
  98. package/dist/charts.types.d.ts +63 -1
  99. package/dist/colour.d.ts +4 -4
  100. package/dist/colour.js +4 -4
  101. package/dist/court.d.ts +85 -2
  102. package/dist/court.js +102 -7
  103. package/dist/court.more.d.ts +46 -0
  104. package/dist/court.more.js +219 -0
  105. package/dist/courts.style.d.ts +14 -2
  106. package/dist/courts.style.js +51 -4
  107. package/dist/crowd.d.ts +164 -0
  108. package/dist/crowd.draw.d.ts +17 -0
  109. package/dist/crowd.draw.js +204 -0
  110. package/dist/crowd.js +480 -0
  111. package/dist/crowd.park.d.ts +19 -0
  112. package/dist/crowd.park.js +153 -0
  113. package/dist/curling.stats.d.ts +271 -0
  114. package/dist/curling.stats.js +301 -0
  115. package/dist/draw.d.ts +16 -0
  116. package/dist/draw.js +462 -54
  117. package/dist/element-entry.d.ts +3 -1
  118. package/dist/element-entry.js +2 -1
  119. package/dist/element.d.ts +13 -2
  120. package/dist/element.js +79 -4
  121. package/dist/emblems-source.d.ts +50 -0
  122. package/dist/emblems-source.js +56 -0
  123. package/dist/eras.d.ts +3 -1
  124. package/dist/eras.js +8 -3
  125. package/dist/features.js +204 -0
  126. package/dist/field.data.d.ts +16 -0
  127. package/dist/field.data.js +50 -0
  128. package/dist/fields.kit.d.ts +2 -2
  129. package/dist/fields.types.d.ts +24 -4
  130. package/dist/formations.js +18 -7
  131. package/dist/formations.sports.d.ts +8 -0
  132. package/dist/formations.sports.js +251 -0
  133. package/dist/frames.providers.js +71 -8
  134. package/dist/geojson.js +3 -0
  135. package/dist/geometry-entry.d.ts +2 -1
  136. package/dist/geometry-entry.js +2 -1
  137. package/dist/geometry.d.ts +20 -0
  138. package/dist/geometry.js +78 -1
  139. package/dist/grounds.d.ts +4 -0
  140. package/dist/grounds.data.js +12 -0
  141. package/dist/halls.d.ts +65 -0
  142. package/dist/halls.js +122 -0
  143. package/dist/hardwood.d.ts +31 -0
  144. package/dist/hardwood.js +157 -0
  145. package/dist/heat.js +21 -13
  146. package/dist/heat.types.d.ts +5 -0
  147. package/dist/heat.zones.d.ts +4 -2
  148. package/dist/heat.zones.js +207 -7
  149. package/dist/homeside.js +24 -0
  150. package/dist/index.d.ts +30 -4
  151. package/dist/index.js +18 -2
  152. package/dist/layers-entry.d.ts +2 -1
  153. package/dist/layers-entry.js +1 -0
  154. package/dist/layers.d.ts +2 -1
  155. package/dist/layers.env.d.ts +12 -1
  156. package/dist/layers.env.js +36 -4
  157. package/dist/layers.js +32 -13
  158. package/dist/layers.types.d.ts +9 -2
  159. package/dist/leagues/athletics.d.ts +63 -0
  160. package/dist/leagues/athletics.js +87 -0
  161. package/dist/leagues/budo.d.ts +196 -0
  162. package/dist/leagues/budo.js +105 -0
  163. package/dist/leagues/courts.d.ts +269 -4
  164. package/dist/leagues/courts.js +155 -1
  165. package/dist/leagues/hockey.js +41 -19
  166. package/dist/leagues/skating.d.ts +145 -0
  167. package/dist/leagues/skating.js +73 -0
  168. package/dist/leagues/sources.js +41 -0
  169. package/dist/leagues/types.d.ts +1 -1
  170. package/dist/leagues-entry.d.ts +4 -2
  171. package/dist/leagues-entry.js +2 -1
  172. package/dist/leagues.d.ts +17 -4
  173. package/dist/leagues.js +246 -5
  174. package/dist/lineup.codes.d.ts +23 -0
  175. package/dist/lineup.codes.js +122 -0
  176. package/dist/logo.d.ts +25 -0
  177. package/dist/logo.js +47 -0
  178. package/dist/look.d.ts +3 -3
  179. package/dist/look.js +39 -6
  180. package/dist/outline.d.ts +1 -1
  181. package/dist/outline.js +46 -4
  182. package/dist/pageant.d.ts +120 -0
  183. package/dist/pageant.draw.d.ts +21 -0
  184. package/dist/pageant.draw.js +171 -0
  185. package/dist/pageant.js +211 -0
  186. package/dist/pageant.style.d.ts +8 -0
  187. package/dist/pageant.style.js +42 -0
  188. package/dist/png.d.ts +8 -1
  189. package/dist/png.js +21 -1
  190. package/dist/pointer-entry.d.ts +2 -0
  191. package/dist/pointer-entry.js +2 -0
  192. package/dist/pointer.d.ts +113 -0
  193. package/dist/pointer.js +151 -0
  194. package/dist/pool.d.ts +176 -0
  195. package/dist/pool.figures.d.ts +306 -0
  196. package/dist/pool.figures.js +148 -0
  197. package/dist/pool.js +304 -0
  198. package/dist/pool.kit.d.ts +48 -0
  199. package/dist/pool.kit.js +141 -0
  200. package/dist/pool.labels.d.ts +20 -0
  201. package/dist/pool.labels.js +258 -0
  202. package/dist/pool.race.d.ts +65 -0
  203. package/dist/pool.race.js +140 -0
  204. package/dist/pool.replay.d.ts +46 -0
  205. package/dist/pool.replay.js +127 -0
  206. package/dist/pool.section.d.ts +13 -0
  207. package/dist/pool.section.js +83 -0
  208. package/dist/pool.style.d.ts +20 -0
  209. package/dist/pool.style.js +169 -0
  210. package/dist/pool.types.d.ts +145 -0
  211. package/dist/pool.types.js +3 -0
  212. package/dist/pool.words.d.ts +13 -0
  213. package/dist/pool.words.js +101 -0
  214. package/dist/ranges.names.js +53 -3
  215. package/dist/replay-entry.d.ts +3 -0
  216. package/dist/replay-entry.js +2 -0
  217. package/dist/restyle.js +1 -1
  218. package/dist/road.draw.js +3 -2
  219. package/dist/road.js +2 -1
  220. package/dist/road.types.d.ts +3 -0
  221. package/dist/rules.d.ts +138 -0
  222. package/dist/rules.js +511 -0
  223. package/dist/rules.types.d.ts +4 -0
  224. package/dist/rules.types.js +64 -0
  225. package/dist/score.d.ts +3 -0
  226. package/dist/score.js +93 -6
  227. package/dist/sheet.d.ts +3 -0
  228. package/dist/sheet.js +7 -3
  229. package/dist/situation.d.ts +135 -0
  230. package/dist/situation.draw.d.ts +34 -0
  231. package/dist/situation.draw.js +202 -0
  232. package/dist/situation.js +337 -0
  233. package/dist/situation.style.d.ts +2 -0
  234. package/dist/situation.style.js +45 -0
  235. package/dist/situation.types.d.ts +77 -0
  236. package/dist/situation.types.js +3 -0
  237. package/dist/situation.widgets.d.ts +39 -0
  238. package/dist/situation.widgets.js +129 -0
  239. package/dist/skating/describe.d.ts +7 -0
  240. package/dist/skating/describe.js +44 -0
  241. package/dist/skating/events.d.ts +24 -0
  242. package/dist/skating/events.js +47 -0
  243. package/dist/skating/figure.d.ts +24 -0
  244. package/dist/skating/figure.js +37 -0
  245. package/dist/skating/long.d.ts +54 -0
  246. package/dist/skating/long.js +111 -0
  247. package/dist/skating/parts.d.ts +43 -0
  248. package/dist/skating/parts.js +40 -0
  249. package/dist/skating/plan.d.ts +55 -0
  250. package/dist/skating/plan.js +197 -0
  251. package/dist/skating/short.d.ts +47 -0
  252. package/dist/skating/short.js +89 -0
  253. package/dist/skating/track.d.ts +66 -0
  254. package/dist/skating/track.js +94 -0
  255. package/dist/skating/types.d.ts +57 -0
  256. package/dist/skating/types.js +3 -0
  257. package/dist/skating-entry.d.ts +17 -0
  258. package/dist/skating-entry.js +11 -0
  259. package/dist/skating.style.d.ts +4 -0
  260. package/dist/skating.style.js +24 -0
  261. package/dist/strings.d.ts +470 -0
  262. package/dist/strings.js +674 -13
  263. package/dist/style.d.ts +2 -0
  264. package/dist/style.js +19 -2
  265. package/dist/surfaces/artistic.d.ts +9 -0
  266. package/dist/surfaces/artistic.js +94 -0
  267. package/dist/surfaces/athletics.d.ts +16 -0
  268. package/dist/surfaces/athletics.js +132 -0
  269. package/dist/surfaces/baseball.js +17 -0
  270. package/dist/surfaces/basketball.floor.d.ts +27 -0
  271. package/dist/surfaces/basketball.floor.js +83 -0
  272. package/dist/surfaces/basketball.gear.d.ts +27 -0
  273. package/dist/surfaces/basketball.gear.js +90 -0
  274. package/dist/surfaces/basketball.js +44 -22
  275. package/dist/surfaces/budo.d.ts +6 -0
  276. package/dist/surfaces/budo.js +104 -0
  277. package/dist/surfaces/curling.d.ts +16 -0
  278. package/dist/surfaces/curling.fold.d.ts +67 -0
  279. package/dist/surfaces/curling.fold.js +107 -0
  280. package/dist/surfaces/curling.js +37 -3
  281. package/dist/surfaces/dive.d.ts +9 -0
  282. package/dist/surfaces/dive.js +128 -0
  283. package/dist/surfaces/field.d.ts +7 -2
  284. package/dist/surfaces/field.js +15 -9
  285. package/dist/surfaces/football.js +7 -0
  286. package/dist/surfaces/hockey.js +4 -2
  287. package/dist/surfaces/net.d.ts +2 -1
  288. package/dist/surfaces/net.js +160 -15
  289. package/dist/surfaces/polo.d.ts +12 -0
  290. package/dist/surfaces/polo.js +159 -0
  291. package/dist/surfaces/pool.d.ts +27 -0
  292. package/dist/surfaces/pool.js +198 -0
  293. package/dist/surfaces/skating.d.ts +6 -0
  294. package/dist/surfaces/skating.js +62 -0
  295. package/dist/surfaces/soccer.js +2 -1
  296. package/dist/surfaces/surface.d.ts +76 -0
  297. package/dist/surfaces/venue.d.ts +15 -0
  298. package/dist/surfaces/venue.js +95 -0
  299. package/dist/target-entry.d.ts +2 -0
  300. package/dist/target-entry.js +2 -0
  301. package/dist/target.d.ts +200 -0
  302. package/dist/target.js +525 -0
  303. package/dist/team.common.d.ts +1 -1
  304. package/dist/team.common.js +3 -3
  305. package/dist/team.formation.d.ts +3 -1
  306. package/dist/team.formation.js +3 -3
  307. package/dist/team.layers.d.ts +2 -2
  308. package/dist/team.layers.js +4 -1
  309. package/dist/team.lineup.d.ts +27 -0
  310. package/dist/team.lineup.js +207 -0
  311. package/dist/team.network.js +6 -5
  312. package/dist/team.shape.js +5 -5
  313. package/dist/team.types.d.ts +40 -1
  314. package/dist/tenants.d.ts +77 -0
  315. package/dist/tenants.data.d.ts +8 -0
  316. package/dist/tenants.data.js +1793 -0
  317. package/dist/tenants.js +46 -0
  318. package/dist/turn-entry.d.ts +12 -0
  319. package/dist/turn-entry.js +7 -0
  320. package/dist/turn.camera.d.ts +116 -0
  321. package/dist/turn.camera.js +184 -0
  322. package/dist/turn.dom.d.ts +40 -0
  323. package/dist/turn.dom.js +353 -0
  324. package/dist/turn.kit.d.ts +86 -0
  325. package/dist/turn.kit.js +110 -0
  326. package/dist/turn.markup.d.ts +48 -0
  327. package/dist/turn.markup.js +108 -0
  328. package/dist/turn.model.d.ts +78 -0
  329. package/dist/turn.model.js +91 -0
  330. package/dist/turn.style.d.ts +5 -0
  331. package/dist/turn.style.js +56 -0
  332. package/dist/types.d.ts +221 -20
  333. package/dist/units.d.ts +1 -1
  334. package/dist/units.js +22 -1
  335. package/dist/uprights.board.d.ts +17 -0
  336. package/dist/uprights.board.js +59 -0
  337. package/dist/uprights.budo.d.ts +17 -0
  338. package/dist/uprights.budo.js +87 -0
  339. package/dist/uprights.d.ts +37 -0
  340. package/dist/uprights.fields.d.ts +32 -0
  341. package/dist/uprights.fields.js +172 -0
  342. package/dist/uprights.goal.d.ts +49 -0
  343. package/dist/uprights.goal.js +69 -0
  344. package/dist/uprights.js +169 -0
  345. package/dist/uprights.more.d.ts +7 -0
  346. package/dist/uprights.more.js +183 -0
  347. package/dist/uprights.net.d.ts +12 -0
  348. package/dist/uprights.net.js +109 -0
  349. package/dist/uprights.netkit.d.ts +28 -0
  350. package/dist/uprights.netkit.js +67 -0
  351. package/dist/uprights.pools.d.ts +8 -0
  352. package/dist/uprights.pools.js +190 -0
  353. package/dist/uprights.rinks.d.ts +27 -0
  354. package/dist/uprights.rinks.js +106 -0
  355. package/dist/uprights.track.d.ts +50 -0
  356. package/dist/uprights.track.js +246 -0
  357. package/dist/version.d.ts +1 -1
  358. package/dist/version.js +1 -1
  359. package/dist/zones-entry.d.ts +2 -0
  360. package/dist/zones-entry.js +2 -0
  361. package/package.json +56 -3
@@ -0,0 +1,355 @@
1
+ // What an athletics surface is for a set of options: the track's geometry, the infield, the races and the field events marked, and where each can be cropped to, all as plain data in metres, in
2
+ // the frame of track.ts (the origin the middle of the track, x along the straights, y down the page). Kept apart from the drawing so that the outline, the description, the ranges, the replay,
3
+ // the tests and the drawing agree: a test reads a figure from the plan, not from the markup. Nothing here draws.
4
+ import { lengthUnit } from "../common.js";
5
+ import { num } from "../format.js";
6
+ import { ATHLETICS_LEAGUES, WA_SHORT_TRACK, WA_STANDARD_TRACK } from "../leagues/athletics.js";
7
+ import { chooseLeague } from "../leagues.js";
8
+ import { groundById } from "../grounds.js";
9
+ import { soccerSpec } from "../surfaces/soccer-spec.js";
10
+ import { FIELD_EVENT_IDS, RACES, RACE_IDS, raceById } from "./events.js";
11
+ import { infieldParts, jumpStrip, ARENA } from "./infield.js";
12
+ import { addParts, emptyParts, indoorStraightOf, indoorStraightParts, outerRadius, ovalParts, stadiumD, straightOf } from "./oval.js";
13
+ import { layoutOf } from "./layout.js";
14
+ import { markRace, startPivot, startPoints } from "./races.js";
15
+ import { parseRange } from "../ranges.names.js";
16
+ import { steeplechaseParts, waterPitBox } from "./steeplechase.js";
17
+ import { laneEdge } from "./track.js";
18
+ const isAthleticsLeague = (l) => typeof l === "object" && l !== null && l.family === "athletics";
19
+ /** The league a set of options asks for: the outdoor standard track, or the covered one. */
20
+ export function athleticsLeague(o) {
21
+ const { league } = chooseLeague(o);
22
+ return isAthleticsLeague(league) ? league : ATHLETICS_LEAGUES[0];
23
+ }
24
+ const TRACKS = ["red", "blue", "terracotta", "green"];
25
+ const INFIELDS = ["pitch", "grass", "none"];
26
+ /** A whole number of lanes within a range, or the drawn one. @throws RangeError for a number outside the range the rules allow. */
27
+ function lanesIn(asked, range, drawn, label) {
28
+ if (asked === undefined)
29
+ return drawn;
30
+ if (typeof asked !== "number" || !Number.isInteger(asked))
31
+ throw new RangeError(`${label} must be a whole number`);
32
+ if (asked < range.min || asked > range.max)
33
+ throw new RangeError(`${label} of ${asked} is outside what the rules allow: ${range.min} to ${range.max}`);
34
+ return asked;
35
+ }
36
+ /** The field events the options ask for: all of them, none, or a list. @throws Error for a name that is not a field event. */
37
+ export function eventsOf(style) {
38
+ const asked = style?.events;
39
+ if (asked === undefined || asked === true)
40
+ return [...FIELD_EVENT_IDS];
41
+ if (asked === false)
42
+ return [];
43
+ if (!Array.isArray(asked))
44
+ throw new TypeError("athletics.events is true, false or a list of field events");
45
+ for (const e of asked)
46
+ if (!FIELD_EVENT_IDS.includes(e))
47
+ throw new Error(`Unknown field event "${String(e)}": use one of ${FIELD_EVENT_IDS.join(", ")}`);
48
+ return FIELD_EVENT_IDS.filter((e) => asked.includes(e));
49
+ }
50
+ /** The races the options ask to be marked. @throws Error for a name that is not a race. */
51
+ export function racesOf(style, indoor = false) {
52
+ const asked = style?.races;
53
+ if (asked === undefined)
54
+ return [];
55
+ const here = (r) => (indoor ? INDOOR_RACES.includes(r.id) : r.indoor !== true);
56
+ if (asked === "all")
57
+ return RACES.filter(here);
58
+ if (!Array.isArray(asked))
59
+ throw new TypeError('athletics.races is "all" or a list of races');
60
+ for (const r of asked)
61
+ if (raceById(r) === undefined)
62
+ throw new Error(`Unknown race "${String(r)}": use one of ${RACE_IDS.join(", ")}`);
63
+ const list = RACES.filter((r) => asked.includes(r.id));
64
+ for (const r of list)
65
+ if (!here(r))
66
+ throw new RangeError(indoor ? `The covered 200 m track does not mark the ${r.id}: use one of ${INDOOR_RACES.join(", ")}` : `The ${r.id} is run on the covered track: the 400 m track marks the other races`);
67
+ return list;
68
+ }
69
+ /** The races the covered track marks: the 60 m and its hurdles on the infield straight, and the races of 200 m to 5000 m on the oval. */
70
+ export const INDOOR_RACES = Object.freeze(["60m", "60m-hurdles", "200m", "400m", "800m", "1000m", "1500m", "mile", "3000m", "5000m"]);
71
+ /** Round a box out by `m`. */
72
+ const grow = (b, m) => ({ x0: b.x0 - m, y0: b.y0 - m, x1: b.x1 + m, y1: b.y1 + m });
73
+ const union = (boxes) => ({ x0: Math.min(...boxes.map((b) => b.x0)), y0: Math.min(...boxes.map((b) => b.y0)), x1: Math.max(...boxes.map((b) => b.x1)), y1: Math.max(...boxes.map((b) => b.y1)) });
74
+ /** The lighter bands of the mown turf, aligned to a pitch of 105 m in 19 bands, so that the grass round a pitch is mown as the pitch is (the soccer builder's `bands`). */
75
+ function turfBands(x0, x1, y0, y1) {
76
+ const band = 105 / 19;
77
+ const left = -52.5;
78
+ let d = "";
79
+ for (let i = -3; i < 22; i += 1) {
80
+ if (Math.abs(i) % 2 !== 1)
81
+ continue;
82
+ const a = left + i * band;
83
+ const b = a + band;
84
+ if (b <= x0 || a >= x1)
85
+ continue;
86
+ d += `M${num(Math.max(a, x0))} ${num(y0)}H${num(Math.min(b, x1))}V${num(y1)}H${num(Math.max(a, x0))}Z`;
87
+ }
88
+ return d;
89
+ }
90
+ /** The turf of the standard stadium: the oval's inside, cut off at each end where the synthetic surface of the segments begins (Marking Plan; Manual 2.2.1.1). */
91
+ function turfOf(o) {
92
+ const S = o.straight;
93
+ const R = o.radius;
94
+ const xg = S / 2 + ARENA.turf;
95
+ const yc = Math.sqrt(R * R - ARENA.turf * ARENA.turf);
96
+ const d = `M${num(-xg)} ${num(-yc)}A${num(R)} ${num(R)} 0 0 1 ${num(-S / 2)} ${num(-R)}H${num(S / 2)}A${num(R)} ${num(R)} 0 0 1 ${num(xg)} ${num(-yc)}V${num(yc)}A${num(R)} ${num(R)} 0 0 1 ${num(S / 2)} ${num(R)}H${num(-S / 2)}A${num(R)} ${num(R)} 0 0 1 ${num(-xg)} ${num(yc)}Z`;
97
+ return { d };
98
+ }
99
+ /** The number of lanes of the oval the options ask for. */
100
+ export function athleticsLanes(o) {
101
+ const fig = athleticsLeague(o).dimensions;
102
+ return lanesIn(o.athletics?.lanes, fig.lanes, fig.lanes.drawn, "The number of lanes");
103
+ }
104
+ /** The standard track and everything the options ask to be marked on it, as a plan. */
105
+ export function athleticsPlan(o) {
106
+ const league = athleticsLeague(o);
107
+ const fig = league.dimensions;
108
+ const indoor = fig.banking !== null;
109
+ const style = o.athletics;
110
+ const lanes = lanesIn(style?.lanes, fig.lanes, fig.lanes.drawn, "The number of lanes");
111
+ const straightLanes = lanesIn(style?.straightLanes, fig.straightLanes, Math.max(lanes, fig.straightLanes.min), "The number of lanes of the straight");
112
+ const track = style?.track === undefined ? "red" : style.track;
113
+ if (!TRACKS.includes(track))
114
+ throw new Error(`Unknown track look "${String(track)}": use one of ${TRACKS.join(", ")}`);
115
+ const infield = style?.infield === undefined ? (indoor ? "none" : "pitch") : style.infield;
116
+ if (!INFIELDS.includes(infield))
117
+ throw new Error(`Unknown infield "${String(infield)}": use one of ${INFIELDS.join(", ")}`);
118
+ const layoutEvent = style?.layout;
119
+ if (layoutEvent !== undefined && !FIELD_EVENT_IDS.includes(layoutEvent))
120
+ throw new Error(`Unknown field event "${String(layoutEvent)}": use one of ${FIELD_EVENT_IDS.join(", ")}`);
121
+ const events = indoor ? [] : eventsOf(style);
122
+ const races = racesOf(style, indoor);
123
+ const startRace = style?.start === undefined ? "400m" : style.start;
124
+ if (raceById(startRace) === undefined)
125
+ throw new Error(`Unknown race "${String(startRace)}": use one of ${RACE_IDS.join(", ")}`);
126
+ const wj = style?.waterJump === undefined ? "inside" : style.waterJump;
127
+ if (wj !== "inside" && wj !== "outside" && wj !== "none")
128
+ throw new Error(`Unknown water jump "${String(wj)}": use inside, outside or none`);
129
+ const numbers = style?.numbers !== false;
130
+ const parsed = o.range === undefined ? undefined : parseRange({ sport: "athletics", league: o.league, range: o.range });
131
+ const closeUp = parsed?.kind === "name" && parsed.name === "start";
132
+ const oval = { straight: fig.straight, radius: fig.radius, lane: fig.lane, lanes, line: fig.line, inner: fig.inner, outer: fig.outer };
133
+ if (layoutEvent !== undefined)
134
+ return layoutPlan(layoutEvent, league, oval, lanes, track, numbers);
135
+ const R = oval.radius;
136
+ const S = oval.straight;
137
+ const Rout = outerRadius(oval);
138
+ const longest = indoor ? 60 : 110;
139
+ const straight = indoor ? indoorStraightOf(oval, fig, straightLanes, 60) : straightOf(oval, fig, straightLanes, longest);
140
+ const parts = emptyParts();
141
+ const D = turfOf(oval);
142
+ // The ground inside the track: the synthetic surface of the segments, and the turf between them (Marking Plan).
143
+ addParts(parts, ovalParts(oval, fig, indoor ? undefined : straight, numbers, indoor ? undefined : D.d));
144
+ if (indoor) {
145
+ // The covered track's bends are banked (Rule 43): shaded, as a plan shows a slope.
146
+ const rr = Rout;
147
+ parts.areas.push({ id: "banking-east", feature: "banking", cls: "ky-ath-bank", d: `M${num(S / 2)} ${num(-rr)}A${num(rr)} ${num(rr)} 0 0 1 ${num(S / 2)} ${num(rr)}V${num(R)}A${num(R)} ${num(R)} 0 0 0 ${num(S / 2)} ${num(-R)}Z` }, { id: "banking-west", feature: "banking", cls: "ky-ath-bank", d: `M${num(-S / 2)} ${num(rr)}A${num(rr)} ${num(rr)} 0 0 1 ${num(-S / 2)} ${num(-rr)}V${num(-R)}A${num(R)} ${num(R)} 0 0 0 ${num(-S / 2)} ${num(R)}Z` });
148
+ addParts(parts, indoorStraightParts(straight, numbers, fig.line));
149
+ }
150
+ const turf = infield === "none" || indoor ? undefined : { d: D.d, stripes: turfBands(-S / 2 - ARENA.turf, S / 2 + ARENA.turf, -R, R) };
151
+ // The pitch inside the track: the options', else the size the ground's article publishes, else the international 105 by 68 m.
152
+ const ground = groundById(o.ground);
153
+ const groundPitch = ground === undefined || ground.pitchSize === "default" ? undefined : { ...ground.pitch, unit: "m" };
154
+ const spec = soccerSpec(o.athletics?.pitch ?? o.pitch ?? groundPitch, "m");
155
+ let pitch = infield === "pitch" && !indoor ? { spec, x: -spec.length / 2, y: -spec.width / 2 } : undefined;
156
+ void lengthUnit;
157
+ // The races: the starts and marks of each.
158
+ const raceMarks = [];
159
+ const ctx = { oval, fig, lanes, straight, numbers, numberHeight: 0.95 };
160
+ const sc = races.filter((r) => r.kind === "steeplechase");
161
+ const raceIds = new Set(races.map((r) => r.id));
162
+ if (!raceIds.has(startRace) && (o.athletics?.start !== undefined || closeUp))
163
+ raceIds.add(startRace);
164
+ for (const race of RACES) {
165
+ if (!raceIds.has(race.id))
166
+ continue;
167
+ if (race.kind === "steeplechase")
168
+ continue;
169
+ raceMarks.push(markRace(ctx, race));
170
+ }
171
+ let waterJump;
172
+ if (sc.length > 0 && wj !== "none") {
173
+ waterJump = wj;
174
+ let first = true;
175
+ for (const race of sc) {
176
+ const made = steeplechaseParts(ctx, wj, race, first);
177
+ first = false;
178
+ addParts(parts, made.parts);
179
+ }
180
+ }
181
+ for (const r of raceMarks)
182
+ addParts(parts, r.parts);
183
+ let startView;
184
+ if (closeUp) {
185
+ const marks = raceMarks.find((r) => r.race.id === startRace);
186
+ if (marks !== undefined) {
187
+ const pivot = startPivot(oval, marks);
188
+ const pts = startPoints(oval, marks, straight);
189
+ const c = Math.cos(pivot.heading);
190
+ const sn = Math.sin(pivot.heading);
191
+ const rot = pts.map(([x, y]) => [(x - pivot.x) * c + (y - pivot.y) * sn, -(x - pivot.x) * sn + (y - pivot.y) * c]);
192
+ const xs = rot.map((p) => p[0]);
193
+ const ys = rot.map((p) => p[1]);
194
+ const side = marks.race.kind === "straight" ? 0.8 : 3;
195
+ const win = { x0: Math.min(...xs) - (marks.race.kind === "straight" ? 7 : 9), x1: Math.max(...xs) + (marks.race.kind === "straight" ? 7 : 9), y0: Math.min(...ys) - side, y1: Math.max(...ys) + side };
196
+ startView = { pivot, window: win };
197
+ // The pitch is left out of a close-up that does not reach it.
198
+ if (pitch !== undefined) {
199
+ const corners = [[win.x0, win.y0], [win.x1, win.y0], [win.x1, win.y1], [win.x0, win.y1]].map(([x, y]) => [pivot.x + x * c - y * sn, pivot.y + x * sn + y * c]);
200
+ const gx0 = Math.min(...corners.map((p) => p[0]));
201
+ const gx1 = Math.max(...corners.map((p) => p[0]));
202
+ const gy0 = Math.min(...corners.map((p) => p[1]));
203
+ const gy1 = Math.max(...corners.map((p) => p[1]));
204
+ if (gx1 < pitch.x || gx0 > pitch.x + pitch.spec.length || gy1 < pitch.y || gy0 > pitch.y + pitch.spec.width)
205
+ pitch = undefined;
206
+ }
207
+ // On a bend the numbers painted on the track would be specks beside the fan of the starts: they become tags that read at the size of the view.
208
+ if (marks.race.kind !== "straight") {
209
+ // A tag's disc is 1.56 times its figure's height across and must sit inside a lane (1.22 m): the figure is 0.66 m, a disc of 1.03 m.
210
+ const tag = 0.66;
211
+ for (const d of marks.parts.digits) {
212
+ if (d.id !== "start-number")
213
+ continue;
214
+ const back = tag * 0.78 + 0.35;
215
+ const at = d.from === undefined ? [d.x, d.y] : [d.from[0] - d.from[2] * back, d.from[1] - d.from[3] * back];
216
+ Object.assign(d, { h: tag, tag: true, x: at[0], y: at[1] });
217
+ }
218
+ }
219
+ }
220
+ }
221
+ // The field events.
222
+ const inf = indoor ? { parts: emptyParts(), facilities: [] } : infieldParts(oval, new Set(events));
223
+ addParts(parts, inf.parts);
224
+ const box = { x0: -S / 2 - Rout, y0: -Rout, x1: S / 2 + Rout, y1: Rout };
225
+ let extent = grow(box, 1);
226
+ const edgeStraight = R + straight.lanes * straight.lane;
227
+ if (!indoor)
228
+ extent = union([extent, { x0: straight.x0, y0: -Rout, x1: straight.x1, y1: edgeStraight }]);
229
+ if (events.includes("long-jump") || events.includes("triple-jump"))
230
+ extent = union([extent, jumpStrip()]);
231
+ if (events.includes("javelin-throw"))
232
+ extent = union([extent, ...inf.facilities.filter((f) => f.event === "javelin-throw").map((f) => f.box)].map((b) => ({ x0: Math.max(b.x0, -S / 2 - Rout - 6), y0: b.y0, x1: Math.min(b.x1, S / 2 + Rout + 6), y1: b.y1 })));
233
+ extent = grow(extent, 0);
234
+ const plan = {
235
+ sport: "athletics",
236
+ league: league.id,
237
+ indoor,
238
+ figures: fig,
239
+ oval,
240
+ lanes,
241
+ straight,
242
+ track,
243
+ infield,
244
+ box,
245
+ extent,
246
+ areas: parts.areas,
247
+ shapes: parts.shapes,
248
+ digits: parts.digits,
249
+ turf,
250
+ pitch,
251
+ facilities: inf.facilities,
252
+ races: raceMarks,
253
+ waterJump,
254
+ events,
255
+ lineWidth: fig.line,
256
+ drawnLine: Math.max(fig.line, (box.x1 - box.x0) * 0.0009),
257
+ ranges: {},
258
+ marks: [],
259
+ start: startRace,
260
+ startView,
261
+ numbers,
262
+ };
263
+ plan.ranges = rangesOf(plan);
264
+ plan.marks.push(...marksOf(plan));
265
+ return plan;
266
+ }
267
+ /** The plan of one field event alone, at a large scale: its facility in its own frame (the athlete throws or jumps to the right from the origin), with the box round it as the playing surface. */
268
+ function layoutPlan(event, league, oval, lanes, track, numbers) {
269
+ const lay = layoutOf(event);
270
+ const box = grow(lay.box, 0);
271
+ const width = box.x1 - box.x0;
272
+ const plan = {
273
+ sport: "athletics",
274
+ league: league.id,
275
+ indoor: false,
276
+ figures: league.dimensions,
277
+ oval,
278
+ lanes,
279
+ straight: undefined,
280
+ track,
281
+ infield: "none",
282
+ box,
283
+ extent: box,
284
+ areas: lay.parts.areas,
285
+ shapes: lay.parts.shapes.map((sh) => (sh.kind !== "dot" && sh.kind !== "bar" && sh.cls === "ky-ath-s" ? { ...sh, least: Math.max(0.07, width * 0.0011) } : sh)),
286
+ digits: [],
287
+ turf: undefined,
288
+ pitch: undefined,
289
+ facilities: [lay.facility],
290
+ races: [],
291
+ waterJump: undefined,
292
+ events: [event],
293
+ lineWidth: 0.05,
294
+ drawnLine: Math.max(0.05, width * 0.0016),
295
+ ranges: {},
296
+ marks: lay.marks,
297
+ start: "400m",
298
+ startView: undefined,
299
+ numbers,
300
+ layout: event,
301
+ };
302
+ return plan;
303
+ }
304
+ /** The union of the boxes of the facilities of one event on one side of the stadium. */
305
+ function facilityBox(plan, event, side) {
306
+ const list = plan.facilities.filter((f) => f.event === event && (side === "all" || (side === "left" ? f.x < 0 : f.x >= 0)));
307
+ return list.length === 0 ? undefined : grow(union(list.map((f) => f.box)), 1.5);
308
+ }
309
+ /** The named ranges of the plan, in metres. */
310
+ function rangesOf(plan) {
311
+ const o = plan.oval;
312
+ const S = o.straight;
313
+ const R = o.radius;
314
+ const Rout = plan.box.y1;
315
+ const out = {};
316
+ const outer = plan.indoor || plan.straight === undefined;
317
+ const edge = outer ? R + plan.lanes * o.lane : R + plan.straight.lanes * plan.straight.lane;
318
+ out.bend = { left: { x0: -S / 2 - Rout, y0: -Rout, x1: -S / 2, y1: Rout }, right: { x0: S / 2, y0: -Rout, x1: S / 2 + Rout, y1: Rout } };
319
+ out["home-straight"] = outer ? { x0: -S / 2, y0: R - 1, x1: S / 2, y1: edge + 1 } : { x0: plan.straight.x0, y0: R - 1, x1: plan.straight.x1, y1: edge + 1 };
320
+ out["back-straight"] = { x0: -S / 2, y0: -Rout, x1: S / 2, y1: -R + 1 };
321
+ out.finish = { x0: S / 2 - 20, y0: R - 2, x1: S / 2 + 10, y1: edge + 2 };
322
+ out.infield = { x0: -S / 2 - R, y0: -R, x1: S / 2 + R, y1: R };
323
+ for (const e of FIELD_EVENT_IDS) {
324
+ const left = facilityBox(plan, e, "left");
325
+ const right = facilityBox(plan, e, "right");
326
+ if (left !== undefined && right !== undefined)
327
+ out[e] = { left, right };
328
+ else if (left !== undefined || right !== undefined)
329
+ out[e] = (left ?? right);
330
+ }
331
+ // The water jump's pit, wherever the jump is (the steeplechase's lap is drawn only when a steeplechase is marked): the inside one by default.
332
+ const wj = plan.waterJump ?? "inside";
333
+ const pit = waterPitBox(plan.oval, wj);
334
+ out["water-jump"] = grow(pit, 5);
335
+ void laneEdge;
336
+ void stadiumD;
337
+ return out;
338
+ }
339
+ /** The parts of the plan to label. */
340
+ function marksOf(plan) {
341
+ const o = plan.oval;
342
+ const S = o.straight;
343
+ const R = o.radius;
344
+ const marks = [
345
+ { id: "finish-line", x: S / 2, y: R + 0.6 * o.lane, side: "bottom" },
346
+ { id: "kerb", x: -S / 4, y: -R, side: "top" },
347
+ { id: "lane-lines", x: -S / 4, y: R + o.lane, side: "bottom" },
348
+ { id: "bend", x: S / 2 + R * 0.7071 + 2, y: -R * 0.7071, side: "top" },
349
+ { id: "straight", x: 0, y: -R - 3, side: "top" },
350
+ ];
351
+ if (plan.pitch !== undefined)
352
+ marks.push({ id: "infield-pitch", x: 0, y: -plan.pitch.spec.width / 2, side: "top" });
353
+ return marks;
354
+ }
355
+ export { WA_SHORT_TRACK, WA_STANDARD_TRACK };
@@ -0,0 +1,102 @@
1
+ import { type AthleticsFigures } from "../leagues/athletics.ts";
2
+ import { type Parts, type StraightPlan } from "./oval.ts";
3
+ import { type Oval } from "./track.ts";
4
+ import type { RaceInfo } from "./types.ts";
5
+ /** Where a lane's start line stands. */
6
+ export interface StartPlacement {
7
+ lane: number;
8
+ /** The distance of the start along the lane's running line from the finish line, forward. */
9
+ s: number;
10
+ /** Where the lane's running line crosses the start line, and the way the athlete faces there. */
11
+ x: number;
12
+ y: number;
13
+ heading: number;
14
+ /** How far ahead of lane 1's start it is, on the running line. */
15
+ stagger: number;
16
+ }
17
+ /** A point of the plan. */
18
+ export type Pt = [number, number];
19
+ /** One race's marks as data, for the description, the replay and the tests. */
20
+ export interface RaceMarks {
21
+ race: RaceInfo;
22
+ /** The distance of lane 1's start line from the finish line along its running line: the least that is round whole laps to the distance of the race. */
23
+ s1: number;
24
+ /** The start line of each lane (a staggered start's, or a straight's: `x` is then the line's). Empty for a curved start. */
25
+ starts: StartPlacement[];
26
+ /** The curved start line from the kerb out across the track. */
27
+ curve?: Pt[];
28
+ /** The outer group's curved start line. */
29
+ group?: Pt[];
30
+ /** The breakline, from lane 2's inside to the outer edge. */
31
+ breakline?: Pt[];
32
+ /** The takeover zones: for each lane and change, where the zone starts and ends and the scratch line stands, as distances along the lane. */
33
+ zones?: {
34
+ lane: number;
35
+ change: number;
36
+ start: number;
37
+ scratch: number;
38
+ end: number;
39
+ }[];
40
+ /** The hurdles: for each lane, the distances along its running line (or, on the straight, the x) of the marks. */
41
+ hurdles?: {
42
+ lane: number;
43
+ at: number[];
44
+ }[];
45
+ /** What is drawn. */
46
+ parts: Parts;
47
+ }
48
+ /** What the marks of a race are made in. */
49
+ export interface RaceContext {
50
+ oval: Oval;
51
+ fig: AthleticsFigures;
52
+ /** The oval's lanes that are used, and the straight's. */
53
+ lanes: number;
54
+ straight: StraightPlan | undefined;
55
+ /** True to put the lane numbers on the track behind each start line. */
56
+ numbers: boolean;
57
+ /** The height of those numbers. */
58
+ numberHeight: number;
59
+ }
60
+ /** The unit normal of a heading, pointing out from the middle of the track: to the right of the athlete. */
61
+ export declare const outward: (heading: number) => Pt;
62
+ /** The unit vector of a heading. */
63
+ export declare const ahead: (heading: number) => Pt;
64
+ /** The ends of the line across lane `n` at distance `s` of its running line, from radius `ra` to radius `rb` (measured as the track's radii are: outward from the middle of a bend). */
65
+ export declare function across(o: Oval, n: number, s: number, ra: number, rb: number): [Pt, Pt];
66
+ /** Where a point `back` metres behind `s` on lane `n` is, and the unit vector out from the middle there. */
67
+ export declare function atLane(o: Oval, n: number, s: number, side?: number): {
68
+ x: number;
69
+ y: number;
70
+ heading: number;
71
+ };
72
+ /** The inside and the outside radius of the paintable width of lane `n`: between the line on its left and the line on its right (1.17 m of a 1.22 m lane). */
73
+ export declare function laneSpan(o: Oval, n: number): [number, number];
74
+ /** The distance of lane 1's start from the finish line for a race of `distance` metres: the least start that is a whole number of laps before the finish line, round to the distance (Rule 14.3). */
75
+ export declare function firstStart(o: Oval, distance: number, nominal?: number): number;
76
+ /** The stagger of lane `n` for a race run in lanes round `bends` bends, with the breakline's correction where the race has one (Manual Tables 2.2.1.6a and 2.2.1.6b). */
77
+ export declare function staggerOf(o: Oval, n: number, bends: number, breakline: boolean, fig: AthleticsFigures): number;
78
+ /** Where a curved start line begins on a circle of radius `a`, from its distance `s` along that circle's running line: the bend it is on or runs into, the angle there, and the wire already unwound. */
79
+ export declare function curveOrigin(o: Oval, a: number, s: number): {
80
+ side: "right" | "left";
81
+ f0: number;
82
+ wire: number;
83
+ };
84
+ /**
85
+ * A curved start line, or a breakline, as points: the involute of the circle of radius `a` (see `involutePoint`) that starts where `origin` says, from `rIn` out to `rOut` (radii about the bend's
86
+ * centre; a start line on the straight is the same curve). Where `rIn` is less than `a` a short run in to the kerb along the line across the track joins it.
87
+ */
88
+ export declare function curveAcross(o: Oval, origin: {
89
+ side: "right" | "left";
90
+ f0: number;
91
+ wire: number;
92
+ }, a: number, rIn: number, rOut: number, steps?: number): Pt[];
93
+ /** The marks of one race on the oval or the straight. */
94
+ export declare function markRace(ctx: RaceContext, race: RaceInfo): RaceMarks;
95
+ /** Every point that makes the start of a race: the ends of each lane's start line, the curved lines, the breakline. For a close-up's window. */
96
+ export declare function startPoints(o: Oval, marks: RaceMarks, straight: StraightPlan | undefined): Pt[];
97
+ /** The pivot of a start's close-up: where the view is turned about, and the way the athletes face there. */
98
+ export declare function startPivot(o: Oval, marks: RaceMarks): {
99
+ x: number;
100
+ y: number;
101
+ heading: number;
102
+ };