@johnmorrisdotca/kyogijo-stadiums 0.4.0 → 0.5.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 (244) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/NOTICE.md +4 -0
  3. package/README.md +71 -16
  4. package/dist/annotate.js +1 -1
  5. package/dist/athletics/describe.d.ts +8 -0
  6. package/dist/athletics/describe.js +41 -0
  7. package/dist/athletics/events.d.ts +17 -0
  8. package/dist/athletics/events.js +48 -0
  9. package/dist/athletics/facilities.d.ts +80 -0
  10. package/dist/athletics/facilities.js +176 -0
  11. package/dist/athletics/infield.d.ts +79 -0
  12. package/dist/athletics/infield.js +166 -0
  13. package/dist/athletics/layout.d.ts +13 -0
  14. package/dist/athletics/layout.js +98 -0
  15. package/dist/athletics/oval.d.ts +57 -0
  16. package/dist/athletics/oval.js +116 -0
  17. package/dist/athletics/plan.d.ts +97 -0
  18. package/dist/athletics/plan.js +355 -0
  19. package/dist/athletics/races.d.ts +102 -0
  20. package/dist/athletics/races.js +355 -0
  21. package/dist/athletics/steeplechase.d.ts +72 -0
  22. package/dist/athletics/steeplechase.js +196 -0
  23. package/dist/athletics/stencil.d.ts +13 -0
  24. package/dist/athletics/stencil.js +85 -0
  25. package/dist/athletics/track.d.ts +54 -0
  26. package/dist/athletics/track.js +105 -0
  27. package/dist/athletics/types.d.ts +77 -0
  28. package/dist/athletics/types.js +1 -0
  29. package/dist/athletics-entry.d.ts +17 -0
  30. package/dist/athletics-entry.js +11 -0
  31. package/dist/athletics.style.d.ts +16 -0
  32. package/dist/athletics.style.js +51 -0
  33. package/dist/atlas-entry.d.ts +4 -2
  34. package/dist/atlas-entry.js +3 -2
  35. package/dist/atlas.d.ts +83 -7
  36. package/dist/atlas.js +108 -8
  37. package/dist/charts-entry.d.ts +6 -1
  38. package/dist/charts-entry.js +3 -0
  39. package/dist/charts.athletics.d.ts +183 -0
  40. package/dist/charts.athletics.js +455 -0
  41. package/dist/charts.curling.heat.d.ts +24 -0
  42. package/dist/charts.curling.heat.js +116 -0
  43. package/dist/charts.curling.js +14 -3
  44. package/dist/charts.pool.d.ts +67 -0
  45. package/dist/charts.pool.js +76 -0
  46. package/dist/charts.skating.d.ts +204 -0
  47. package/dist/charts.skating.js +426 -0
  48. package/dist/charts.types.d.ts +53 -1
  49. package/dist/court.d.ts +85 -2
  50. package/dist/court.js +102 -7
  51. package/dist/court.more.d.ts +46 -0
  52. package/dist/court.more.js +219 -0
  53. package/dist/courts.style.d.ts +14 -2
  54. package/dist/courts.style.js +51 -4
  55. package/dist/crowd.d.ts +154 -0
  56. package/dist/crowd.draw.d.ts +17 -0
  57. package/dist/crowd.draw.js +198 -0
  58. package/dist/crowd.js +465 -0
  59. package/dist/crowd.park.d.ts +19 -0
  60. package/dist/crowd.park.js +153 -0
  61. package/dist/curling.stats.d.ts +271 -0
  62. package/dist/curling.stats.js +301 -0
  63. package/dist/draw.d.ts +16 -0
  64. package/dist/draw.js +318 -35
  65. package/dist/element-entry.d.ts +1 -1
  66. package/dist/element-entry.js +1 -1
  67. package/dist/element.d.ts +13 -2
  68. package/dist/element.js +77 -3
  69. package/dist/features.js +131 -0
  70. package/dist/fields.kit.d.ts +2 -2
  71. package/dist/fields.types.d.ts +18 -4
  72. package/dist/frames.providers.js +63 -8
  73. package/dist/geojson.js +3 -0
  74. package/dist/geometry-entry.d.ts +2 -1
  75. package/dist/geometry-entry.js +2 -1
  76. package/dist/geometry.d.ts +20 -0
  77. package/dist/geometry.js +78 -1
  78. package/dist/grounds.d.ts +4 -0
  79. package/dist/grounds.data.js +12 -0
  80. package/dist/hardwood.d.ts +31 -0
  81. package/dist/hardwood.js +157 -0
  82. package/dist/heat.js +4 -3
  83. package/dist/heat.types.d.ts +5 -0
  84. package/dist/heat.zones.d.ts +2 -2
  85. package/dist/heat.zones.js +154 -6
  86. package/dist/homeside.js +17 -0
  87. package/dist/index.d.ts +16 -2
  88. package/dist/index.js +10 -1
  89. package/dist/layers.d.ts +1 -1
  90. package/dist/layers.env.d.ts +3 -1
  91. package/dist/layers.env.js +4 -2
  92. package/dist/layers.js +4 -4
  93. package/dist/leagues/athletics.d.ts +63 -0
  94. package/dist/leagues/athletics.js +87 -0
  95. package/dist/leagues/courts.d.ts +269 -4
  96. package/dist/leagues/courts.js +155 -1
  97. package/dist/leagues/skating.d.ts +145 -0
  98. package/dist/leagues/skating.js +73 -0
  99. package/dist/leagues/sources.js +20 -0
  100. package/dist/leagues/types.d.ts +1 -1
  101. package/dist/leagues-entry.d.ts +4 -2
  102. package/dist/leagues-entry.js +2 -1
  103. package/dist/leagues.d.ts +14 -4
  104. package/dist/leagues.js +146 -5
  105. package/dist/look.d.ts +3 -3
  106. package/dist/look.js +39 -6
  107. package/dist/outline.d.ts +1 -1
  108. package/dist/outline.js +30 -2
  109. package/dist/pageant.d.ts +120 -0
  110. package/dist/pageant.draw.d.ts +21 -0
  111. package/dist/pageant.draw.js +171 -0
  112. package/dist/pageant.js +207 -0
  113. package/dist/pageant.style.d.ts +8 -0
  114. package/dist/pageant.style.js +42 -0
  115. package/dist/png.d.ts +8 -1
  116. package/dist/png.js +21 -1
  117. package/dist/pool.d.ts +176 -0
  118. package/dist/pool.figures.d.ts +304 -0
  119. package/dist/pool.figures.js +144 -0
  120. package/dist/pool.js +304 -0
  121. package/dist/pool.kit.d.ts +48 -0
  122. package/dist/pool.kit.js +141 -0
  123. package/dist/pool.labels.d.ts +20 -0
  124. package/dist/pool.labels.js +257 -0
  125. package/dist/pool.race.d.ts +65 -0
  126. package/dist/pool.race.js +140 -0
  127. package/dist/pool.replay.d.ts +46 -0
  128. package/dist/pool.replay.js +127 -0
  129. package/dist/pool.section.d.ts +13 -0
  130. package/dist/pool.section.js +83 -0
  131. package/dist/pool.style.d.ts +20 -0
  132. package/dist/pool.style.js +169 -0
  133. package/dist/pool.types.d.ts +145 -0
  134. package/dist/pool.types.js +3 -0
  135. package/dist/pool.words.d.ts +13 -0
  136. package/dist/pool.words.js +101 -0
  137. package/dist/ranges.names.js +47 -3
  138. package/dist/replay-entry.d.ts +3 -0
  139. package/dist/replay-entry.js +2 -0
  140. package/dist/restyle.js +1 -1
  141. package/dist/score.js +70 -5
  142. package/dist/sheet.d.ts +3 -0
  143. package/dist/sheet.js +7 -3
  144. package/dist/skating/describe.d.ts +7 -0
  145. package/dist/skating/describe.js +44 -0
  146. package/dist/skating/events.d.ts +24 -0
  147. package/dist/skating/events.js +47 -0
  148. package/dist/skating/figure.d.ts +24 -0
  149. package/dist/skating/figure.js +37 -0
  150. package/dist/skating/long.d.ts +54 -0
  151. package/dist/skating/long.js +111 -0
  152. package/dist/skating/parts.d.ts +43 -0
  153. package/dist/skating/parts.js +40 -0
  154. package/dist/skating/plan.d.ts +55 -0
  155. package/dist/skating/plan.js +197 -0
  156. package/dist/skating/short.d.ts +47 -0
  157. package/dist/skating/short.js +89 -0
  158. package/dist/skating/track.d.ts +66 -0
  159. package/dist/skating/track.js +94 -0
  160. package/dist/skating/types.d.ts +57 -0
  161. package/dist/skating/types.js +3 -0
  162. package/dist/skating-entry.d.ts +17 -0
  163. package/dist/skating-entry.js +11 -0
  164. package/dist/skating.style.d.ts +4 -0
  165. package/dist/skating.style.js +24 -0
  166. package/dist/strings.d.ts +296 -0
  167. package/dist/strings.js +328 -11
  168. package/dist/style.d.ts +2 -0
  169. package/dist/style.js +11 -1
  170. package/dist/surfaces/artistic.d.ts +9 -0
  171. package/dist/surfaces/artistic.js +94 -0
  172. package/dist/surfaces/athletics.d.ts +16 -0
  173. package/dist/surfaces/athletics.js +132 -0
  174. package/dist/surfaces/baseball.js +15 -0
  175. package/dist/surfaces/basketball.floor.d.ts +27 -0
  176. package/dist/surfaces/basketball.floor.js +83 -0
  177. package/dist/surfaces/basketball.gear.d.ts +27 -0
  178. package/dist/surfaces/basketball.gear.js +90 -0
  179. package/dist/surfaces/basketball.js +43 -21
  180. package/dist/surfaces/curling.d.ts +16 -0
  181. package/dist/surfaces/curling.fold.d.ts +67 -0
  182. package/dist/surfaces/curling.fold.js +107 -0
  183. package/dist/surfaces/curling.js +37 -3
  184. package/dist/surfaces/dive.d.ts +9 -0
  185. package/dist/surfaces/dive.js +128 -0
  186. package/dist/surfaces/field.d.ts +7 -2
  187. package/dist/surfaces/field.js +15 -9
  188. package/dist/surfaces/hockey.js +1 -0
  189. package/dist/surfaces/net.d.ts +2 -1
  190. package/dist/surfaces/net.js +160 -15
  191. package/dist/surfaces/polo.d.ts +12 -0
  192. package/dist/surfaces/polo.js +159 -0
  193. package/dist/surfaces/pool.d.ts +27 -0
  194. package/dist/surfaces/pool.js +198 -0
  195. package/dist/surfaces/skating.d.ts +6 -0
  196. package/dist/surfaces/skating.js +62 -0
  197. package/dist/surfaces/soccer.js +2 -1
  198. package/dist/surfaces/surface.d.ts +44 -0
  199. package/dist/surfaces/venue.d.ts +15 -0
  200. package/dist/surfaces/venue.js +95 -0
  201. package/dist/tenants.d.ts +77 -0
  202. package/dist/tenants.data.d.ts +8 -0
  203. package/dist/tenants.data.js +945 -0
  204. package/dist/tenants.js +46 -0
  205. package/dist/turn-entry.d.ts +12 -0
  206. package/dist/turn-entry.js +7 -0
  207. package/dist/turn.camera.d.ts +105 -0
  208. package/dist/turn.camera.js +168 -0
  209. package/dist/turn.dom.d.ts +40 -0
  210. package/dist/turn.dom.js +351 -0
  211. package/dist/turn.kit.d.ts +86 -0
  212. package/dist/turn.kit.js +110 -0
  213. package/dist/turn.markup.d.ts +48 -0
  214. package/dist/turn.markup.js +108 -0
  215. package/dist/turn.model.d.ts +78 -0
  216. package/dist/turn.model.js +91 -0
  217. package/dist/turn.style.d.ts +5 -0
  218. package/dist/turn.style.js +55 -0
  219. package/dist/types.d.ts +183 -18
  220. package/dist/units.d.ts +1 -1
  221. package/dist/units.js +15 -1
  222. package/dist/uprights.board.d.ts +17 -0
  223. package/dist/uprights.board.js +59 -0
  224. package/dist/uprights.d.ts +37 -0
  225. package/dist/uprights.fields.d.ts +32 -0
  226. package/dist/uprights.fields.js +172 -0
  227. package/dist/uprights.goal.d.ts +49 -0
  228. package/dist/uprights.goal.js +69 -0
  229. package/dist/uprights.js +160 -0
  230. package/dist/uprights.more.d.ts +7 -0
  231. package/dist/uprights.more.js +183 -0
  232. package/dist/uprights.net.d.ts +12 -0
  233. package/dist/uprights.net.js +109 -0
  234. package/dist/uprights.netkit.d.ts +28 -0
  235. package/dist/uprights.netkit.js +67 -0
  236. package/dist/uprights.pools.d.ts +8 -0
  237. package/dist/uprights.pools.js +190 -0
  238. package/dist/uprights.rinks.d.ts +27 -0
  239. package/dist/uprights.rinks.js +106 -0
  240. package/dist/uprights.track.d.ts +50 -0
  241. package/dist/uprights.track.js +246 -0
  242. package/dist/version.d.ts +1 -1
  243. package/dist/version.js +1 -1
  244. package/package.json +19 -2
