@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
+ // The marks of the races: where each starts and how its start line is made (a straight line across the lane, a staggered one, a curved one), the breakline of the 800 m, the group starts of
2
+ // the long races, the takeover zones of the relays and the hurdles' marks. The distances are the Track and Field Facilities Manual's (2019 edition, 2.2.1.6, 2.2.3 and Tables 2.2.1.6a, 2.2.1.6b
3
+ // and 2.2.3.1) and Book C2.1's Technical Rules (Rules 14.3, 16.1, 17.5, 22 and 24 in the edition in force from 1 July 2026). Everything is metres, in the frame of track.ts.
4
+ import { line, poly } from "../fields.kit.js";
5
+ import { WA_SHORT_STAGGER_TABLE } from "../leagues/athletics.js";
6
+ import { emptyParts, outerRadius } from "./oval.js";
7
+ import { distanceOn, involutePoint, laneEdge, laneRadius, lap, pose, stagger, breaklineStagger } from "./track.js";
8
+ /** The unit normal of a heading, pointing out from the middle of the track: to the right of the athlete. */
9
+ export const outward = (heading) => [-Math.sin(heading), Math.cos(heading)];
10
+ /** The unit vector of a heading. */
11
+ export const ahead = (heading) => [Math.cos(heading), Math.sin(heading)];
12
+ /** 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). */
13
+ export function across(o, n, s, ra, rb) {
14
+ const r = laneRadius(o, n);
15
+ const p = pose(o, r, s);
16
+ const [nx, ny] = outward(p.heading);
17
+ return [
18
+ [p.x + (ra - r) * nx, p.y + (ra - r) * ny],
19
+ [p.x + (rb - r) * nx, p.y + (rb - r) * ny],
20
+ ];
21
+ }
22
+ /** Where a point `back` metres behind `s` on lane `n` is, and the unit vector out from the middle there. */
23
+ export function atLane(o, n, s, side = 0) {
24
+ const r = laneRadius(o, n);
25
+ const p = pose(o, r + side, s);
26
+ return p;
27
+ }
28
+ /** 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). */
29
+ export function laneSpan(o, n) {
30
+ return [laneEdge(o, n - 1), laneEdge(o, n) - o.line];
31
+ }
32
+ /** 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). */
33
+ export function firstStart(o, distance, nominal) {
34
+ const L = nominal ?? lap(o, 1);
35
+ const k = Math.ceil(distance / L - 1e-9);
36
+ return k * L - distance;
37
+ }
38
+ /** 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). */
39
+ export function staggerOf(o, n, bends, breakline, fig) {
40
+ if (n <= 1)
41
+ return 0;
42
+ if (fig.banking !== null) {
43
+ // The covered track's own table (Marking Plan, 1 January 2026).
44
+ const row = bends === 1 ? WA_SHORT_STAGGER_TABLE["one-bend"] : bends === 3 ? WA_SHORT_STAGGER_TABLE["three-bends"] : WA_SHORT_STAGGER_TABLE["two-bends"];
45
+ return row[n - 2] ?? stagger(o, n, bends);
46
+ }
47
+ return breakline ? (bends - 1) * stagger(o, n, 1) + breaklineStagger(o, n) : stagger(o, n, bends);
48
+ }
49
+ /** 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. */
50
+ export function curveOrigin(o, a, s) {
51
+ const S = o.straight;
52
+ const L = 2 * S + 2 * Math.PI * a;
53
+ const d = ((s % L) + L) % L;
54
+ const bend = Math.PI * a;
55
+ if (d < bend)
56
+ return { side: "right", f0: d / a, wire: 0 };
57
+ if (d < bend + S)
58
+ return { side: "left", f0: 0, wire: bend + S - d };
59
+ if (d <= 2 * bend + S)
60
+ return { side: "left", f0: (d - bend - S) / a, wire: 0 };
61
+ return { side: "right", f0: 0, wire: L - d };
62
+ }
63
+ /** The distance of a point out from the circle of radius `a` of the track, whichever part of the track it is over. */
64
+ function offsetOf(o, a, x, y) {
65
+ const S = o.straight;
66
+ if (x >= S / 2)
67
+ return Math.hypot(x - S / 2, y) - a;
68
+ if (x <= -S / 2)
69
+ return Math.hypot(x + S / 2, y) - a;
70
+ return Math.abs(y) - a;
71
+ }
72
+ /**
73
+ * 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
74
+ * 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.
75
+ */
76
+ export function curveAcross(o, origin, a, rIn, rOut, steps = 30) {
77
+ const w0 = origin.wire - a * origin.f0;
78
+ const at = (f) => involutePoint(o, origin.side, a, w0, f);
79
+ const out = [];
80
+ const f0 = origin.f0;
81
+ if (rIn < a - 1e-9) {
82
+ const p0 = at(f0);
83
+ const nx = origin.side === "right" ? Math.sin(f0) : -Math.sin(f0);
84
+ const ny = origin.side === "right" ? Math.cos(f0) : -Math.cos(f0);
85
+ out.push([p0[0] + (rIn - a) * nx, p0[1] + (rIn - a) * ny]);
86
+ }
87
+ const solve = (d) => {
88
+ let lo = f0;
89
+ let hi = f0 + 2.4;
90
+ for (let i = 0; i < 60; i += 1) {
91
+ const mid = (lo + hi) / 2;
92
+ const [x, y] = at(mid);
93
+ if (offsetOf(o, a, x, y) < d)
94
+ lo = mid;
95
+ else
96
+ hi = mid;
97
+ }
98
+ return (lo + hi) / 2;
99
+ };
100
+ const d0 = Math.max(rIn - a, 0);
101
+ const d1 = rOut - a;
102
+ for (let i = 0; i <= steps; i += 1)
103
+ out.push(at(solve(d0 + ((d1 - d0) * i) / steps)));
104
+ return out;
105
+ }
106
+ const W = 0.05;
107
+ /** A line across a lane at distance `s`, from radius `ra` to `rb`. */
108
+ function crossLine(o, id, feature, n, s, ra, rb, w, cls) {
109
+ const [a, b] = across(o, n, s, ra, rb);
110
+ return line(id, feature, a[0], a[1], b[0], b[1], { w, ...(cls === undefined ? {} : { cls }) });
111
+ }
112
+ /** A takeover zone's line: `len` metres across the lane from its inside line, with a hook of 0.15 m turned 45 degrees, to point into the zone (`into` = +1: ahead of the line). */
113
+ function zoneLine(o, id, cls, n, s, len, into) {
114
+ const [ra] = laneSpan(o, n);
115
+ const r = laneRadius(o, n);
116
+ const p = pose(o, r, s);
117
+ const [fx, fy] = ahead(p.heading);
118
+ const [nx, ny] = outward(p.heading);
119
+ const base = [p.x + (ra - r) * nx, p.y + (ra - r) * ny];
120
+ const tip = [base[0] + len * nx, base[1] + len * ny];
121
+ const k = 0.15 * Math.SQRT1_2;
122
+ return poly(id, "relay-zones", [base, tip, [tip[0] - k * nx + into * k * fx, tip[1] - k * ny + into * k * fy]], { w: W, cls });
123
+ }
124
+ /** A mark in the middle of a lane, `len` long across it, at distance `s`. */
125
+ function middleLine(o, id, feature, n, s, len, cls) {
126
+ const r = laneRadius(o, n);
127
+ const [lo, hi] = laneSpan(o, n);
128
+ const mid = (lo + hi) / 2;
129
+ void r;
130
+ return crossLine(o, id, feature, n, s, mid - len / 2, mid + len / 2, W, cls);
131
+ }
132
+ /** The marks of one race on the oval or the straight. */
133
+ export function markRace(ctx, race) {
134
+ const { oval: o, fig, lanes } = ctx;
135
+ const parts = emptyParts();
136
+ const out = { race, s1: 0, starts: [], parts };
137
+ const S = o.straight;
138
+ const R = o.radius;
139
+ const Rout = outerRadius(o);
140
+ const kind = race.kind;
141
+ const digitAt = (n, s) => {
142
+ if (!ctx.numbers)
143
+ return;
144
+ const p = pose(o, laneRadius(o, n), s - ctx.numberHeight * 1.2);
145
+ const q = pose(o, laneRadius(o, n), s);
146
+ parts.digits.push({ id: "start-number", feature: "lane-numbers", text: String(n), x: p.x, y: p.y, h: ctx.numberHeight, from: [q.x, q.y, Math.cos(q.heading), Math.sin(q.heading)] });
147
+ };
148
+ if (kind === "straight") {
149
+ const st = ctx.straight;
150
+ const sl = st?.lane ?? o.lane;
151
+ const n = st?.lanes ?? lanes;
152
+ const top = st?.y0 ?? R;
153
+ const x = (st?.finish ?? S / 2) - race.distance;
154
+ for (let k = 1; k <= n; k += 1) {
155
+ const y0 = top + (k - 1) * sl;
156
+ const y1 = top + k * sl - W;
157
+ parts.shapes.push(line("start-line", "start-lines", x, y0, x, y1, { w: W }));
158
+ out.starts.push({ lane: k, s: lap(o, 1) - race.distance, x, y: (y0 + y1) / 2, heading: 0, stagger: 0 });
159
+ if (ctx.numbers)
160
+ parts.digits.push({ id: "start-number", feature: "lane-numbers", text: String(k), x: x - ctx.numberHeight * 1.2, y: (y0 + y1) / 2, h: ctx.numberHeight });
161
+ }
162
+ if (race.hurdles === true)
163
+ addStraightHurdles(parts, out, race, st?.finish ?? S / 2, top, sl, n);
164
+ return out;
165
+ }
166
+ // The covered track's running line is 200.000 m by the plan, which counts its banking's transitions; the circle drawn here is a little shorter, so a start is a whole number of nominal laps back.
167
+ const indoor = fig.banking !== null;
168
+ const s1 = firstStart(o, race.distance, indoor ? 200 : undefined);
169
+ out.s1 = s1;
170
+ if (kind === "stagger") {
171
+ const bends = indoor ? 2 : (race.bends ?? 2);
172
+ for (let n = 1; n <= lanes; n += 1) {
173
+ const st = staggerOf(o, n, bends, race.breakline === true, fig);
174
+ const s = s1 + st;
175
+ const p = pose(o, laneRadius(o, n), s);
176
+ out.starts.push({ lane: n, s, x: p.x, y: p.y, heading: p.heading, stagger: st });
177
+ const [ra, rb] = laneSpan(o, n);
178
+ parts.shapes.push(crossLine(o, "start-line", "start-lines", n, s, ra, rb, W));
179
+ digitAt(n, s);
180
+ }
181
+ if (race.breakline === true) {
182
+ const a = laneRadius(o, 1);
183
+ // Outdoors the 800 m's breakline is at the end of the first bend, the involute of the second bend's circle; on the covered track it is at the end of the second bend (Rule 44), the first bend's.
184
+ out.breakline = curveAcross(o, { side: indoor ? "right" : "left", f0: 0, wire: S }, a, laneEdge(o, 1), Rout);
185
+ parts.shapes.push(poly("breakline", "breakline", out.breakline, { w: W, cls: "ky-ath-g" }));
186
+ }
187
+ if (race.legs !== undefined)
188
+ relayZones(ctx, race, out);
189
+ if (race.hurdles === true)
190
+ oval400Hurdles(ctx, race, out);
191
+ return out;
192
+ }
193
+ if (kind === "curved") {
194
+ const a = laneRadius(o, 1);
195
+ const origin = curveOrigin(o, a, s1);
196
+ out.curve = curveAcross(o, origin, a, R, Rout);
197
+ parts.shapes.push(poly("start-line", "start-lines", out.curve, { w: W }));
198
+ // The number of each lane, behind the curved line where it crosses the lane's middle.
199
+ if (ctx.numbers) {
200
+ for (let n = 1; n <= lanes; n += 1) {
201
+ const r = laneRadius(o, n);
202
+ const [lo, hi] = laneSpan(o, n);
203
+ const mid = (lo + hi) / 2;
204
+ const [px, py] = curveAcross(o, origin, a, mid, mid, 1)[0];
205
+ const sMid = distanceOn(o, mid, px, py);
206
+ const p = pose(o, mid, sMid - ctx.numberHeight * 1.2);
207
+ void r;
208
+ const q = pose(o, mid, sMid);
209
+ parts.digits.push({ id: "start-number", feature: "lane-numbers", text: String(n), x: p.x, y: p.y, h: ctx.numberHeight, from: [q.x, q.y, Math.cos(q.heading), Math.sin(q.heading)] });
210
+ }
211
+ }
212
+ // Where each lane's middle crosses the line: for the athletes' places, and the replay's.
213
+ for (let n = 1; n <= lanes; n += 1) {
214
+ const r = laneRadius(o, n);
215
+ const [px, py] = curveAcross(o, origin, a, r, r, 1)[0];
216
+ const sRun = distanceOn(o, r, px, py);
217
+ const p = pose(o, r, sRun);
218
+ out.starts.push({ lane: n, s: sRun, x: px, y: py, heading: p.heading, stagger: sRun - s1 });
219
+ }
220
+ if (race.group === true) {
221
+ // The outer half starts from lane 5 (lane 4 on a six-lane track), a line of its own whose inside is 0.20 m outside the line between lanes 4 and 5 (Rule 17.5.2, Manual 2.2.1.6).
222
+ const gl = lanes <= 6 ? 4 : 5;
223
+ const ag = laneRadius(o, gl);
224
+ const sg = s1 + breaklineStagger(o, gl);
225
+ out.group = curveAcross(o, curveOrigin(o, ag, sg), ag, laneEdge(o, gl - 1), Rout);
226
+ parts.shapes.push(poly("group-start-line", "start-lines", out.group, { w: W }));
227
+ }
228
+ return out;
229
+ }
230
+ return out;
231
+ }
232
+ /** The hurdles' marks of the sprint hurdles on the finish straight (Manual Table 2.2.3.1; Rule 22.2). */
233
+ function addStraightHurdles(parts, out, race, finish, R, sl, lanes) {
234
+ const men = race.id === "110m-hurdles" || race.id === "60m-hurdles";
235
+ const first = men ? 13.72 : 13;
236
+ const between = men ? 9.14 : 8.5;
237
+ const cls = men ? "ky-ath-b" : "ky-ath-y";
238
+ const xs = [];
239
+ // Ten hurdles in 100 and 110 m, five in the covered track's 60 m (Table 2.2.3.1; the Marking Plan for the Standard Short Track).
240
+ const count = race.distance <= 60 ? 5 : 10;
241
+ for (let k = 0; k < count; k += 1)
242
+ xs.push(finish - race.distance + first + between * k);
243
+ out.hurdles = [];
244
+ for (let n = 1; n <= lanes; n += 1) {
245
+ const y0 = R + (n - 1) * sl;
246
+ const y1 = R + n * sl - W;
247
+ for (const x of xs) {
248
+ // Two marks, 0.05 m by 0.10 m, one each side of the lane, on the line the hurdle's nearer face stands on (Rule 22.3).
249
+ parts.shapes.push(line("hurdle-mark", "hurdle-marks", x + 0.025, y0 + W, x + 0.025, y0 + W + 0.1, { w: W, cls }), line("hurdle-mark", "hurdle-marks", x + 0.025, y1 - 0.1, x + 0.025, y1, { w: W, cls }));
250
+ }
251
+ out.hurdles.push({ lane: n, at: xs });
252
+ }
253
+ }
254
+ /** The 400 m hurdles' marks on the oval: ten hurdles in each lane, the first 45 m from the start, then every 35 m (Table 2.2.3.1; Rule 22.2). */
255
+ function oval400Hurdles(ctx, race, out) {
256
+ const o = ctx.oval;
257
+ out.hurdles = [];
258
+ for (const st of out.starts) {
259
+ const at = [];
260
+ for (let k = 0; k < 10; k += 1)
261
+ at.push(st.s + 45 + 35 * k);
262
+ out.hurdles.push({ lane: st.lane, at });
263
+ const [ra, rb] = laneSpan(o, st.lane);
264
+ for (const s of at)
265
+ out.parts.shapes.push(crossLine(o, "hurdle-mark", "hurdle-marks", st.lane, s + 0.025, ra + W, ra + W + 0.1, W, "ky-ath-g"), crossLine(o, "hurdle-mark", "hurdle-marks", st.lane, s + 0.025, rb - 0.1, rb, W, "ky-ath-g"));
266
+ }
267
+ void race;
268
+ }
269
+ /** The takeover zones of a relay: the 4 x 100 m's (30 m long, the scratch line 20 m from the zone's start) and the 4 x 400 m's (20 m long, the scratch line in the middle). Rule 24.3. */
270
+ function relayZones(ctx, race, out) {
271
+ const { oval: o, lanes } = ctx;
272
+ const sprint = race.id === "4x100m";
273
+ const long = sprint ? 30 : 20;
274
+ const before = sprint ? 20 : 10;
275
+ const cls = sprint ? "ky-ath-y" : "ky-ath-b";
276
+ const len = sprint ? 1.1 : 0.8;
277
+ out.zones = [];
278
+ const shapes = out.parts.shapes;
279
+ for (const st of out.starts) {
280
+ const n = st.lane;
281
+ const legs = race.legs;
282
+ let scratch = st.s;
283
+ for (let change = 1; change <= 3; change += 1) {
284
+ scratch += legs[change - 1];
285
+ // A 4 x 400 m's second and third takeovers are not in lanes: they stand across the track either side of the finish line (Manual 2.2.1.6, Marking Plan) and are marked below.
286
+ if (!sprint && change >= 2)
287
+ continue;
288
+ const zs = scratch - before;
289
+ const ze = zs + long;
290
+ out.zones.push({ lane: n, change, start: zs, scratch, end: ze });
291
+ shapes.push(zoneLine(o, "zone-start", cls, n, zs, len, 1), zoneLine(o, "zone-end", cls, n, ze, len, -1));
292
+ shapes.push(middleLine(o, "scratch-line", "relay-zones", n, scratch, 0.4));
293
+ }
294
+ }
295
+ if (!sprint) {
296
+ // Across the track, 10 m before the finish line in lanes 2 to 8 and 10 m after it in lanes 2 to 5: a mark 0.80 m long in the middle of the lane, parallel to the finish line.
297
+ const S = o.straight;
298
+ for (let n = 2; n <= lanes; n += 1) {
299
+ const [lo, hi] = laneSpan(o, n);
300
+ const mid = (lo + hi) / 2;
301
+ const before10 = lap(o, n) - 10;
302
+ shapes.push(crossLine(o, "zone-start", "relay-zones", n, before10, mid - 0.4, mid + 0.4, W, cls));
303
+ if (n <= 5)
304
+ shapes.push(crossLine(o, "zone-end", "relay-zones", n, 10, mid - 0.4, mid + 0.4, W, cls));
305
+ }
306
+ void S;
307
+ }
308
+ }
309
+ /** 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. */
310
+ export function startPoints(o, marks, straight) {
311
+ const out = [];
312
+ if (marks.race.kind === "curved") {
313
+ if (marks.curve !== undefined)
314
+ out.push(...marks.curve);
315
+ if (marks.group !== undefined)
316
+ out.push(...marks.group);
317
+ return out;
318
+ }
319
+ const R = o.radius;
320
+ if (marks.race.kind === "straight") {
321
+ const sl = straight?.lane ?? o.lane;
322
+ const n = straight?.lanes ?? o.lanes;
323
+ for (const st of marks.starts)
324
+ out.push([st.x, R], [st.x, R + n * sl]);
325
+ return out;
326
+ }
327
+ for (const st of marks.starts) {
328
+ const [ra, rb] = laneSpan(o, st.lane);
329
+ out.push(...across(o, st.lane, st.s, ra, rb));
330
+ }
331
+ if (marks.curve !== undefined)
332
+ out.push(...marks.curve);
333
+ if (marks.group !== undefined)
334
+ out.push(...marks.group);
335
+ return out;
336
+ }
337
+ /** The pivot of a start's close-up: where the view is turned about, and the way the athletes face there. */
338
+ export function startPivot(o, marks) {
339
+ if (marks.race.kind === "straight") {
340
+ const first = marks.starts[0];
341
+ const mid = marks.starts[Math.floor(marks.starts.length / 2)] ?? first;
342
+ return { x: mid.x, y: mid.y, heading: 0 };
343
+ }
344
+ if (marks.starts.length > 0) {
345
+ const mid = marks.starts[Math.floor((marks.starts.length - 1) / 2)];
346
+ return { x: mid.x, y: mid.y, heading: mid.heading };
347
+ }
348
+ const c = marks.curve;
349
+ const a = laneRadius(o, 1);
350
+ const origin = curveOrigin(o, a, marks.s1);
351
+ const p0 = pose(o, a, marks.s1);
352
+ void origin;
353
+ const mid = c[Math.floor(c.length / 2)];
354
+ return { x: mid[0], y: mid[1], heading: p0.heading };
355
+ }
@@ -0,0 +1,72 @@
1
+ import { type Parts } from "./oval.ts";
2
+ import { type RaceContext } from "./races.ts";
3
+ import { type Oval, type Pose } from "./track.ts";
4
+ import type { RaceInfo, WaterJump } from "./types.ts";
5
+ type Pt = [number, number];
6
+ /** The figures of the diverted bend with the jump inside (Manual 2.2.4.1, comments on Figure 2.2.4.1a): the arcs of the oval's radius, the transitions and the straight between them. */
7
+ export declare const INSIDE: {
8
+ /** The length of the lap, from A to A. */
9
+ readonly lap: 396.0849;
10
+ /** The angle of the arc on the oval's own circle before the transition: 42.5525 degrees, 27.3307 m on the 36.80 m running line. */
11
+ readonly b2: 27.3307;
12
+ /** The transition: 13.41545 m on a 16.20 m radius, 47.4475 degrees. */
13
+ readonly b1: 13.41545;
14
+ readonly radius: 16.2;
15
+ /** The straight between the transitions. */
16
+ readonly middle: 30.202;
17
+ /** The distance of the centres of the transition arcs from the second semicircle's centre, along the axis and across it. */
18
+ readonly centre: {
19
+ readonly out: 13.863;
20
+ readonly across: 15.101;
21
+ };
22
+ /** The distance of each of the five hurdles from the finish line along the lap. */
23
+ readonly hurdles: readonly [21.997, 101.214, 180.431, 259.648, 338.865];
24
+ readonly start3000: 172.594;
25
+ readonly start2000: 376.504;
26
+ };
27
+ /** The figures of the lap with the jump outside (Manual 2.2.4.1, Figure 2.2.4.1b and 2.2.4.1e). */
28
+ export declare const OUTSIDE: {
29
+ readonly lap: 419.407;
30
+ readonly straight: 9.86;
31
+ readonly radius: 36.7;
32
+ readonly hurdles: readonly [17.866, 101.747, 185.628, 269.509, 353.39];
33
+ readonly start3000: 355.256;
34
+ readonly start2000: 97.035;
35
+ };
36
+ /** The steeplechase hurdle's figures (Rule 23.5 and 23.6; Figure 2.2.4.1c): at least 3.94 m wide and 0.127 m square at the top; the water jump's hurdle and pit are 3.66 m. */
37
+ export declare const HURDLE: {
38
+ readonly width: 3.94;
39
+ readonly bar: 0.127;
40
+ readonly inside: 0.3;
41
+ readonly pit: 3.66;
42
+ };
43
+ /** The pose of the steeplechase's running line `s` metres from the finish line, with the diverted part of the lap and the oval's offset inside. */
44
+ export interface ScPose extends Pose {
45
+ /** How far the track's inside edge is inside the running line: 0.30 m on the oval, 0.20 m where the lap is diverted from it. */
46
+ innerOffset: number;
47
+ /** True where the lap is off the oval's own line. */
48
+ diverted: boolean;
49
+ }
50
+ /** The oval's lane 1 distance that a distance along the steeplechase lap comes to where the two are the same line, and `undefined` where the lap is diverted. */
51
+ export declare function standardDistance(o: Oval, variant: WaterJump, s: number): number | undefined;
52
+ /** The length of the lap with the jump where it is. */
53
+ export declare const scLap: (_o: Oval, variant: WaterJump) => number;
54
+ /** A point of the steeplechase's running line. */
55
+ export declare function scPose(o: Oval, variant: WaterJump, s: number): ScPose;
56
+ /** The box round the water jump's pit, in metres. */
57
+ export declare function waterPitBox(o: Oval, variant: WaterJump): {
58
+ x0: number;
59
+ y0: number;
60
+ x1: number;
61
+ y1: number;
62
+ };
63
+ /** The steeplechase's marks: the diverted border, the five hurdles, the water jump, and the start of the race (curved, with the outer group's line for the 3000 m). */
64
+ export declare function steeplechaseParts(ctx: RaceContext, variant: WaterJump, race: RaceInfo | undefined, drawLap: boolean): {
65
+ parts: Parts;
66
+ start?: {
67
+ s: number;
68
+ curve: Pt[];
69
+ group?: Pt[];
70
+ };
71
+ };
72
+ export {};
@@ -0,0 +1,196 @@
1
+ // The steeplechase's lap and its water jump, inside the second bend of the 400 m Standard Track or outside it (Track and Field Facilities Manual 2019, 2.2.4 and Figures 2.2.4.1a, 2.2.4.1b, 2.2.4.1e,
2
+ // with its comments on Figure 2.2.4.1a; Technical Rule 23 of Book C2.1). The lap with the jump inside is 396.0849 m (3.916 m shorter than the oval's), made of the first bend, the back straight, a
3
+ // diverted bend (two arcs of the oval's own radius, two transitions of 16.20 m radius and a 30.2020 m straight) and the finish straight; the lap with the jump outside is 419.407 m (19.406 m longer): a
4
+ // transitional straight of 9.86 m, a semicircle of 36.70 m and another straight of 9.86 m in place of the oval's bend. Five hurdles stand in a lap, the fourth the water jump. Metres.
5
+ import { line, path, poly } from "../fields.kit.js";
6
+ import { num } from "../format.js";
7
+ import { emptyParts, outerRadius } from "./oval.js";
8
+ import { curveAcross, curveOrigin, outward } from "./races.js";
9
+ import { laneEdge, laneRadius, lap, pose, breaklineStagger } from "./track.js";
10
+ /** The figures of the diverted bend with the jump inside (Manual 2.2.4.1, comments on Figure 2.2.4.1a): the arcs of the oval's radius, the transitions and the straight between them. */
11
+ export const INSIDE = {
12
+ /** The length of the lap, from A to A. */
13
+ lap: 396.0849,
14
+ /** The angle of the arc on the oval's own circle before the transition: 42.5525 degrees, 27.3307 m on the 36.80 m running line. */
15
+ b2: 27.3307,
16
+ /** The transition: 13.41545 m on a 16.20 m radius, 47.4475 degrees. */
17
+ b1: 13.41545,
18
+ radius: 16.2,
19
+ /** The straight between the transitions. */
20
+ middle: 30.202,
21
+ /** The distance of the centres of the transition arcs from the second semicircle's centre, along the axis and across it. */
22
+ centre: { out: 13.863, across: 15.101 },
23
+ /** The distance of each of the five hurdles from the finish line along the lap. */
24
+ hurdles: [21.997, 101.214, 180.431, 259.648, 338.865],
25
+ start3000: 172.594,
26
+ start2000: 376.504,
27
+ };
28
+ /** The figures of the lap with the jump outside (Manual 2.2.4.1, Figure 2.2.4.1b and 2.2.4.1e). */
29
+ export const OUTSIDE = {
30
+ lap: 419.407,
31
+ straight: 9.86,
32
+ radius: 36.7,
33
+ hurdles: [17.866, 101.747, 185.628, 269.509, 353.39],
34
+ start3000: 355.256,
35
+ start2000: 97.035,
36
+ };
37
+ /** The steeplechase hurdle's figures (Rule 23.5 and 23.6; Figure 2.2.4.1c): at least 3.94 m wide and 0.127 m square at the top; the water jump's hurdle and pit are 3.66 m. */
38
+ export const HURDLE = { width: 3.94, bar: 0.127, inside: 0.3, pit: 3.66 };
39
+ /** The oval's lane 1 distance that a distance along the steeplechase lap comes to where the two are the same line, and `undefined` where the lap is diverted. */
40
+ export function standardDistance(o, variant, s) {
41
+ const a = laneRadius(o, 1);
42
+ const L = lap(o, 1);
43
+ const t = ((s % scLap(o, variant)) + scLap(o, variant)) % scLap(o, variant);
44
+ if (variant === "outside") {
45
+ const first = Math.PI * a + o.straight;
46
+ if (t <= first)
47
+ return t;
48
+ if (t >= first + 2 * OUTSIDE.straight + Math.PI * OUTSIDE.radius)
49
+ return t - (OUTSIDE.lap - L);
50
+ return undefined;
51
+ }
52
+ const first = Math.PI * a + o.straight + INSIDE.b2;
53
+ if (t <= first)
54
+ return t;
55
+ if (t >= first + 2 * INSIDE.b1 + INSIDE.middle)
56
+ return t + (L - INSIDE.lap);
57
+ return undefined;
58
+ }
59
+ /** The length of the lap with the jump where it is. */
60
+ export const scLap = (_o, variant) => (variant === "outside" ? OUTSIDE.lap : INSIDE.lap);
61
+ /** A point of the steeplechase's running line. */
62
+ export function scPose(o, variant, s) {
63
+ const a = laneRadius(o, 1);
64
+ const L = scLap(o, variant);
65
+ const t = ((s % L) + L) % L;
66
+ const std = standardDistance(o, variant, t);
67
+ if (std !== undefined)
68
+ return { ...pose(o, a, std), innerOffset: 0.3, diverted: false };
69
+ const S = o.straight;
70
+ const cp1x = -S / 2;
71
+ if (variant === "outside") {
72
+ const first = Math.PI * a + S;
73
+ const x0 = cp1x - OUTSIDE.straight;
74
+ const r = OUTSIDE.radius;
75
+ const d = t - first;
76
+ if (d <= OUTSIDE.straight)
77
+ return { x: cp1x - d, y: -r, heading: Math.PI, innerOffset: 0.2, diverted: true };
78
+ if (d <= OUTSIDE.straight + Math.PI * r) {
79
+ const f = (d - OUTSIDE.straight) / r;
80
+ return { x: x0 - r * Math.sin(f), y: -r * Math.cos(f), heading: Math.PI - f, innerOffset: 0.2, diverted: true };
81
+ }
82
+ const e = d - OUTSIDE.straight - Math.PI * r;
83
+ return { x: x0 + e, y: r, heading: 0, innerOffset: 0.2, diverted: true };
84
+ }
85
+ // The jump inside: the upper transition about the centre above the axis, the straight, the lower transition.
86
+ const first = Math.PI * a + S + INSIDE.b2;
87
+ const R = INSIDE.radius;
88
+ const cu = [cp1x - INSIDE.centre.out, -INSIDE.centre.across];
89
+ const cl = [cp1x - INSIDE.centre.out, INSIDE.centre.across];
90
+ // The angle of the ray from the centre of a transition to the oval's circle: 227.55 degrees for the upper one.
91
+ const phi0 = Math.atan2(-INSIDE.centre.across, -INSIDE.centre.out);
92
+ const d = t - first;
93
+ if (d <= INSIDE.b1) {
94
+ const phi = phi0 - d / R;
95
+ return { x: cu[0] + R * Math.cos(phi), y: cu[1] + R * Math.sin(phi), heading: Math.atan2(-Math.cos(phi), Math.sin(phi)) + 0, innerOffset: 0.2, diverted: true };
96
+ }
97
+ if (d <= INSIDE.b1 + INSIDE.middle)
98
+ return { x: cu[0] - R, y: cu[1] + (d - INSIDE.b1), heading: Math.PI / 2, innerOffset: 0.2, diverted: true };
99
+ const e = d - INSIDE.b1 - INSIDE.middle;
100
+ // The lower transition: from the leftmost point (180 degrees) round to the ray towards the oval's circle at 132.45 degrees.
101
+ const lower = Math.PI - e / R;
102
+ return { x: cl[0] + R * Math.cos(lower), y: cl[1] + R * Math.sin(lower), heading: Math.atan2(-Math.cos(lower), Math.sin(lower)), innerOffset: 0.2, diverted: true };
103
+ }
104
+ /** The line of the diverted inner border and, for the jump outside, the band the diverted track lies on. */
105
+ function divertedLines(o, variant) {
106
+ const S = o.straight;
107
+ const R = o.radius;
108
+ const cp1x = -S / 2;
109
+ if (variant === "outside") {
110
+ const c = cp1x - OUTSIDE.straight;
111
+ const inner = `M${num(cp1x)} ${num(-R)}H${num(c)}A${num(R)} ${num(R)} 0 0 0 ${num(c)} ${num(R)}H${num(cp1x)}`;
112
+ const wide = 5;
113
+ const band = `M${num(cp1x)} ${num(-R)}H${num(c)}A${num(R)} ${num(R)} 0 0 0 ${num(c)} ${num(R)}H${num(cp1x)}V${num(R + wide)}H${num(c)}A${num(R + wide)} ${num(R + wide)} 0 0 1 ${num(c)} ${num(-R - wide)}H${num(cp1x)}Z`;
114
+ return { shapes: [path("water-jump-border", "water-jump", inner, { w: 0.05 })], areas: [{ id: "water-jump-track", feature: "water-jump", cls: "ky-ath-track", d: band }] };
115
+ }
116
+ const k = Math.hypot(INSIDE.centre.out, INSIDE.centre.across);
117
+ const ux = -INSIDE.centre.out / k;
118
+ const uy = INSIDE.centre.across / k;
119
+ // The oval's circle is touched, from inside, by the transition's circle on the ray from the semicircle's centre through its centre: at 36.5 m from the centre.
120
+ const upper = [cp1x + R * ux, -R * uy];
121
+ const lower = [cp1x + R * ux, R * uy];
122
+ const x = cp1x - INSIDE.centre.out - INSIDE.radius;
123
+ const yc = INSIDE.centre.across;
124
+ const d = `M${num(upper[0])} ${num(upper[1])}A${num(INSIDE.radius)} ${num(INSIDE.radius)} 0 0 0 ${num(x)} ${num(-yc)}V${num(yc)}A${num(INSIDE.radius)} ${num(INSIDE.radius)} 0 0 0 ${num(lower[0])} ${num(lower[1])}`;
125
+ return { shapes: [path("water-jump-border", "water-jump", d, { w: 0.05 })], areas: [] };
126
+ }
127
+ /** One hurdle: a bar across the lane at distance `s` of the lap, from `inner - 0.30` outwards `width`. */
128
+ function hurdle(o, variant, s, width, id, feature) {
129
+ const p = scPose(o, variant, s);
130
+ const [nx, ny] = outward(p.heading);
131
+ const a = -p.innerOffset - HURDLE.inside;
132
+ const b = a + width;
133
+ return line(id, feature, p.x + a * nx, p.y + a * ny, p.x + b * nx, p.y + b * ny, { w: HURDLE.bar, cls: "ky-ath-k", least: 0.2 });
134
+ }
135
+ /** The water jump's pit: 3.66 m along the run and 3.66 m across, from its hurdle forward, as a filled area with its border. */
136
+ function waterPit(o, variant, s) {
137
+ const p = scPose(o, variant, s);
138
+ const [nx, ny] = outward(p.heading);
139
+ const fx = Math.cos(p.heading);
140
+ const fy = Math.sin(p.heading);
141
+ const a = -p.innerOffset - HURDLE.inside;
142
+ const b = a + HURDLE.pit;
143
+ const corner = (along, off) => [p.x + along * fx + off * nx, p.y + along * fy + off * ny];
144
+ const pts = [corner(0, a), corner(0, b), corner(HURDLE.pit, b), corner(HURDLE.pit, a)];
145
+ const d = `M${pts.map(([x, y]) => `${num(x)} ${num(y)}`).join("L")}Z`;
146
+ return { area: { id: "water-pit", feature: "water-jump", cls: "ky-ath-water", d }, edge: poly("water-pit-edge", "water-jump", pts, { closed: true, w: 0.05 }) };
147
+ }
148
+ /** The box round the water jump's pit, in metres. */
149
+ export function waterPitBox(o, variant) {
150
+ const hurdleAt = variant === "outside" ? OUTSIDE.hurdles[3] : INSIDE.hurdles[3];
151
+ const p = scPose(o, variant, hurdleAt);
152
+ const [nx, ny] = outward(p.heading);
153
+ const fx = Math.cos(p.heading);
154
+ const fy = Math.sin(p.heading);
155
+ const a = -p.innerOffset - HURDLE.inside;
156
+ const b = a + HURDLE.pit;
157
+ const pts = [[0, a], [0, b], [HURDLE.pit, b], [HURDLE.pit, a]].map(([along, off]) => [p.x + along * fx + off * nx, p.y + along * fy + off * ny]);
158
+ return { x0: Math.min(...pts.map((q) => q[0])), y0: Math.min(...pts.map((q) => q[1])), x1: Math.max(...pts.map((q) => q[0])), y1: Math.max(...pts.map((q) => q[1])) };
159
+ }
160
+ /** The steeplechase's marks: the diverted border, the five hurdles, the water jump, and the start of the race (curved, with the outer group's line for the 3000 m). */
161
+ export function steeplechaseParts(ctx, variant, race, drawLap) {
162
+ const o = ctx.oval;
163
+ const parts = emptyParts();
164
+ if (drawLap) {
165
+ const lines = divertedLines(o, variant);
166
+ parts.shapes.push(...lines.shapes);
167
+ parts.areas.push(...lines.areas);
168
+ const at = variant === "outside" ? OUTSIDE.hurdles : INSIDE.hurdles;
169
+ at.forEach((s, i) => {
170
+ if (i === 3) {
171
+ const pit = waterPit(o, variant, s);
172
+ parts.areas.push(pit.area);
173
+ parts.shapes.push(pit.edge, hurdle(o, variant, s, HURDLE.pit, "water-jump-hurdle", "water-jump"));
174
+ }
175
+ else
176
+ parts.shapes.push(hurdle(o, variant, s, HURDLE.width, `steeplechase-hurdle-${i + 1}`, "steeplechase-hurdles"));
177
+ });
178
+ }
179
+ if (race === undefined)
180
+ return { parts };
181
+ const sc = race.id === "2000m-steeplechase" ? (variant === "outside" ? OUTSIDE.start2000 : INSIDE.start2000) : variant === "outside" ? OUTSIDE.start3000 : INSIDE.start3000;
182
+ const std = standardDistance(o, variant, sc) ?? sc;
183
+ const a = laneRadius(o, 1);
184
+ const Rout = outerRadius(o);
185
+ const curve = curveAcross(o, curveOrigin(o, a, std), a, o.radius, Rout);
186
+ parts.shapes.push(poly("start-line", "start-lines", curve, { w: 0.05 }));
187
+ const out = { s: sc, curve };
188
+ if (race.group === true) {
189
+ const gl = ctx.lanes <= 6 ? 4 : 5;
190
+ const ag = laneRadius(o, gl);
191
+ const group = curveAcross(o, curveOrigin(o, ag, std + breaklineStagger(o, gl)), ag, laneEdge(o, gl - 1), Rout);
192
+ parts.shapes.push(poly("group-start-line", "start-lines", group, { w: 0.05 }));
193
+ out.group = group;
194
+ }
195
+ return { parts, start: out };
196
+ }
@@ -0,0 +1,13 @@
1
+ /** The width of a digit over its height, and the gap between two digits over the height. */
2
+ export declare const DIGIT_WIDTH = 0.62;
3
+ export declare const DIGIT_GAP = 0.14;
4
+ /** The thickness of a stroke over the height. */
5
+ export declare const STROKE = 0.18;
6
+ /** The width of a string of digits at the height `h`. */
7
+ export declare const digitsWidth: (text: string, h: number) => number;
8
+ /**
9
+ * A string of digits as one path of strokes, centred on `(cx, cy)`, `h` tall, upright. `stroke-width` is `strokeWidth(h)`. Characters that are not digits are left out.
10
+ */
11
+ export declare function digitsD(text: string, h: number, cx: number, cy: number): string;
12
+ /** The thickness of a stroke at the height `h`. */
13
+ export declare const strokeWidth: (h: number) => number;