@@ -0,0 +1,80 @@
1
+ import type { Area, Shape } from "../fields.types.ts";
2
+ import type { Parts } from "./oval.ts";
3
+ import type { AthleticsFieldEvent } from "./types.ts";
4
+ /** A point in the plan. */
5
+ type Pt = readonly [number, number];
6
+ /** What a facility is, as data: where it stands, which way it throws or jumps, and the figures that say how big it is. */
7
+ export interface Facility {
8
+ id: string;
9
+ event: AthleticsFieldEvent;
10
+ /** The throw's circle centre, the javelin arc's centre point, the high jump's bar centre, the pole vault's box, the jump's take-off board. */
11
+ x: number;
12
+ y: number;
13
+ /** The direction of the throw or the jump, in radians clockwise from the right as the eye sees it. */
14
+ heading: number;
15
+ /** A box round all of it, for the ranges. */
16
+ box: {
17
+ x0: number;
18
+ y0: number;
19
+ x1: number;
20
+ y1: number;
21
+ };
22
+ /** The length of the sector, or the runway, or the run-up. */
23
+ reach?: number;
24
+ }
25
+ /** The angle of a throw's sector: 34.92 degrees (Rule 32, landing sector; Manual 2.4.1.4). Its lines are 12 m apart (the chord) 20 m from the circle's centre, 0.60 m more for each metre further: 2 asin(0.3). */
26
+ export declare const SECTOR_ANGLE: number;
27
+ /** The javelin sector's half angle: the arc's radius is 8 m and the runway 4 m wide, so the lines through the arc's ends make 28.96 degrees (Rule 32, landing sector; Figure 2.4.3.2). */
28
+ export declare const JAVELIN_HALF: number;
29
+ /** A rectangle in the plan, `w` along `heading` and `h` across it, centred at `c`, as an area. */
30
+ export declare function rectArea(id: string, cls: Area["cls"], feature: string, c: Pt, heading: number, w: number, h: number): Area;
31
+ /** The outline of the same rectangle as a line. */
32
+ export declare function rectLine(id: string, feature: string, c: Pt, heading: number, w: number, h: number, wLine: number): Shape;
33
+ /** The sector of a throw: its two lines from the circle's rim to the arc, and the arc, 50 mm wide, the inner edges through the circle's centre (Rule 32, landing sector). */
34
+ export declare function sectorShapes(id: string, feature: string, c: Pt, heading: number, rim: number, reach: number, half?: number): Shape[];
35
+ /** A throwing circle (Rule 32, throwing circle, and 32.8; Manual 2.4.1.2): the concrete inside the rim, the rim, the 0.75 m lines at right angles to the sector's axis, the centre. `d` is the inside diameter. */
36
+ export declare function circleParts(id: string, event: AthleticsFieldEvent, c: Pt, heading: number, d: number, rim?: number): Parts;
37
+ /** The shot put's stop board (Rule 33.2; Manual 2.4.4.3): an arc on the circle's inside edge, 1.21 m long (the chord), 0.112 m wide, centred on the sector's axis. */
38
+ export declare function stopBoard(id: string, c: Pt, heading: number, d: number): Area;
39
+ /**
40
+ * A discus or hammer cage in plan (Rules 35 and 37; Manual 2.4.1.3, Figure 2.4.1.2): U-shaped, its mouth 6 m wide and 7 m in front of the circle's centre, the netting no nearer than 3 m behind it. The
41
+ * mouth and the line the sides take to it are the rules'; the rear is drawn as an arc about the centre, which is ours.
42
+ */
43
+ export declare function cageShapes(id: string, c: Pt, heading: number): Shape[];
44
+ /** The javelin's runway and arc (Rule 32, javelin throw runway; Manual 2.4.3.2 and 2.4.3.3), about the centre point of the arc and the throwing direction `heading` (0 throws right). */
45
+ export declare function javelinParts(id: string, c: Pt, heading: number, runway?: number): {
46
+ parts: Parts;
47
+ facility: Facility;
48
+ };
49
+ /** The box round some points. */
50
+ export declare function bounds(points: readonly Pt[]): {
51
+ x0: number;
52
+ y0: number;
53
+ x1: number;
54
+ y1: number;
55
+ };
56
+ /**
57
+ * A high jump facility (Rule 27; Manual 2.3.3): the uprights 4.02 m apart (Rule 27.6: 4.00 to 4.04 m), the crossbar, the landing mat of 6 m by 4 m at the least (Rule 27, landing area; Manual 2.3.3.4) with the bar's
58
+ * plane 0.5 m inside its front edge, the 50 mm line 3 m outside each upright (Rule 27.2, note) and the run-up: a 16 m wide area, 15 m long (Rule 27.3: 25 m for the major competitions), drawn
59
+ * as a dashed outline cut by the arc of the 15 m radius. `heading` is the way the athlete jumps; the facility is about the bar's middle.
60
+ */
61
+ export declare function highJumpParts(id: string, c: Pt, heading: number, runup?: number): {
62
+ parts: Parts;
63
+ facility: Facility;
64
+ };
65
+ /**
66
+ * A pole vault's box and 0-line at the end of a runway (Rule 28; Manual 2.3.4.2): the box 1.00 m long, 0.60 m wide at the front and 0.15 m at the stop board, set so that the top inside
67
+ * edge of the stop board is on the 0-line, which is 10 mm wide and runs on past the uprights (5.20 m apart at the least). `c` is the middle of the stop board and `heading` the way the athlete
68
+ * runs; the landing mat of 6.00 m by 6.00 m at the least with front pieces 2 m long (Rule 28; Manual 2.3.4.4) lies beyond, its front 0.10 to 0.15 m from the box.
69
+ */
70
+ export declare function poleVaultEnd(id: string, c: Pt, heading: number): {
71
+ parts: Parts;
72
+ facility: Facility;
73
+ };
74
+ /** The runway of a jump: two lines 1.22 m apart (Rule 29; Manual 2.3.1.2) from `from` to `to` along `heading`, as lines. */
75
+ export declare function runwayLines(id: string, event: AthleticsFieldEvent, from: Pt, to: Pt, width?: number): Shape[];
76
+ /** A landing pit (Rule 30; Manual 2.3.1.4): the sand with its white border, `length` long along the jump and `width` across it, centred at `c`. */
77
+ export declare function pitParts(id: string, event: AthleticsFieldEvent, c: Pt, heading: number, length: number, width: number): Parts;
78
+ /** A take-off board (Rule 29, take-off board; Manual 2.3.1.3): 1.22 m by 0.20 m, white, whose edge nearer the pit is the take-off line, `d` metres from the pit's near end. `pitEdge` is that end's middle. */
79
+ export declare function takeOffBoard(id: string, event: AthleticsFieldEvent, pitEdge: Pt, heading: number, d: number): Area;
80
+ export {};
@@ -0,0 +1,176 @@
1
+ // The field events' facilities, each at the size the World Athletics Track and Field Facilities Manual (2019 edition, 2.3 and 2.4, Figures 2.3.1.1a, 2.3.2.1, 2.3.3.1, 2.3.4.1, 2.4.1.1 to 2.4.4.2)
2
+ // and Technical Rules 25 to 38 of Book C2.1 give, in metres, and each made about its own origin and the direction of its throw or its run: a circle, a runway, a pit, a mat, a sector. The standard
3
+ // stadium's places for them (Figure 2.5a and the Marking Plan) are in infield.ts. Nothing here draws SVG.
4
+ import { arc, line, path, poly, ring } from "../fields.kit.js";
5
+ import { num } from "../format.js";
6
+ import { arcD } from "./track.js";
7
+ import { emptyParts } from "./oval.js";
8
+ /** The angle of a throw's sector: 34.92 degrees (Rule 32, landing sector; Manual 2.4.1.4). Its lines are 12 m apart (the chord) 20 m from the circle's centre, 0.60 m more for each metre further: 2 asin(0.3). */
9
+ export const SECTOR_ANGLE = 2 * Math.asin(0.3);
10
+ /** The javelin sector's half angle: the arc's radius is 8 m and the runway 4 m wide, so the lines through the arc's ends make 28.96 degrees (Rule 32, landing sector; Figure 2.4.3.2). */
11
+ export const JAVELIN_HALF = Math.asin(2 / 8);
12
+ const f = (n) => num(n);
13
+ const at = (c, theta, k, side = 0) => [c[0] + k * Math.cos(theta) - side * Math.sin(theta), c[1] + k * Math.sin(theta) + side * Math.cos(theta)];
14
+ /** The path of a closed polygon filled as an area. */
15
+ const polyArea = (id, cls, feature, points) => ({ id, feature, cls, d: `M${points.map(([x, y]) => `${f(x)} ${f(y)}`).join("L")}Z` });
16
+ /** A rectangle in the plan, `w` along `heading` and `h` across it, centred at `c`, as an area. */
17
+ export function rectArea(id, cls, feature, c, heading, w, h) {
18
+ return polyArea(id, cls, feature, [at(c, heading, -w / 2, -h / 2), at(c, heading, w / 2, -h / 2), at(c, heading, w / 2, h / 2), at(c, heading, -w / 2, h / 2)]);
19
+ }
20
+ /** The outline of the same rectangle as a line. */
21
+ export function rectLine(id, feature, c, heading, w, h, wLine) {
22
+ return poly(id, feature, [at(c, heading, -w / 2, -h / 2), at(c, heading, w / 2, -h / 2), at(c, heading, w / 2, h / 2), at(c, heading, -w / 2, h / 2)], { closed: true, w: wLine });
23
+ }
24
+ /** The sector of a throw: its two lines from the circle's rim to the arc, and the arc, 50 mm wide, the inner edges through the circle's centre (Rule 32, landing sector). */
25
+ export function sectorShapes(id, feature, c, heading, rim, reach, half = SECTOR_ANGLE / 2) {
26
+ const a = heading - half;
27
+ const b = heading + half;
28
+ const p0 = at(c, a, rim);
29
+ const p1 = at(c, a, reach);
30
+ const q0 = at(c, b, rim);
31
+ const q1 = at(c, b, reach);
32
+ const thin = { cls: "ky-ath-s", least: 0.07 };
33
+ return [poly(`${id}-sector-line`, feature, [p0, p1], thin), poly(`${id}-sector-line`, feature, [q0, q1], thin), path(`${id}-sector-arc`, feature, arcD(c[0], c[1], reach, a, b), thin)];
34
+ }
35
+ /** A throwing circle (Rule 32, throwing circle, and 32.8; Manual 2.4.1.2): the concrete inside the rim, the rim, the 0.75 m lines at right angles to the sector's axis, the centre. `d` is the inside diameter. */
36
+ export function circleParts(id, event, c, heading, d, rim = 0.06) {
37
+ const parts = emptyParts();
38
+ const r = d / 2;
39
+ parts.areas.push({ id: `${id}-circle`, feature: event, cls: "ky-ath-concrete", d: `M${f(c[0] - r)} ${f(c[1])}A${f(r)} ${f(r)} 0 1 0 ${f(c[0] + r)} ${f(c[1])}A${f(r)} ${f(r)} 0 1 0 ${f(c[0] - r)} ${f(c[1])}Z` });
40
+ parts.shapes.push(ring(`${id}-rim`, event, c[0], c[1], r + rim / 2, r + rim / 2, { w: rim }));
41
+ for (const side of [-1, 1]) {
42
+ const p = at(c, heading, 0, side * (r + rim));
43
+ const q = at(c, heading, 0, side * (r + rim + 0.75));
44
+ parts.shapes.push(line(`${id}-circle-line`, event, p[0], p[1], q[0], q[1], { w: 0.05 }));
45
+ }
46
+ return parts;
47
+ }
48
+ /** The shot put's stop board (Rule 33.2; Manual 2.4.4.3): an arc on the circle's inside edge, 1.21 m long (the chord), 0.112 m wide, centred on the sector's axis. */
49
+ export function stopBoard(id, c, heading, d) {
50
+ const r = d / 2;
51
+ const half = Math.asin(1.21 / 2 / r);
52
+ const inner = r;
53
+ const outer = r + 0.112;
54
+ const p = (k, th) => at(c, th, k);
55
+ const a = heading - half;
56
+ const b = heading + half;
57
+ const [i0, i1, o0, o1] = [p(inner, a), p(inner, b), p(outer, a), p(outer, b)];
58
+ return { id: `${id}-stop-board`, feature: "shot-put", cls: "ky-ath-board", d: `M${f(i0[0])} ${f(i0[1])}A${f(inner)} ${f(inner)} 0 0 1 ${f(i1[0])} ${f(i1[1])}L${f(o1[0])} ${f(o1[1])}A${f(outer)} ${f(outer)} 0 0 0 ${f(o0[0])} ${f(o0[1])}Z` };
59
+ }
60
+ /**
61
+ * A discus or hammer cage in plan (Rules 35 and 37; Manual 2.4.1.3, Figure 2.4.1.2): U-shaped, its mouth 6 m wide and 7 m in front of the circle's centre, the netting no nearer than 3 m behind it. The
62
+ * mouth and the line the sides take to it are the rules'; the rear is drawn as an arc about the centre, which is ours.
63
+ */
64
+ export function cageShapes(id, c, heading) {
65
+ const mouth = [7, 3];
66
+ const wide = 4.4;
67
+ const pts = (u, v) => at(c, heading, u, v);
68
+ const back = 4.4;
69
+ const left = [pts(mouth[0], -mouth[1]), pts(0, -wide)];
70
+ const right = [pts(mouth[0], mouth[1]), pts(0, wide)];
71
+ const a0 = heading - Math.PI / 2;
72
+ const a1 = heading + Math.PI / 2;
73
+ const rear = arcD(c[0], c[1], back, a0 + Math.PI, a1 + Math.PI);
74
+ return [poly(`${id}-cage`, "cage", [left[0], left[1]], { w: 0.12, cls: "ky-ath-k" }), poly(`${id}-cage`, "cage", [right[0], right[1]], { w: 0.12, cls: "ky-ath-k" }), path(`${id}-cage`, "cage", rear, { w: 0.12, cls: "ky-ath-k" })];
75
+ }
76
+ /** The javelin's runway and arc (Rule 32, javelin throw runway; Manual 2.4.3.2 and 2.4.3.3), about the centre point of the arc and the throwing direction `heading` (0 throws right). */
77
+ export function javelinParts(id, c, heading, runway = 36.5) {
78
+ const parts = emptyParts();
79
+ const xe = 8 * Math.cos(JAVELIN_HALF);
80
+ const w = 2;
81
+ // The runway: two lines 4 m apart from the ends of the arc back `runway` metres, the arc, and the 0.75 m lines at right angles to the runway at the arc's ends.
82
+ for (const side of [-1, 1]) {
83
+ const p = at(c, heading, xe, side * w);
84
+ const q = at(c, heading, xe - runway, side * w);
85
+ parts.shapes.push(line(`${id}-runway-line`, "javelin-throw", p[0], p[1], q[0], q[1], { w: 0.05 }));
86
+ const m0 = at(c, heading, xe, side * w);
87
+ const m1 = at(c, heading, xe, side * (w + 0.75));
88
+ parts.shapes.push(line(`${id}-arc-mark`, "javelin-throw", m0[0], m0[1], m1[0], m1[1], { w: 0.07 }));
89
+ // Two white squares of 0.05 m beside the runway, 4 m back from the ends of the arc.
90
+ const sq = at(c, heading, xe - 4, side * (w + 0.12));
91
+ parts.shapes.push(line(`${id}-square`, "javelin-throw", sq[0], sq[1], sq[0], sq[1], { w: 0.05 }));
92
+ }
93
+ parts.shapes.push(arc(`${id}-arc`, "javelin-throw", c[0], c[1], 8, (heading - JAVELIN_HALF) * (180 / Math.PI), (heading + JAVELIN_HALF) * (180 / Math.PI), { w: 0.07 }));
94
+ parts.shapes.push(...sectorShapes(id, "javelin-throw", c, heading, 8, 100, JAVELIN_HALF));
95
+ const end = at(c, heading, xe - runway, 0);
96
+ const box = bounds([end, at(c, heading, 100, -25), at(c, heading, 100, 25), at(c, heading, 0, 0), at(c, heading, 100, 0)]);
97
+ return { parts, facility: { id, event: "javelin-throw", x: c[0], y: c[1], heading, box, reach: 100 } };
98
+ }
99
+ /** The box round some points. */
100
+ export function bounds(points) {
101
+ const xs = points.map((p) => p[0]);
102
+ const ys = points.map((p) => p[1]);
103
+ return { x0: Math.min(...xs), y0: Math.min(...ys), x1: Math.max(...xs), y1: Math.max(...ys) };
104
+ }
105
+ /**
106
+ * A high jump facility (Rule 27; Manual 2.3.3): the uprights 4.02 m apart (Rule 27.6: 4.00 to 4.04 m), the crossbar, the landing mat of 6 m by 4 m at the least (Rule 27, landing area; Manual 2.3.3.4) with the bar's
107
+ * plane 0.5 m inside its front edge, the 50 mm line 3 m outside each upright (Rule 27.2, note) and the run-up: a 16 m wide area, 15 m long (Rule 27.3: 25 m for the major competitions), drawn
108
+ * as a dashed outline cut by the arc of the 15 m radius. `heading` is the way the athlete jumps; the facility is about the bar's middle.
109
+ */
110
+ export function highJumpParts(id, c, heading, runup = 15) {
111
+ const parts = emptyParts();
112
+ parts.areas.push(rectArea(`${id}-mat`, "ky-ath-mat", "high-jump", at(c, heading, 1.5), heading, 4, 6));
113
+ parts.shapes.push(rectLine(`${id}-mat-edge`, "high-jump", at(c, heading, 1.5), heading, 4, 6, 0.05));
114
+ const a = at(c, heading, 0, -5.01);
115
+ const b = at(c, heading, 0, 5.01);
116
+ parts.shapes.push(line(`${id}-take-off-line`, "high-jump", a[0], a[1], b[0], b[1], { w: 0.05 }));
117
+ const bar0 = at(c, heading, 0, -2.01);
118
+ const bar1 = at(c, heading, 0, 2.01);
119
+ parts.shapes.push(line(`${id}-crossbar`, "high-jump", bar0[0], bar0[1], bar1[0], bar1[1], { w: 0.04, cls: "ky-ath-k" }));
120
+ for (const side of [-1, 1]) {
121
+ const p = at(c, heading, 0, side * 2.01);
122
+ parts.shapes.push({ kind: "dot", id: `${id}-upright`, feature: "high-jump", cx: p[0], cy: p[1], r: 0.1, paint: "post" });
123
+ }
124
+ // The run-up: 8 m either side of the middle of the bar, to 15 m back, rounded by the arc of 15 m about the bar's middle.
125
+ const half = 8;
126
+ const x = Math.sqrt(runup * runup - half * half);
127
+ const [p0, p1] = [at(c, heading, 0, -half), at(c, heading, -x, -half)];
128
+ const [q0, q1] = [at(c, heading, -x, half), at(c, heading, 0, half)];
129
+ const sweep = 0;
130
+ const d = `M${f(p0[0])} ${f(p0[1])}L${f(p1[0])} ${f(p1[1])}A${f(runup)} ${f(runup)} 0 0 ${sweep} ${f(q0[0])} ${f(q0[1])}L${f(q1[0])} ${f(q1[1])}`;
131
+ parts.shapes.push(path(`${id}-run-up`, "high-jump", d, { w: 0.04, dash: [0.6, 0.6], least: 0.04 }));
132
+ const box = bounds([at(c, heading, 4.5, -3.2), at(c, heading, 4.5, 3.2), at(c, heading, -runup, -half), at(c, heading, -runup, half)]);
133
+ return { parts, facility: { id, event: "high-jump", x: c[0], y: c[1], heading, box, reach: runup } };
134
+ }
135
+ /**
136
+ * A pole vault's box and 0-line at the end of a runway (Rule 28; Manual 2.3.4.2): the box 1.00 m long, 0.60 m wide at the front and 0.15 m at the stop board, set so that the top inside
137
+ * edge of the stop board is on the 0-line, which is 10 mm wide and runs on past the uprights (5.20 m apart at the least). `c` is the middle of the stop board and `heading` the way the athlete
138
+ * runs; the landing mat of 6.00 m by 6.00 m at the least with front pieces 2 m long (Rule 28; Manual 2.3.4.4) lies beyond, its front 0.10 to 0.15 m from the box.
139
+ */
140
+ export function poleVaultEnd(id, c, heading) {
141
+ const parts = emptyParts();
142
+ parts.areas.push(polyArea(`${id}-box`, "ky-ath-pad", "pole-vault", [at(c, heading, -1, -0.3), at(c, heading, 0, -0.075), at(c, heading, 0, 0.075), at(c, heading, -1, 0.3)]));
143
+ parts.areas.push(rectArea(`${id}-mat`, "ky-ath-mat", "pole-vault", at(c, heading, 0.15 + 4), heading, 8, 6));
144
+ parts.shapes.push(rectLine(`${id}-mat-edge`, "pole-vault", at(c, heading, 0.15 + 4), heading, 8, 6, 0.05));
145
+ const a = at(c, heading, 0, -2.8);
146
+ const b = at(c, heading, 0, 2.8);
147
+ parts.shapes.push(line(`${id}-zero-line`, "pole-vault", a[0], a[1], b[0], b[1], { w: 0.03, least: 0.08, cls: "ky-ath-r" }));
148
+ for (const side of [-1, 1]) {
149
+ const p = at(c, heading, 0.1, side * 2.6);
150
+ parts.shapes.push({ kind: "dot", id: `${id}-upright`, feature: "pole-vault", cx: p[0], cy: p[1], r: 0.1, paint: "post" });
151
+ }
152
+ const box = bounds([at(c, heading, -45, -0.7), at(c, heading, -45, 0.7), at(c, heading, 8.2, -3), at(c, heading, 8.2, 3)]);
153
+ return { parts, facility: { id, event: "pole-vault", x: c[0], y: c[1], heading, box, reach: 45 } };
154
+ }
155
+ /** The runway of a jump: two lines 1.22 m apart (Rule 29; Manual 2.3.1.2) from `from` to `to` along `heading`, as lines. */
156
+ export function runwayLines(id, event, from, to, width = 1.22) {
157
+ const heading = Math.atan2(to[1] - from[1], to[0] - from[0]);
158
+ return [-1, 1].map((side) => {
159
+ const a = at(from, heading, 0, (side * width) / 2);
160
+ const b = at(to, heading, 0, (side * width) / 2);
161
+ return line(`${id}-runway-line`, event, a[0], a[1], b[0], b[1], { w: 0.05 });
162
+ });
163
+ }
164
+ /** A landing pit (Rule 30; Manual 2.3.1.4): the sand with its white border, `length` long along the jump and `width` across it, centred at `c`. */
165
+ export function pitParts(id, event, c, heading, length, width) {
166
+ const parts = emptyParts();
167
+ parts.areas.push(rectArea(`${id}-pit`, "ky-ath-sand", event, c, heading, length, width));
168
+ parts.shapes.push(rectLine(`${id}-pit-border`, event, c, heading, length, width, 0.05));
169
+ return parts;
170
+ }
171
+ /** A take-off board (Rule 29, take-off board; Manual 2.3.1.3): 1.22 m by 0.20 m, white, whose edge nearer the pit is the take-off line, `d` metres from the pit's near end. `pitEdge` is that end's middle. */
172
+ export function takeOffBoard(id, event, pitEdge, heading, d) {
173
+ // The board lies behind the take-off line, away from the pit: along the run, `d` from the pit's near end is the line and the board is the 0.20 m before it.
174
+ const line0 = at(pitEdge, heading, -d);
175
+ return rectArea(id, "ky-ath-board", event, at(line0, heading, -0.1), heading, 0.2, 1.22);
176
+ }
@@ -0,0 +1,79 @@
1
+ import type { Shape } from "../fields.types.ts";
2
+ import { type Facility } from "./facilities.ts";
3
+ import { type Parts } from "./oval.ts";
4
+ import type { Oval } from "./track.ts";
5
+ import type { AthleticsFieldEvent } from "./types.ts";
6
+ /** The sectors' lengths (Manual 2.1.1.3 and Figures 2.4.1.1, 2.4.2.1, 2.4.3.1, 2.4.4.1): the shot's 25 m, the discus's 80 m, the hammer's 90 m, the javelin's 100 m. */
7
+ export declare const SECTOR_REACH: {
8
+ readonly "shot-put": 25;
9
+ readonly "discus-throw": 80;
10
+ readonly "hammer-throw": 90;
11
+ readonly "javelin-throw": 100;
12
+ };
13
+ /** Where the standard stadium puts each facility, in metres from the centre of the semicircle of its segment (CP1 at the left, CP2 at the right), read from the Marking Plan. */
14
+ export declare const ARENA: {
15
+ /** The synthetic surface of a segment ends, and the turf of the infield begins, this far from the semicircle's centre along the straights' axis (Marking Plan; 108.39 m of turf between: a 105 m pitch fits). */
16
+ readonly turf: 12;
17
+ /** The javelin arc's apex is this far beyond the segment's centre; the centre point is 8 m behind it. */
18
+ readonly javelinApex: number;
19
+ /** The shot put circles: from the segment's centre outward along the axis, and the distance up or down. */
20
+ readonly shotWest: {
21
+ readonly out: 13.5;
22
+ readonly across: 18.15;
23
+ };
24
+ readonly shotEast: {
25
+ readonly out: 14.2;
26
+ readonly across: 18.5;
27
+ };
28
+ /** The combined discus and hammer circle of the second segment. */
29
+ readonly discus: {
30
+ readonly out: 20.4;
31
+ readonly across: -24.1;
32
+ readonly heading: number;
33
+ };
34
+ /** The two pole vault runways of the first segment: their middle lines, the boxes' places and the length, from the axis. */
35
+ readonly vaultA: {
36
+ readonly out: 23.26;
37
+ readonly box: 16.5;
38
+ };
39
+ readonly vaultB: {
40
+ readonly out: 15.82;
41
+ readonly box: 20.7;
42
+ };
43
+ readonly vaultReach: 26;
44
+ /** The high jumps: the bar's distance beyond the segment's centre and the distance of each from the axis (the spacing of two sets of uprights is 12 to 15 m: 13.5 m). */
45
+ readonly highJump: {
46
+ readonly out: 15;
47
+ readonly across: 6.75;
48
+ };
49
+ /** The long and triple jump beyond the back straight (Figure 2.5a, Marking Plan): the strip, the two runways' middle lines, and the pits' places along the straight. */
50
+ readonly strip: {
51
+ readonly x0: -45.6;
52
+ readonly x1: 46.2;
53
+ readonly y0: -56.6;
54
+ };
55
+ readonly rows: readonly [{
56
+ readonly y: -54.2;
57
+ readonly west: readonly [-39.7, -31.7];
58
+ readonly east: readonly [23, 31.1];
59
+ }, {
60
+ readonly y: -50.4;
61
+ readonly west: readonly [-30.9, -22.9];
62
+ readonly east: readonly [31.9, 40];
63
+ }];
64
+ };
65
+ /** What the infield is made of: shapes and areas, the facilities as data, and the box of what lies beyond the oval. */
66
+ export interface Infield {
67
+ parts: Parts;
68
+ facilities: Facility[];
69
+ }
70
+ /** The standard stadium's field events, those asked for. */
71
+ export declare function infieldParts(o: Oval, events: ReadonlySet<AthleticsFieldEvent>): Infield;
72
+ /** The strip beyond the back straight where the long and the triple jumps are, as a box, when they are drawn. */
73
+ export declare const jumpStrip: () => {
74
+ x0: number;
75
+ y0: number;
76
+ x1: number;
77
+ y1: number;
78
+ };
79
+ export type { Shape };
@@ -0,0 +1,166 @@
1
+ // The standard stadium's field events, each in its usual place (Track and Field Facilities Manual 2019, 2.5, Figure 2.5a "Standard layout of competition facility", and the Marking Plan for the 400 m
2
+ // Standard Track, Figure 2.2.1.6a, at 1:350): the pole vaults and the shot put circles in the first segment, the discus and hammer circle, the high jumps and the other shot put in the second, a
3
+ // javelin runway through the middle of each, and the long and triple jumps beyond the back straight. The places are read off the Marking Plan to the metre, which is the precision of its scale; the
4
+ // sizes of everything are the Manual's and the Rules'. Metres, in the frame of track.ts.
5
+ import { line } from "../fields.kit.js";
6
+ import { num } from "../format.js";
7
+ import { bounds, cageShapes, circleParts, highJumpParts, javelinParts, pitParts, poleVaultEnd, rectArea, runwayLines, sectorShapes, stopBoard, takeOffBoard, } from "./facilities.js";
8
+ import { addParts, emptyParts } from "./oval.js";
9
+ /** The sectors' lengths (Manual 2.1.1.3 and Figures 2.4.1.1, 2.4.2.1, 2.4.3.1, 2.4.4.1): the shot's 25 m, the discus's 80 m, the hammer's 90 m, the javelin's 100 m. */
10
+ export const SECTOR_REACH = { "shot-put": 25, "discus-throw": 80, "hammer-throw": 90, "javelin-throw": 100 };
11
+ /** Where the standard stadium puts each facility, in metres from the centre of the semicircle of its segment (CP1 at the left, CP2 at the right), read from the Marking Plan. */
12
+ export const ARENA = {
13
+ /** The synthetic surface of a segment ends, and the turf of the infield begins, this far from the semicircle's centre along the straights' axis (Marking Plan; 108.39 m of turf between: a 105 m pitch fits). */
14
+ turf: 12,
15
+ /** The javelin arc's apex is this far beyond the segment's centre; the centre point is 8 m behind it. */
16
+ javelinApex: 55.1 - 42.195,
17
+ /** The shot put circles: from the segment's centre outward along the axis, and the distance up or down. */
18
+ shotWest: { out: 13.5, across: 18.15 },
19
+ shotEast: { out: 14.2, across: 18.5 },
20
+ /** The combined discus and hammer circle of the second segment. */
21
+ discus: { out: 20.4, across: -24.1, heading: Math.PI * (1 - 0.097) },
22
+ /** The two pole vault runways of the first segment: their middle lines, the boxes' places and the length, from the axis. */
23
+ vaultA: { out: 23.26, box: 16.5 },
24
+ vaultB: { out: 15.82, box: 20.7 },
25
+ vaultReach: 26,
26
+ /** The high jumps: the bar's distance beyond the segment's centre and the distance of each from the axis (the spacing of two sets of uprights is 12 to 15 m: 13.5 m). */
27
+ highJump: { out: 15, across: 6.75 },
28
+ /** The long and triple jump beyond the back straight (Figure 2.5a, Marking Plan): the strip, the two runways' middle lines, and the pits' places along the straight. */
29
+ strip: { x0: -45.6, x1: 46.2, y0: -56.6 },
30
+ rows: [
31
+ { y: -54.2, west: [-39.7, -31.7], east: [23, 31.1] },
32
+ { y: -50.4, west: [-30.9, -22.9], east: [31.9, 40] },
33
+ ],
34
+ };
35
+ const rad = (deg) => (deg * Math.PI) / 180;
36
+ void rad;
37
+ /** The standard stadium's field events, those asked for. */
38
+ export function infieldParts(o, events) {
39
+ const parts = emptyParts();
40
+ const facilities = [];
41
+ const S = o.straight;
42
+ const cp1x = -S / 2;
43
+ const cp2x = S / 2;
44
+ const has = (e) => events.has(e);
45
+ // The javelin: a runway through the middle of each segment, thrown towards the middle of the infield.
46
+ if (has("javelin-throw")) {
47
+ const apex = ARENA.javelinApex;
48
+ for (const [id, cx, heading] of [
49
+ ["javelin-west", cp1x - apex - 8 + 0, 0],
50
+ ["javelin-east", cp2x + apex + 8, Math.PI],
51
+ ]) {
52
+ // The centre point is 8 m behind the arc's apex, so beyond the segment's centre by `apex + 8` on its own side.
53
+ const made = javelinParts(id, [id === "javelin-west" ? cp1x - apex - 8 : cx, 0], heading);
54
+ addParts(parts, made.parts);
55
+ facilities.push(made.facility);
56
+ }
57
+ }
58
+ // The shot put: two circles in the first segment, throwing to the east, and one in the second, throwing to the west.
59
+ if (has("shot-put")) {
60
+ const circles = [
61
+ ["shot-west-north", [cp1x - ARENA.shotWest.out, -ARENA.shotWest.across], 0],
62
+ ["shot-west-south", [cp1x - ARENA.shotWest.out, ARENA.shotWest.across], 0],
63
+ ["shot-east-south", [cp2x + ARENA.shotEast.out, ARENA.shotEast.across], Math.PI],
64
+ ];
65
+ for (const [id, c, heading] of circles) {
66
+ addParts(parts, circleParts(id, "shot-put", c, heading, 2.135));
67
+ parts.areas.push(stopBoard(id, c, heading, 2.135));
68
+ parts.shapes.push(...sectorShapes(id, "shot-put", c, heading, 1.0675, SECTOR_REACH["shot-put"]));
69
+ const reach = SECTOR_REACH["shot-put"];
70
+ facilities.push({ id, event: "shot-put", x: c[0], y: c[1], heading, reach, box: bounds([c, [c[0] + Math.cos(heading) * reach, c[1] + Math.sin(heading) * reach + reach * 0.31], [c[0] + Math.cos(heading) * reach, c[1] + Math.sin(heading) * reach - reach * 0.31]]) });
71
+ }
72
+ }
73
+ // The discus and the hammer: one circle and cage in the second segment, near the 1500 m start, throwing across the infield.
74
+ if (has("discus-throw") || has("hammer-throw")) {
75
+ const c = [cp2x + ARENA.discus.out, ARENA.discus.across];
76
+ const heading = ARENA.discus.heading;
77
+ const both = has("discus-throw") && has("hammer-throw");
78
+ // A circle of 2.50 m, reduced to 2.135 m for the hammer by a ring inside it (Rule 32, throwing circle): both are drawn when both events are.
79
+ addParts(parts, circleParts("discus-hammer", has("discus-throw") ? "discus-throw" : "hammer-throw", c, heading, has("discus-throw") ? 2.5 : 2.135));
80
+ if (both)
81
+ parts.shapes.push(...circleParts("hammer-insert", "hammer-throw", c, heading, 2.135).shapes.filter((s) => s.kind === "ring"));
82
+ parts.shapes.push(...cageShapes("discus-hammer", c, heading));
83
+ const reach = has("hammer-throw") ? SECTOR_REACH["hammer-throw"] : SECTOR_REACH["discus-throw"];
84
+ parts.shapes.push(...sectorShapes("discus-hammer", has("discus-throw") ? "discus-throw" : "hammer-throw", c, heading, has("discus-throw") ? 1.25 : 1.0675, reach));
85
+ if (both) {
86
+ // The discus's shorter arc across the same sector.
87
+ const half = Math.asin(0.3);
88
+ const a = heading - half;
89
+ const b = heading + half;
90
+ const r = SECTOR_REACH["discus-throw"];
91
+ const p = [c[0] + r * Math.cos(a), c[1] + r * Math.sin(a)];
92
+ const q = [c[0] + r * Math.cos(b), c[1] + r * Math.sin(b)];
93
+ parts.shapes.push({ kind: "path", id: "discus-sector-arc", feature: "discus-throw", d: `M${num(p[0])} ${num(p[1])}A${num(r)} ${num(r)} 0 0 1 ${num(q[0])} ${num(q[1])}` });
94
+ }
95
+ const end = [c[0] + Math.cos(heading) * reach, c[1] + Math.sin(heading) * reach];
96
+ for (const event of ["discus-throw", "hammer-throw"])
97
+ if (has(event))
98
+ facilities.push({ id: event === "discus-throw" ? "discus" : "hammer", event, x: c[0], y: c[1], heading, reach: SECTOR_REACH[event], box: bounds([c, [end[0], end[1] + 0.31 * reach], [end[0], end[1] - 0.31 * reach], [c[0] - 5, c[1] - 5], [c[0] + 8, c[1] + 8]]) });
99
+ }
100
+ // The high jump: two, symmetrical about the javelin runway of the second segment, the athletes running up from the track and jumping towards the middle.
101
+ if (has("high-jump")) {
102
+ for (const [id, side] of [["high-jump-north", -1], ["high-jump-south", 1]]) {
103
+ const made = highJumpParts(id, [cp2x + ARENA.highJump.out, side * ARENA.highJump.across], Math.PI);
104
+ addParts(parts, made.parts);
105
+ facilities.push(made.facility);
106
+ }
107
+ }
108
+ // The pole vault: two runways in the first segment, each with a box and a mat at both ends, and the runway cut where the javelin's crosses it.
109
+ if (has("pole-vault")) {
110
+ for (const [name, v] of [["pole-vault-a", ARENA.vaultA], ["pole-vault-b", ARENA.vaultB]]) {
111
+ const x = cp1x - v.out;
112
+ const reach = ARENA.vaultReach;
113
+ const gap = has("javelin-throw") ? 2.1 : 0;
114
+ for (const side of [-1, 1]) {
115
+ const lo = side === -1 ? -reach : gap;
116
+ const hi = side === -1 ? -gap : reach;
117
+ parts.shapes.push(...runwayLines(`${name}-${side}`, "pole-vault", [x, lo], [x, hi]));
118
+ }
119
+ for (const side of [-1, 1]) {
120
+ const made = poleVaultEnd(`${name}-${side === -1 ? "north" : "south"}`, [x, side * v.box], side === -1 ? -Math.PI / 2 : Math.PI / 2);
121
+ // The vaulter runs towards the box from the far end, so the box's 0-line faces the mat beyond it, away from the runway's middle.
122
+ addParts(parts, made.parts);
123
+ facilities.push(made.facility);
124
+ }
125
+ }
126
+ }
127
+ // The long and the triple jump, beyond the back straight: two runways in a strip, each with a pit at either end, a take-off board for each run.
128
+ if (has("long-jump") || has("triple-jump")) {
129
+ const strip = { id: "jump-strip", feature: "track", cls: "ky-ath-track", d: `M${num(ARENA.strip.x0)} ${num(ARENA.strip.y0)}H${num(ARENA.strip.x1)}V${num(-o.radius - o.lanes * o.lane + 0.3)}H${num(ARENA.strip.x0)}Z` };
130
+ parts.areas.push(strip);
131
+ const boxes = [];
132
+ for (const [r, row] of ARENA.rows.entries()) {
133
+ const west = row.west;
134
+ const east = row.east;
135
+ // The runway between the pits' near ends, and across both to the pits' far ends so that it reads as one facility with a pit at either end.
136
+ parts.shapes.push(...runwayLines(`jump-${r}`, has("long-jump") ? "long-jump" : "triple-jump", [west[1], row.y], [east[0], row.y]));
137
+ for (const [pit, edge, heading, name] of [
138
+ [west, west[1], Math.PI, "west"],
139
+ [east, east[0], 0, "east"],
140
+ ]) {
141
+ const id = `jump-${r}-${name}`;
142
+ const event = has("long-jump") ? "long-jump" : "triple-jump";
143
+ addParts(parts, pitParts(id, event, [(pit[0] + pit[1]) / 2, row.y], 0, pit[1] - pit[0], 3));
144
+ const jumpHeading = heading;
145
+ // The athlete runs towards the pit: for the west pit that is westward, so the board is east of the pit's near end (the east edge), and the run comes from the east.
146
+ const near = [edge, row.y];
147
+ if (has("long-jump"))
148
+ parts.areas.push(takeOffBoard(`${id}-lj-board`, "long-jump", near, jumpHeading + 0, 3));
149
+ if (has("triple-jump")) {
150
+ parts.areas.push(takeOffBoard(`${id}-tj-board-men`, "triple-jump", near, jumpHeading, 13), takeOffBoard(`${id}-tj-board-women`, "triple-jump", near, jumpHeading, 11));
151
+ }
152
+ boxes.push({ x0: pit[0] - 1, x1: pit[1] + 1, y0: row.y - 2, y1: row.y + 2 });
153
+ // One facility for each of the two events that jump into this pit, the pit's near end being its place.
154
+ for (const e of ["long-jump", "triple-jump"])
155
+ if (has(e))
156
+ facilities.push({ id, event: e, x: edge, y: row.y, heading: jumpHeading, reach: 45, box: { x0: Math.min(pit[0], pit[1]) - 1, x1: Math.max(pit[0], pit[1]) + 1, y0: row.y - 2.5, y1: row.y + 2.5 } });
157
+ }
158
+ }
159
+ void boxes;
160
+ }
161
+ void line;
162
+ void rectArea;
163
+ return { parts, facilities };
164
+ }
165
+ /** The strip beyond the back straight where the long and the triple jumps are, as a box, when they are drawn. */
166
+ export const jumpStrip = () => ({ x0: ARENA.strip.x0, y0: ARENA.strip.y0, x1: ARENA.strip.x1, y1: -46 });
@@ -0,0 +1,13 @@
1
+ import { type Facility } from "./facilities.ts";
2
+ import { type Parts } from "./oval.ts";
3
+ import type { MBox, MMark } from "./plan.ts";
4
+ import type { AthleticsFieldEvent } from "./types.ts";
5
+ /** What a field event alone is: its shapes, its facility as data, the box round it and the parts to label. */
6
+ export interface Layout {
7
+ parts: Parts;
8
+ facility: Facility;
9
+ box: MBox;
10
+ marks: MMark[];
11
+ }
12
+ /** The layout of one field event alone. */
13
+ export declare function layoutOf(event: AthleticsFieldEvent): Layout;
@@ -0,0 +1,98 @@
1
+ // One field event alone, at a large scale, from the diagram of its facility (Track and Field Facilities Manual 2019, Figures 2.3.1.1a, 2.3.2.1, 2.3.3.1, 2.3.4.1, 2.4.1.1 to 2.4.4.2; Technical Rules 25 to 38 of
2
+ // Book C2.1): the athlete throws or jumps to the right, from a point at the origin (a circle's centre, a javelin arc's centre point, a jump's take-off line, the bar, the vault's zero line). Metres.
3
+ import { line } from "../fields.kit.js";
4
+ import { num } from "../format.js";
5
+ import { bounds, cageShapes, circleParts, highJumpParts, javelinParts, pitParts, poleVaultEnd, runwayLines, sectorShapes, stopBoard, takeOffBoard, SECTOR_ANGLE, } from "./facilities.js";
6
+ import { addParts, emptyParts } from "./oval.js";
7
+ import { SECTOR_REACH } from "./infield.js";
8
+ const O = [0, 0];
9
+ /** The landing sector as ground: mown grass between the sector lines from `rim` out to the arc (a circle's rim, or a javelin arc's). */
10
+ function sectorGrass(id, event, reach, half, rim = 0) {
11
+ const at = (k, a) => `${num(k * Math.cos(a))} ${num(k * Math.sin(a))}`;
12
+ return { id: `${id}-sector-ground`, feature: event, cls: "ky-ath-grass", d: `M${at(rim, -half)}L${at(reach, -half)}A${num(reach)} ${num(reach)} 0 0 1 ${at(reach, half)}L${at(rim, half)}Z` };
13
+ }
14
+ /** A runway as ground: the track's own surface, `width` wide from `x0` to `x1`. */
15
+ function runwayGround(id, event, x0, x1, width) {
16
+ return { id: `${id}-runway-ground`, feature: event, cls: "ky-ath-track", d: `M${num(x0)} ${num(-width / 2)}H${num(x1)}V${num(width / 2)}H${num(x0)}Z` };
17
+ }
18
+ /** The long jump's and the triple jump's facility: the runway 45 m long (Rule 29; Manual 2.3.1.2), the take-off boards, the landing area 3 m wide and 8 m long. */
19
+ function jumpLayout(event) {
20
+ const parts = emptyParts();
21
+ parts.areas.push(runwayGround(event, event, -45, event === "long-jump" ? 3 : 13, 1.22));
22
+ // The pit: 8 m long, 3 m wide, its near end 3 m past the long jump's take-off line (Manual 2.3.1.4) or 13 m past the men's triple jump line (Rule 31: not less than 13 m for men, 11 for women).
23
+ const near = event === "long-jump" ? 3 : 13;
24
+ const len = 8;
25
+ addParts(parts, pitParts(event, event, [near + len / 2, 0], 0, len, 3));
26
+ parts.shapes.push(...runwayLines(event, event, [-45, 0], [near + len, 0]));
27
+ parts.areas.push(takeOffBoard(`${event}-board`, event, [near, 0], 0, near));
28
+ if (event === "triple-jump")
29
+ parts.areas.push(takeOffBoard(`${event}-board-women`, event, [near, 0], 0, 11));
30
+ // The window shows the last 24 m of the runway: the rest runs off its edge.
31
+ const box = { x0: -24, y0: -3.2, x1: near + len + 2, y1: 3.2 };
32
+ const facility = { id: event, event, x: 0, y: 0, heading: 0, box, reach: 45 };
33
+ const marks = [
34
+ { id: "runway", x: -25, y: -0.61, side: "top" },
35
+ { id: "take-off-board", x: -0.1, y: 0.6, side: "bottom" },
36
+ { id: "landing-area", x: near + len / 2, y: -1.5, side: "top" },
37
+ ];
38
+ return { parts, facility, box, marks };
39
+ }
40
+ /** The throws: a circle with its sector and, for the discus and the hammer, the cage; the shot's stop board. */
41
+ function throwLayout(event) {
42
+ const parts = emptyParts();
43
+ const d = event === "discus-throw" ? 2.5 : 2.135;
44
+ const reach = SECTOR_REACH[event];
45
+ parts.areas.push(sectorGrass(event, event, reach, SECTOR_ANGLE / 2));
46
+ addParts(parts, circleParts(event, event, O, 0, d));
47
+ if (event === "shot-put")
48
+ parts.areas.push(stopBoard(event, O, 0, d));
49
+ else
50
+ parts.shapes.push(...cageShapes(event, O, 0));
51
+ parts.shapes.push(...sectorShapes(event, event, O, 0, d / 2, reach));
52
+ const half = SECTOR_ANGLE / 2;
53
+ const box = { x0: -8, y0: -reach * Math.sin(half) - 2, x1: reach + 2, y1: reach * Math.sin(half) + 2 };
54
+ const facility = { id: event, event, x: 0, y: 0, heading: 0, box, reach };
55
+ const marks = [
56
+ { id: "throwing-circle", x: 0, y: -d / 2 - 0.6, side: "top" },
57
+ { id: "sector", x: reach * 0.5, y: -reach * 0.5 * Math.tan(half), side: "top" },
58
+ ];
59
+ if (event === "shot-put")
60
+ marks.push({ id: "stop-board", x: d / 2, y: d / 2 + 0.5, side: "bottom" });
61
+ else
62
+ marks.push({ id: "cage", x: 7, y: -3, side: "top" });
63
+ return { parts, facility, box, marks };
64
+ }
65
+ /** The layout of one field event alone. */
66
+ export function layoutOf(event) {
67
+ switch (event) {
68
+ case "long-jump":
69
+ case "triple-jump":
70
+ return jumpLayout(event);
71
+ case "shot-put":
72
+ case "discus-throw":
73
+ case "hammer-throw":
74
+ return throwLayout(event);
75
+ case "javelin-throw": {
76
+ const made = javelinParts("javelin", O, 0);
77
+ const half = Math.asin(2 / 8);
78
+ made.parts.areas.unshift(runwayGround("javelin", "javelin-throw", -36.5 + 8 * Math.cos(half), 8 * Math.cos(half), 4), sectorGrass("javelin", "javelin-throw", 100, half, 8));
79
+ const box = { x0: -18, y0: -100 * Math.sin(half) - 2, x1: 102, y1: 100 * Math.sin(half) + 2 };
80
+ return { parts: made.parts, facility: made.facility, box, marks: [{ id: "runway", x: -20, y: -2.1, side: "top" }, { id: "throwing-arc", x: 8, y: -1.5, side: "top" }, { id: "sector", x: 50, y: -50 * Math.tan(half), side: "top" }] };
81
+ }
82
+ case "high-jump": {
83
+ const made = highJumpParts("high-jump", O, 0);
84
+ made.parts.areas.unshift({ id: "high-jump-runway-ground", feature: "high-jump", cls: "ky-ath-track", d: `M0 -8H${num(-Math.sqrt(15 * 15 - 64))}A15 15 0 0 0 ${num(-Math.sqrt(15 * 15 - 64))} 8H0Z` });
85
+ const box = made.facility.box;
86
+ return { parts: made.parts, facility: made.facility, box: { x0: box.x0 - 1, y0: box.y0 - 1, x1: box.x1 + 1, y1: box.y1 + 1 }, marks: [{ id: "crossbar", x: 0, y: -2, side: "top" }, { id: "landing-mat", x: 1.5, y: 3.2, side: "bottom" }, { id: "run-up", x: -9, y: -8, side: "top" }] };
87
+ }
88
+ default: {
89
+ // The pole vault: a runway of 45 m to the box, the box, the zero line and the mat beyond.
90
+ const made = poleVaultEnd("pole-vault", O, 0);
91
+ made.parts.areas.unshift(runwayGround("pole-vault", "pole-vault", -45, 0, 1.22));
92
+ made.parts.shapes.push(...runwayLines("pole-vault", "pole-vault", [-45, 0], [0, 0]));
93
+ made.parts.shapes.push(line("pole-vault-runway-end", "pole-vault", -45, -0.61, -45, 0.61, { w: 0.05 }));
94
+ const box = bounds([[-24, -3.5], [9, 3.5]]);
95
+ return { parts: made.parts, facility: made.facility, box, marks: [{ id: "runway", x: -25, y: -0.61, side: "top" }, { id: "zero-line", x: 0, y: -2.9, side: "top" }, { id: "landing-mat", x: 4.2, y: 3.1, side: "bottom" }, { id: "box", x: -0.5, y: 0.5, side: "bottom" }] };
96
+ }
97
+ }
98
+ }