@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,159 @@
1
+ import { el, num } from "../format.js";
2
+ import { feet } from "../pool.js";
3
+ import { box, deckMarkup, leastWidth, paintDefs, poolIds, ropeMarkup, waterMarkup } from "../pool.kit.js";
4
+ import { WATER_POLO } from "../pool.figures.js";
5
+ import { GUTTER } from "./pool.js";
6
+ import { atEnds, edges, ft, quarterTurn } from "./surface.js";
7
+ /** The space behind each goal line where the goal's net hangs, in metres (Part Six 21.6.5 and 21.7.2.1: the goal at least 0.3 m deep, the lane rope 0.3 m behind the goal line): the pool is the field of play, 25 m between the goal lines (Part Six 20.2.2.1), and this behind each of them. */
8
+ export const POLO_BEHIND = WATER_POLO.goal.netClear;
9
+ /**
10
+ * A water polo field of play lying wide, from World Aquatics' rules: 25 by 20 m between the goal lines; lane ropes along both sides whose floats are white at the goal line and the half distance line, red to
11
+ * the 2 m mark, yellow from 2 to 6 m and green from 6 m to the half distance line, with a red mark at 5 m (Part Six 21.7.1 and 21.7.2); the 2 m lines from the goal posts that make the goal areas
12
+ * (Part Six 20.4 and 21.7.2.1.2); the goals, 3 m between the posts (21.6.3.2); the re-entry area 2.0 by 1.08 m in each corner on the side away from the official table (20.7); the referees' platforms 1 m wide along both sides, coloured
13
+ * as the ropes are (21.9.3); the official table (21.10) and the area for flying substitutions on the side of the benches (20.6).
14
+ */
15
+ export function waterPolo(o, plan, ctx = {}) {
16
+ const Lp = plan.length;
17
+ const W = plan.width;
18
+ const b = POLO_BEHIND;
19
+ const ids = ctx.ids ?? poolIds(o.id);
20
+ const full = o.detail === "full";
21
+ const m = ctx.bare === true ? 0 : (o.margin ?? feet(WATER_POLO.deck));
22
+ const platform = WATER_POLO.platform.width;
23
+ const down = plan.table === "bottom";
24
+ // The goal lines stand 0.30 m in from the walls; the marks are measured from them.
25
+ const g0 = b;
26
+ const g1 = Lp - b;
27
+ const at = (d, from) => (from === "left" ? g0 + d : g1 - d);
28
+ const deckTop = GUTTER + platform;
29
+ const deckBottom = GUTTER + platform + (down ? WATER_POLO.table.depth + 0.6 : 0);
30
+ let base = box(o, "gutter", "ky-boards", -GUTTER, -GUTTER, Lp + GUTTER, W + GUTTER);
31
+ base += waterMarkup(o, ids, plan.look, 0, 0, Lp, W);
32
+ // The referees' platforms along both sides, in the colours of the ropes by distance from the goal line.
33
+ const bands = [
34
+ ["red", 0, WATER_POLO.twoMetre],
35
+ ["yellow", WATER_POLO.twoMetre, WATER_POLO.sixMetre],
36
+ ["green", WATER_POLO.sixMetre, WATER_POLO.half],
37
+ ];
38
+ let walk = "";
39
+ for (const [y0, y1] of [[-GUTTER - platform, -GUTTER], [W + GUTTER, W + GUTTER + platform]]) {
40
+ for (const [colour, a, z] of bands) {
41
+ for (const from of ["left", "right"]) {
42
+ const x0 = from === "left" ? at(a, "left") : at(z, "right");
43
+ const x1 = from === "left" ? at(z, "left") : at(a, "right");
44
+ walk += el("rect", { class: ft(o, "referee-platforms", `ky-pl-rp ky-pl-rp-${colour}`), x: feet(x0), y: feet(y0), width: feet(x1 - x0), height: feet(y1 - y0) });
45
+ }
46
+ }
47
+ }
48
+ base += walk;
49
+ let lines = "";
50
+ const thin = leastWidth(o, 0.06, 0.12);
51
+ // The lines across the field: the goal lines and the half distance line white, the 2 m line red, the 5 m line a dashed red one (a red mark at 5 m), the 6 m line yellow.
52
+ const across = (cls, colour, x, dash, w = thin) => el("path", { class: ft(o, cls, `ky-pl-wl ky-pl-wl-${colour}`), d: `M${num(feet(x))} 0V${num(feet(W))}`, "stroke-width": w, "stroke-dasharray": dash, fill: "none" });
53
+ lines += across("goal-lines", "white", g0, undefined, thin * 1.4) + across("goal-lines", "white", g1, undefined, thin * 1.4) + across("half-distance-line", "white", Lp / 2, undefined, thin * 1.4);
54
+ for (const from of ["left", "right"]) {
55
+ lines += across("two-metre-lines", "red", at(WATER_POLO.twoMetre, from));
56
+ lines += across("six-metre-lines", "yellow", at(WATER_POLO.sixMetre, from));
57
+ lines += across("five-metre-marks", "red", at(WATER_POLO.fiveMetre, from), `${num(feet(0.5))} ${num(feet(0.5))}`);
58
+ }
59
+ // The goal areas: red lines 2 m long from the outer faces of the goal posts (Part Six 20.4.1 and 21.7.2.1.2).
60
+ const post = WATER_POLO.goal.post;
61
+ const half = WATER_POLO.goal.width / 2;
62
+ for (const from of ["left", "right"]) {
63
+ const x0 = at(0, from);
64
+ const x1 = at(WATER_POLO.goalAreaLine, from);
65
+ for (const side of [-1, 1]) {
66
+ const y = W / 2 + side * (half + post);
67
+ lines += el("path", { class: ft(o, "goal-area-lines", "ky-pl-wl ky-pl-wl-red"), d: `M${num(feet(x0))} ${num(feet(y))}H${num(feet(x1))}`, "stroke-width": thin, fill: "none" });
68
+ }
69
+ }
70
+ // The re-entry areas, 2.0 m along the goal line and 1.08 m into the field, in each corner on the side away from the table.
71
+ const away = down ? 0 : W;
72
+ const dirY = down ? 1 : -1;
73
+ for (const from of ["left", "right"]) {
74
+ const x0 = at(0, from);
75
+ const x1 = at(WATER_POLO.reEntry.depth, from);
76
+ const xa = Math.min(x0, x1);
77
+ const ya = Math.min(away, away + dirY * WATER_POLO.reEntry.length);
78
+ lines += el("rect", { class: ft(o, "re-entry-areas", "ky-pl-ra"), x: feet(xa), y: feet(ya), width: feet(Math.abs(x1 - x0)), height: feet(WATER_POLO.reEntry.length) });
79
+ }
80
+ // The lane ropes along the sides: white to the goal line, then red, yellow and green, with the red mark at 5 m, and white at the half distance line.
81
+ const runs = [];
82
+ const inset = 0.1;
83
+ for (const y of [inset, W - inset]) {
84
+ runs.push(["white", 0, g0, y], ["white", g1, Lp, y]);
85
+ for (const [colour, a, z] of bands) {
86
+ runs.push([colour, at(a, "left"), at(z, "left"), y]);
87
+ runs.push([colour, at(z, "right"), at(a, "right"), y]);
88
+ }
89
+ runs.push(["white", Lp / 2 - 0.45, Lp / 2 + 0.45, y]);
90
+ for (const from of ["left", "right"]) {
91
+ runs.push(["white", at(0, from) - 0.3, at(0, from) + 0.3, y]);
92
+ runs.push(["red", at(WATER_POLO.fiveMetre, from) - 0.3, at(WATER_POLO.fiveMetre, from) + 0.3, y]);
93
+ }
94
+ }
95
+ lines += ropeMarkup(o, runs, WATER_POLO.rope, 0.42);
96
+ // The goals: two posts and the crossbar between them on each goal line, white, with the net behind the line. The posts and the bar are drawn thicker than they stand, as the lines are.
97
+ const drawn = o.exact ? post : Math.max(post, 0.32);
98
+ for (const from of ["left", "right"]) {
99
+ const x = at(0, from);
100
+ const inward = from === "left" ? 1 : -1;
101
+ lines += el("path", { class: ft(o, "goals", "ky-pl-gn"), d: `M${num(feet(x))} ${num(feet(W / 2 - half))}h${num(feet(-inward * b))}v${num(feet(2 * half))}h${num(feet(inward * b))}`, "stroke-width": feet(0.09), fill: "none" });
102
+ lines += el("path", { class: ft(o, "goals", "ky-pl-gb"), d: `M${num(feet(x))} ${num(feet(W / 2 - half))}V${num(feet(W / 2 + half))}`, "stroke-width": feet(drawn) * 0.8, fill: "none" });
103
+ for (const side of [-1, 1]) {
104
+ const y = W / 2 + side * (half + post / 2);
105
+ lines += el("rect", { class: ft(o, "goal-posts", "ky-pl-gp"), x: feet(x - drawn / 2 - (inward * drawn) / 2 + (inward * drawn) / 2), y: feet(y - drawn / 2), width: feet(drawn), height: feet(drawn), rx: feet(drawn) * 0.12 });
106
+ }
107
+ }
108
+ // The official table and the flying substitution areas on the side of the benches.
109
+ if (down) {
110
+ const t = WATER_POLO.table;
111
+ base += el("rect", { class: ft(o, "official-table", "ky-pl-tb"), x: feet(Lp / 2 - t.length / 2), y: feet(W + GUTTER + platform + 0.5), width: feet(t.length), height: feet(t.depth), rx: feet(0.12) });
112
+ }
113
+ const sub = WATER_POLO.substitution;
114
+ if (full) {
115
+ const y0 = down ? W + GUTTER + platform : -GUTTER - platform - sub;
116
+ base += el("rect", { class: ft(o, "substitution-areas", "ky-pl-sa-l"), x: feet(g0), y: feet(y0), width: feet(Lp / 2 - g0), height: feet(sub) });
117
+ base += el("rect", { class: ft(o, "substitution-areas", "ky-pl-sa-r"), x: feet(Lp / 2), y: feet(y0), width: feet(g1 - Lp / 2), height: feet(sub) });
118
+ }
119
+ // The names of the parts, for a drawing that labels them.
120
+ const marks = [];
121
+ if (o.landmarks === true) {
122
+ marks.push({ id: "goal-line", x: feet(g0), y: 0, side: "top" }, { id: "two-metre-line", x: feet(at(WATER_POLO.twoMetre, "left")), y: feet(W), side: "bottom" }, { id: "five-metre-mark", x: feet(at(WATER_POLO.fiveMetre, "left")), y: 0, side: "top" }, { id: "six-metre-line", x: feet(at(WATER_POLO.sixMetre, "left")), y: feet(W), side: "bottom" }, { id: "half-distance-line", x: feet(Lp / 2), y: 0, side: "top" }, { id: "goal", x: feet(g1), y: feet(W / 2), side: "bottom" }, { id: "re-entry-area", x: feet(at(0.5, "right")), y: feet(down ? 0.5 : W - 0.5), side: down ? "top" : "bottom" }, { id: "referee-platform", x: feet(Lp * 0.3), y: feet(down ? W + GUTTER + platform / 2 : -GUTTER - platform / 2), side: down ? "bottom" : "top" });
123
+ if (down)
124
+ marks.push({ id: "official-table", x: feet(Lp / 2), y: feet(W + GUTTER + platform + 0.5 + WATER_POLO.table.depth), side: "bottom" });
125
+ }
126
+ const view0 = { x: -m, y: -feet(deckTop) - m, w: feet(Lp) + 2 * m, h: feet(W) + feet(deckTop + deckBottom) + 2 * m };
127
+ // The deck is under everything.
128
+ if (ctx.bare !== true)
129
+ base = deckMarkup(o, ids, plan.deck, view0.x, view0.y, view0.w, view0.h) + base;
130
+ const L = feet(Lp);
131
+ const Wf = feet(W);
132
+ const cy = Wf / 2;
133
+ const turned = o.portrait;
134
+ const surface = {
135
+ sport: "waterpolo",
136
+ view: view0,
137
+ base,
138
+ lines,
139
+ defs: ctx.bare === true ? "" : paintDefs(ids, plan.look, plan.deck, plan.seed),
140
+ left: { cx: -m / 2, cy, w: Wf, h: m, rot: -90 },
141
+ right: { cx: L + m / 2, cy, w: Wf, h: m, rot: 90 },
142
+ plate: turned ? { cx: L / 2, cy, w: Wf * 0.9, h: L * 0.3 } : { cx: L / 2, cy, w: L * 0.3, h: Wf * 0.5 },
143
+ turned,
144
+ };
145
+ surface.box = { x: 0, y: 0, w: L, h: Wf };
146
+ surface.lineRef = { width: thin, feet: 1 };
147
+ surface.ranges = {
148
+ half: atEnds((end) => (end === "left" ? edges(0, 0, L / 2, Wf) : edges(L / 2, 0, L, Wf))),
149
+ "goal-area": atEnds((end) => (end === "left" ? edges(0, 0, feet(g0 + WATER_POLO.twoMetre + 0.5), Wf) : edges(L - feet(Lp - g1 + WATER_POLO.twoMetre + 0.5), 0, L, Wf))),
150
+ };
151
+ if (o.landmarks === true)
152
+ surface.marks = marks;
153
+ if (turned) {
154
+ const t = quarterTurn(view0);
155
+ surface.view = t.view;
156
+ surface.turn = t.turn;
157
+ }
158
+ return surface;
159
+ }
@@ -0,0 +1,27 @@
1
+ import type { SwimPlan } from "../pool.ts";
2
+ import { type PoolCtx, type PoolIds } from "../pool.kit.ts";
3
+ import { type BuildOptions, type Mark, type Surface } from "./surface.ts";
4
+ /** The swimming pool lying wide, before its deck and view: the water and the gutter, and the lines, ropes and fixtures over them, in feet from the pool's top left corner. */
5
+ export interface SwimBody {
6
+ /** The gutter and the water. */
7
+ base: string;
8
+ /** The markings on the floor, the ropes and the fixtures. */
9
+ lines: string;
10
+ /** The labels of the parts to name, in feet. */
11
+ marks: Mark[];
12
+ /** Where the lane markings end: kept for the lineRef. */
13
+ line: number;
14
+ }
15
+ /** The gutter round the water, in metres: the width of the coping the pool is drawn with. */
16
+ export declare const GUTTER = 0.4;
17
+ /**
18
+ * The body of a swimming pool: every figure is the rules' (`SWIMMING`, pool.figures.ts). The lane markings on the floor are 46 m (21 m in a 25 m pool) long and end 2 m from the walls in a cross line 1 m long (Part Two 15.13.1 and 15.13.2); a
19
+ * cross line 0.5 m long at 15 m from each end; the touch panels 2.4 m wide at the walls with the target lines; the ropes in the rules' colours with red floats within 5 m of the walls and a mark at 15 m and 25 m;
20
+ * the starting blocks with their lane numbers; the backstroke flags 5 m from each end, and the false start rope 15 m from the starting end.
21
+ */
22
+ export declare function swimBody(o: BuildOptions, plan: SwimPlan, ids: PoolIds): SwimBody;
23
+ /**
24
+ * A swimming pool lying wide with its deck: the body, the deck round it in the colour or the tiles asked for, the data of a race at the walls and over the water, and a side section under the plan if
25
+ * it is asked for. The pool's water is the surface's `box`; the deck is the margin.
26
+ */
27
+ export declare function swimming(o: BuildOptions, plan: SwimPlan, ctx?: PoolCtx): Surface;
@@ -0,0 +1,198 @@
1
+ import { el, num } from "../format.js";
2
+ import { feet } from "../pool.js";
3
+ import { box, deckMarkup, leastWidth, paintDefs, poolIds, ropeMarkup, waterMarkup } from "../pool.kit.js";
4
+ import { raceArt } from "../pool.labels.js";
5
+ import { sectionOf } from "../pool.section.js";
6
+ import { SWIMMING } from "../pool.figures.js";
7
+ import { atEnds, edges, ft, quarterTurn, textOn } from "./surface.js";
8
+ /** The gutter round the water, in metres: the width of the coping the pool is drawn with. */
9
+ export const GUTTER = 0.4;
10
+ /**
11
+ * The body of a swimming pool: every figure is the rules' (`SWIMMING`, pool.figures.ts). The lane markings on the floor are 46 m (21 m in a 25 m pool) long and end 2 m from the walls in a cross line 1 m long (Part Two 15.13.1 and 15.13.2); a
12
+ * cross line 0.5 m long at 15 m from each end; the touch panels 2.4 m wide at the walls with the target lines; the ropes in the rules' colours with red floats within 5 m of the walls and a mark at 15 m and 25 m;
13
+ * the starting blocks with their lane numbers; the backstroke flags 5 m from each end, and the false start rope 15 m from the starting end.
14
+ */
15
+ export function swimBody(o, plan, ids) {
16
+ const L = plan.length;
17
+ const W = plan.width;
18
+ const full = o.detail === "full";
19
+ // Small fixtures are drawn a little larger than they stand, as lines are, unless the true sizes are asked for.
20
+ const s = o.exact ? 1 : 1.5;
21
+ let base = box(o, "gutter", "ky-boards", -GUTTER, -GUTTER, L + GUTTER, W + GUTTER);
22
+ base += waterMarkup(o, ids, plan.look, 0, 0, L, W);
23
+ let lines = "";
24
+ const lw = leastWidth(o, SWIMMING.laneLine.width, 0.32);
25
+ const marks = [];
26
+ // Under pace bars the lanes are the whole race laid flat, not one length of the pool: the furniture that belongs to a length (floor lines and crosses, the marks at 15 m and 25 m, the red ends of the ropes,
27
+ // flags, the false start rope, blocks and panels) is left off and the ropes are plain, so that the picture reads as a chart.
28
+ const neutral = plan.race?.pace !== null && plan.race?.pace !== undefined;
29
+ // The lane markings and their cross lines on the floor: a line along the middle of each lane, 2 m short of each wall, ending in a cross line 1 m long.
30
+ const gap = SWIMMING.laneLine.endGap;
31
+ const half = SWIMMING.laneLine.cross / 2;
32
+ let d = "";
33
+ let crosses = "";
34
+ for (const lane of plan.lanes) {
35
+ d += `M${num(feet(gap))} ${num(feet(lane.y))}H${num(feet(L - gap))}`;
36
+ d += `M${num(feet(gap))} ${num(feet(lane.y - half))}V${num(feet(lane.y + half))}M${num(feet(L - gap))} ${num(feet(lane.y - half))}V${num(feet(lane.y + half))}`;
37
+ if (full)
38
+ for (const x of [SWIMMING.markAt15, L - SWIMMING.markAt15])
39
+ crosses += `M${num(feet(x))} ${num(feet(lane.y - SWIMMING.target.crossAt15 / 2))}V${num(feet(lane.y + SWIMMING.target.crossAt15 / 2))}`;
40
+ }
41
+ if (!neutral)
42
+ lines += el("path", { class: ft(o, "lane-lines", "ky-line"), d: d + crosses, "stroke-width": lw, fill: "none" });
43
+ // The touch panels on the end walls, 2.4 m wide in each lane, with the target line down the middle and its cross line.
44
+ if (plan.pads && !neutral) {
45
+ const depth = o.exact ? 0.05 : 0.3;
46
+ const pad = SWIMMING.pad.width;
47
+ let pads = "";
48
+ let targets = "";
49
+ for (const lane of plan.lanes) {
50
+ for (const end of [0, 1]) {
51
+ const x0 = end === 0 ? 0 : L - depth;
52
+ pads += `M${num(feet(x0))} ${num(feet(lane.y - pad / 2))}h${num(feet(depth))}v${num(feet(pad))}h${num(feet(-depth))}Z`;
53
+ const wall = end === 0 ? 0 : L;
54
+ const inward = end === 0 ? 1 : -1;
55
+ targets += `M${num(feet(wall))} ${num(feet(lane.y))}h${num(feet(inward * depth))}`;
56
+ if (full)
57
+ targets += `M${num(feet(wall + inward * depth * 0.5))} ${num(feet(lane.y - SWIMMING.target.cross / 2))}v${num(feet(SWIMMING.target.cross))}`;
58
+ }
59
+ }
60
+ lines += el("path", { class: ft(o, "touch-pads", "ky-pl-pad"), d: pads });
61
+ lines += el("path", { class: ft(o, "lane-lines", "ky-line"), d: targets, "stroke-width": lw * 0.85, fill: "none" });
62
+ }
63
+ // The ropes: floats in the lane's colour, red within 5 m of each wall, and a distinct run at 15 m and 25 m from the walls.
64
+ const runs = [];
65
+ const red = SWIMMING.redEnds;
66
+ const run = SWIMMING.markRun / 2;
67
+ for (const rope of plan.ropes) {
68
+ if (neutral) {
69
+ runs.push(["white", 0, L, rope.y]);
70
+ continue;
71
+ }
72
+ runs.push([rope.colour, red, L - red, rope.y]);
73
+ runs.push(["red", 0, red, rope.y], ["red", L - red, L, rope.y]);
74
+ const at = [SWIMMING.markAt15, L - SWIMMING.markAt15, ...(plan.course === "long" ? [SWIMMING.markAt25] : [])];
75
+ for (const x of at)
76
+ runs.push(["white", Math.max(0, x - run), Math.min(L, x + run), rope.y]);
77
+ }
78
+ lines += ropeMarkup(o, runs, SWIMMING.floatDiameter, 0.45);
79
+ // The marks on the deck at 15 m (and 25 m) from the walls, and the standards of the flags: on both sides of the pool (Part Two 15.9.1).
80
+ if (full && !neutral) {
81
+ let ticks = "";
82
+ for (const x of [SWIMMING.markAt15, L - SWIMMING.markAt15, ...(plan.course === "long" ? [SWIMMING.markAt25] : [])]) {
83
+ for (const [y0, y1] of [[-GUTTER, 0], [W, W + GUTTER]])
84
+ ticks += `M${num(feet(x))} ${num(feet(y0))}V${num(feet(y1))}`;
85
+ }
86
+ lines += el("path", { class: ft(o, "lane-lines", "ky-line"), d: ticks, "stroke-width": feet(0.22), fill: "none" });
87
+ }
88
+ // The false start rope, 15 m from the starting end, on its standards.
89
+ if (plan.falseStart && !neutral) {
90
+ const x = SWIMMING.falseStart.at;
91
+ lines += el("path", { class: ft(o, "false-start-rope", "ky-pl-fs"), d: `M${num(feet(x))} ${num(feet(-0.7))}V${num(feet(W + 0.7))}`, "stroke-width": feet(0.1) * s, fill: "none", "stroke-dasharray": `${num(feet(0.5))} ${num(feet(0.3))}` });
92
+ if (full)
93
+ for (const y of [-0.7, W + 0.7])
94
+ lines += el("circle", { class: "ky-pl-st", cx: feet(x), cy: feet(y), r: feet(0.22) * s });
95
+ }
96
+ // The backstroke flags: a rope across the pool 5 m from each wall, flags of two colours hanging from it.
97
+ if (plan.flags && !neutral) {
98
+ for (const x of [SWIMMING.flags.at, L - SWIMMING.flags.at]) {
99
+ const fx = feet(x);
100
+ lines += el("path", { class: ft(o, "backstroke-flags", "ky-pl-fa"), d: `M${num(fx)} ${num(feet(-0.6))}V${num(feet(W + 0.6))}`, "stroke-width": feet(0.26) * s, fill: "none", "stroke-dasharray": `${num(feet(0.4))} ${num(feet(0.4))}` });
101
+ lines += el("path", { class: ft(o, "backstroke-flags", "ky-pl-fb2"), d: `M${num(fx)} ${num(feet(-0.6))}V${num(feet(W + 0.6))}`, "stroke-width": feet(0.26) * s, fill: "none", "stroke-dasharray": `0 ${num(feet(0.4))} ${num(feet(0.4))} 0` });
102
+ if (full)
103
+ for (const y of [-0.6, W + 0.6])
104
+ lines += el("circle", { class: "ky-pl-st", cx: fx, cy: feet(y), r: feet(0.22) * s });
105
+ }
106
+ }
107
+ // The starting blocks, 0.5 by 0.5 m (Part Two 15.8.1 asks at least that; the June 2025 edition asked 0.6 m long at the Olympic Games and the World Championships), one in each lane at each end, numbered on the top (Part Two 15.8.1 and 15.8.6).
108
+ if (plan.blocks && !neutral) {
109
+ const bw = SWIMMING.block.width * s;
110
+ const bl = SWIMMING.block.length * s;
111
+ let blocks = "";
112
+ let numbers = "";
113
+ for (const lane of plan.lanes) {
114
+ for (const end of [0, 1]) {
115
+ const x0 = end === 0 ? -0.08 - bl : L + 0.08;
116
+ blocks += `M${num(feet(x0))} ${num(feet(lane.y - bw / 2))}h${num(feet(bl))}v${num(feet(bw))}h${num(feet(-bl))}Z`;
117
+ if (full)
118
+ numbers += textOn(o.portrait, feet(x0 + bl / 2), feet(lane.y) + feet(bw) * 0.32, String(lane.number), { size: feet(bw) * 0.72, anchor: "middle", cls: "ky-pl-bn", weight: 800 });
119
+ }
120
+ }
121
+ lines += el("path", { class: ft(o, "starting-blocks", "ky-pl-bk"), d: blocks });
122
+ lines += numbers;
123
+ }
124
+ if (o.landmarks === true) {
125
+ const top = plan.lanes[0];
126
+ const mid = plan.lanes[Math.floor(plan.lanes.length / 2)];
127
+ const ropeY = plan.ropes[1]?.y ?? plan.ropes[0].y;
128
+ marks.push({ id: "lane-rope", x: feet(L * 0.62), y: feet(ropeY), side: "top" });
129
+ if (!neutral) {
130
+ marks.push({ id: "lane-line", x: feet(L * 0.35), y: feet(mid.y), side: "bottom" }, { id: "lane-end-cross", x: feet(SWIMMING.laneLine.endGap), y: feet(top.y), side: "top" }, { id: "fifteen-metre-mark", x: feet(SWIMMING.markAt15), y: 0, side: "top" });
131
+ }
132
+ if (plan.pads && !neutral)
133
+ marks.push({ id: "touch-pad", x: feet(0.15), y: feet(plan.lanes[plan.lanes.length - 1].y), side: "bottom" });
134
+ if (plan.blocks && !neutral)
135
+ marks.push({ id: "starting-block", x: feet(-0.6), y: feet(plan.lanes[plan.lanes.length - 1].y), side: "bottom" });
136
+ if (plan.flags && !neutral)
137
+ marks.push({ id: "backstroke-flags", x: feet(SWIMMING.flags.at), y: feet(W), side: "bottom" });
138
+ if (plan.falseStart && !neutral)
139
+ marks.push({ id: "false-start-rope", x: feet(SWIMMING.falseStart.at), y: feet(W), side: "bottom" });
140
+ marks.push({ id: "gutter", x: feet(L * 0.8), y: feet(W + GUTTER / 2), side: "bottom" });
141
+ }
142
+ return { base, lines, marks, line: lw };
143
+ }
144
+ /**
145
+ * A swimming pool lying wide with its deck: the body, the deck round it in the colour or the tiles asked for, the data of a race at the walls and over the water, and a side section under the plan if
146
+ * it is asked for. The pool's water is the surface's `box`; the deck is the margin.
147
+ */
148
+ export function swimming(o, plan, ctx = {}) {
149
+ const L = feet(plan.length);
150
+ const W = feet(plan.width);
151
+ const ids = ctx.ids ?? poolIds(o.id);
152
+ const m = ctx.bare === true ? 0 : (o.margin ?? feet(SWIMMING.deck));
153
+ const body = swimBody(o, plan, ids);
154
+ const art = ctx.bare === true ? raceArt({ ...plan, race: undefined }, o) : raceArt(plan, o);
155
+ const section = ctx.bare === true ? sectionOf({ ...plan, section: undefined }, o, 0) : sectionOf(plan, o, W + feet(GUTTER) + m);
156
+ // The view: the deck all round, the plates at the walls, the title or the distances above, the section below.
157
+ // The depth of a section is written to the left of it.
158
+ const left = m + Math.max(art.reachLeft, section.height > 0 ? feet(4) : 0);
159
+ const right = m + art.reachRight;
160
+ const topExtra = Math.max(0, art.reachTop, plan.race?.event === undefined ? 0 : feet(1.6));
161
+ const view0 = { x: -left, y: -m - topExtra, w: L + left + right, h: W + 2 * m + topExtra + (section.height > 0 ? section.height + feet(0.6) : 0) };
162
+ const defs = ctx.bare === true ? "" : paintDefs(ids, plan.look, plan.deck, plan.seed) + section.defs;
163
+ const basis = (ctx.bare === true ? "" : deckMarkup(o, ids, plan.deck, view0.x, view0.y, view0.w, view0.h)) + body.base;
164
+ let lines = body.lines + art.pace + art.left + art.right + art.top;
165
+ if (section.markup !== "")
166
+ lines += section.markup;
167
+ const turned = o.portrait;
168
+ const cy = W / 2;
169
+ // How far the blocks reach beyond the walls, in feet.
170
+ const blockReach = feet(0.08 + SWIMMING.block.length * (o.exact ? 1 : 1.5));
171
+ const plate = turned ? { cx: L / 2, cy, w: W * 0.9, h: L * 0.34 } : { cx: L / 2, cy, w: L * 0.34, h: W * 0.62 };
172
+ const surface = {
173
+ sport: "swimming",
174
+ view: view0,
175
+ base: basis,
176
+ lines,
177
+ defs,
178
+ // The teams' names stand on the deck beyond the blocks.
179
+ left: { cx: -(blockReach + (m - blockReach) / 2), cy, w: W, h: Math.max(1, m - blockReach), rot: -90 },
180
+ right: { cx: L + blockReach + (m - blockReach) / 2, cy, w: W, h: Math.max(1, m - blockReach), rot: 90 },
181
+ plate,
182
+ turned,
183
+ };
184
+ surface.box = { x: 0, y: 0, w: L, h: W };
185
+ surface.lineRef = { width: body.line, feet: 1 };
186
+ surface.ranges = {
187
+ half: atEnds((end) => (end === "left" ? edges(0, 0, L / 2, W) : edges(L / 2, 0, L, W))),
188
+ "pool-end": atEnds((end) => (end === "left" ? edges(0, 0, feet(15), W) : edges(L - feet(15), 0, L, W))),
189
+ };
190
+ if (o.landmarks === true)
191
+ surface.marks = o.detail === "full" ? body.marks : body.marks.filter((mark) => !["lane-end-cross", "fifteen-metre-mark"].includes(mark.id));
192
+ if (turned) {
193
+ const t = quarterTurn(view0);
194
+ surface.view = t.view;
195
+ surface.turn = t.turn;
196
+ }
197
+ return surface;
198
+ }
@@ -0,0 +1,6 @@
1
+ import type { SkatingPlan } from "../skating/plan.ts";
2
+ import { type BuildOptions, type Surface } from "./surface.ts";
3
+ /** Feet in a metre. */
4
+ export declare const K: number;
5
+ /** The surface of the plan lying wide. `margin` is the ground round what is drawn, in feet. */
6
+ export declare function skatingSurface(o: BuildOptions, plan: SkatingPlan, margin: number): Surface;
@@ -0,0 +1,62 @@
1
+ // The skating rink's surface lying wide, from its plan (skating/plan.ts): the ice, the floor of the infield, the warm-up lane, the padding, the boards, the furniture, then every painted line and the
2
+ // blocks. The plan is in metres; the drawing is in feet, as every surface lies, so the whole is drawn inside a group that scales metres to feet and moves the rink's box to the origin.
3
+ import { el } from "../format.js";
4
+ import { areaD, marks, strokes } from "./field.js";
5
+ import { atEnds, edges, ft, quarterTurn } from "./surface.js";
6
+ /** Feet in a metre. */
7
+ export const K = 1 / 0.3048;
8
+ const feetBox = (b, ox, oy) => edges((b.x0 - ox) * K, (b.y0 - oy) * K, (b.x1 - ox) * K, (b.y1 - oy) * K);
9
+ /** A plan's named range in feet, as a surface lists them. */
10
+ function rangeOf(r, ox, oy) {
11
+ return "left" in r ? atEnds((end) => feetBox(end === "left" ? r.left : r.right, ox, oy)) : feetBox(r, ox, oy);
12
+ }
13
+ /** The surface of the plan lying wide. `margin` is the ground round what is drawn, in feet. */
14
+ export function skatingSurface(o, plan, margin) {
15
+ const ox = plan.box.x0;
16
+ const oy = plan.box.y0;
17
+ const full = o.detail === "full";
18
+ const view0 = { x: (plan.extent.x0 - ox) * K - margin, y: (plan.extent.y0 - oy) * K - margin, w: (plan.extent.x1 - plan.extent.x0) * K + 2 * margin, h: (plan.extent.y1 - plan.extent.y0) * K + 2 * margin };
19
+ const place = `scale(${K.toFixed(6)}) translate(${(-ox).toFixed(4)} ${(-oy).toFixed(4)})`;
20
+ const group = (inner, cls) => el("g", { class: cls, transform: place }, inner);
21
+ const turned = o.portrait;
22
+ // Under everything: the apron, then the ice, the floor, the warm-up lane, the padding and the furniture, in the plan's order.
23
+ let base = el("rect", { class: ft(o, "apron", "ky-apron"), x: view0.x, y: view0.y, width: view0.w, height: view0.h });
24
+ let ground = "";
25
+ for (const a of plan.areas)
26
+ ground += el("path", { class: ft(o, a.feature, a.cls), d: areaD(a), "fill-rule": a.evenodd === true ? "evenodd" : undefined });
27
+ base += group(ground, "ky-ska-ground");
28
+ const thick = o.exact ? plan.lineWidth : plan.drawnLine;
29
+ const lines = group(strokes(plan, o) + marks(plan, o), "ky-ska-lines");
30
+ const W = (plan.box.x1 - plan.box.x0) * K;
31
+ const H = (plan.box.y1 - plan.box.y0) * K;
32
+ const surface = {
33
+ sport: "skating",
34
+ view: view0,
35
+ base,
36
+ lines,
37
+ defs: "",
38
+ // A team's name, in a score, is written on the ice either side of the middle.
39
+ left: { cx: W * 0.27, cy: H / 2, w: W * 0.2, h: H * 0.11, rot: 0 },
40
+ right: { cx: W * 0.73, cy: H / 2, w: W * 0.2, h: H * 0.11, rot: 0 },
41
+ plate: { cx: W / 2, cy: H / 2, w: Math.min(W * 0.34, 140), h: Math.min(W * 0.34, 140) * 0.52 },
42
+ turned,
43
+ bannersOver: true,
44
+ onColour: false,
45
+ };
46
+ surface.box = { x: 0, y: 0, w: W, h: H };
47
+ surface.lineRef = { width: thick, feet: K };
48
+ const ranges = {};
49
+ for (const [name, r] of Object.entries(plan.ranges))
50
+ ranges[name] = rangeOf(r, ox, oy);
51
+ surface.ranges = ranges;
52
+ if (o.landmarks === true) {
53
+ const m = plan.marks.map((p) => ({ id: p.id, x: (p.x - ox) * K, y: (p.y - oy) * K, side: p.side }));
54
+ surface.marks = full ? m : m.filter((p) => !p.id.startsWith("~"));
55
+ }
56
+ if (turned) {
57
+ const t = quarterTurn(view0);
58
+ surface.view = t.view;
59
+ surface.turn = t.turn;
60
+ }
61
+ return surface;
62
+ }
@@ -140,7 +140,7 @@ export function soccer(b, o) {
140
140
  // The ends in the team's colour, in a score: the penalty area (or what a small pitch has in its place).
141
141
  const zoneDepth = Math.max(spec.penaltyDepth, L * 0.12);
142
142
  const zoneWidth = Math.max(spec.area === "arcs" ? spec.areaLine + 2 * spec.penaltyDepth : spec.area === "semicircle" ? 2 * spec.penaltyDepth : lay.penaltyHalfWidth * 2, W * 0.4);
143
- if (o.score) {
143
+ if (o.score && o.pageant !== true) {
144
144
  // A rectangle at each end: the penalty area on a full pitch, a box that holds the name on the others (their areas are curved).
145
145
  for (const side of [0, 1])
146
146
  inside += el("rect", { class: `ky-zone ky-zone-${side === 0 ? "left" : "right"}`, x: side === 0 ? 0 : L - zoneDepth, y: cy - zoneWidth / 2, width: zoneDepth, height: zoneWidth });
@@ -231,6 +231,7 @@ export function soccer(b, o) {
231
231
  };
232
232
  surface.lineRef = { width: lw, feet: K };
233
233
  surface.box = { x: 0, y: 0, w: Lf, h: Wf };
234
+ surface.centreRadius = spec.centreRadius * K;
234
235
  // The penalty area reaches as far as its lines do: the area, the arc beyond it and a small pitch's curved area; and a little more.
235
236
  const arcBeyond = spec.arcRadius > 0 && lay.spot > 0 && lay.penaltyDepth > lay.spot ? Math.max(0, spec.arcRadius - (lay.penaltyDepth - lay.spot)) : 0;
236
237
  const curved = spec.area === "arcs" || spec.area === "semicircle" ? spec.penaltyDepth : 0;
@@ -1,4 +1,5 @@
1
1
  import type { Ballpark } from "../ballpark.ts";
2
+ import type { Hardwood } from "../hardwood.ts";
2
3
  import type { Box, Detail, GoalInfo, GoalStyle, Sport, StripePattern } from "../types.ts";
3
4
  /** Where a team's name and logo go, and how its words lie. */
4
5
  export interface Zone {
@@ -20,11 +21,61 @@ export interface PlateSpot {
20
21
  w: number;
21
22
  h: number;
22
23
  }
24
+ /** A place on a surface for a pageant's picture, in the surface's frame in feet; a `disc` is the diameter of the round room the picture must lie in (a curling house). */
25
+ export interface PageantSpot extends PlateSpot {
26
+ disc?: number;
27
+ }
28
+ /** What a surface says of where a pageant's pictures go, when it is not the plain halves and the middle. */
29
+ export interface PageantPlan {
30
+ /** Where the first (left) team's picture goes, and the second's (right), when not in the halves: a diamond's outfield and infield, a sheet's two houses. */
31
+ left?: PageantSpot;
32
+ right?: PageantSpot;
33
+ /** The emblem's place: its centre and the radius of the circle it lies in; the middle of the surface, with the centre circle's radius, when missing. */
34
+ centre?: {
35
+ cx: number;
36
+ cy: number;
37
+ r: number;
38
+ };
39
+ /** Half the length, along the surface, that the pictures keep clear in the middle (a cricket pitch's). */
40
+ clear?: number;
41
+ /** The first picture is the away team's and the second the home team's, whichever side each is on (a diamond's outfield is the visitors', its infield the home team's). */
42
+ byRole?: boolean;
43
+ }
23
44
  /** The parts of a surface a drawing can be cropped to, by name: one box, or one at each end (`left` and `right` as the surface lies wide), in feet in the wide frame. */
24
45
  export type SurfaceRanges = Readonly<Record<string, Box | {
25
46
  left: Box;
26
47
  right: Box;
27
48
  }>>;
49
+ /** The places of a surface's own parts that a game's situation (`situation`) is drawn against, in the wide frame, in feet. */
50
+ export type SituationAnchors = {
51
+ kind: "hockey";
52
+ length: number;
53
+ width: number;
54
+ blueLine: number;
55
+ goalLine: number;
56
+ cornerRadius: number;
57
+ net: {
58
+ width: number;
59
+ depth: number;
60
+ };
61
+ }
62
+ /** The centre of each base, as the drawing has it (first, second, third), and half the diagonal of a base as drawn; where the backstop stands behind home plate. */
63
+ | {
64
+ kind: "baseball";
65
+ bases: readonly (readonly [number, number])[];
66
+ half: number;
67
+ backstop: number;
68
+ }
69
+ /** The field of play from goal line to goal line, with its end zones. */
70
+ /** `arrows` and `numerals`: what a tag must not cover, each as its centre and half its size along the field and across it (`[x, y, hx, hy]`): the arrows beside the yard numbers that point to a goal line, when the field draws them, and the numbers. */
71
+ | {
72
+ kind: "football";
73
+ length: number;
74
+ endZone: number;
75
+ width: number;
76
+ arrows: readonly (readonly [number, number, number, number])[];
77
+ numerals: readonly (readonly [number, number, number, number])[];
78
+ };
28
79
  /** A surface, ready for the teams and the score to be laid on it. */
29
80
  export interface Surface {
30
81
  sport: Sport;
@@ -84,11 +135,30 @@ export interface Surface {
84
135
  plateFits?: boolean;
85
136
  /** For a surface set inside another (a special game): the transform that carries the surface's own frame to the drawing's, which data in the surface's frame (`layers`) is put through. */
86
137
  frame?: string;
138
+ /** A surface drawn in parts, one of which is shown again larger beside it (a folded curling sheet): for each copy, the transform that carries the surface's frame to it and the id of the clip that cuts it. The data layers are drawn in each. */
139
+ panels?: {
140
+ transform: string;
141
+ clip: string;
142
+ }[];
143
+ /** Markup drawn over the data layers: the labels of a surface's panels. */
144
+ over?: string;
145
+ /** True for a surface whose frame has parts the annotations (axes, direction, landmarks) have no margin to stand in. */
146
+ folded?: boolean;
147
+ /** The radius of the centre circle in feet, where the surface has one: the emblem of a pageant lies in it. */
148
+ centreRadius?: number;
149
+ /** Where a pageant's pictures go, when the halves and the middle are not the place. */
150
+ pageant?: PageantPlan;
151
+ /** For a surface whose own frame is turned to an angle that is not a quarter (a close-up of an athletics start): the turn, in degrees, that a layer's texts and logos are given to read upright. */
152
+ upright?: number;
87
153
  /** How wide the surface's ordinary line is drawn, in the units of the `lines` markup, and how many feet one of those units is: what `lineWidth` scales. */
88
154
  lineRef?: {
89
155
  width: number;
90
156
  feet: number;
91
157
  };
158
+ /** For a surface with a crowd: the margin it was drawn with, which holds the stands. */
159
+ crowdMargin?: number;
160
+ /** Where the parts a situation is drawn against are: a rink's lines and nets, a diamond's bases, a gridiron's goal lines. */
161
+ anchors?: SituationAnchors;
92
162
  }
93
163
  /** A part of a surface to label: its anchor on the surface in feet and the margin its label stands in (the top or the bottom as the surface lies wide). */
94
164
  export interface Mark {
@@ -114,9 +184,15 @@ export interface BuildOptions {
114
184
  baselineText?: [string, string];
115
185
  centreLogo?: string;
116
186
  paintThreePoint?: boolean;
187
+ /** The realistic floor, when asked for (`court.hardwood`), its options read. */
188
+ hardwood?: Hardwood;
189
+ /** Words along the sidelines, `[top, bottom]`, on a hardwood floor. */
190
+ sidelineText?: [string, string];
117
191
  };
118
192
  /** True in a score drawing, so that panels are made room for. */
119
193
  score: boolean;
194
+ /** True when the drawing is a pageant: the teams' pictures lie over the halves, so the ends are not painted in the teams' colours. */
195
+ pageant?: boolean;
120
196
  /** The names of the features that have a colour of their own, so that their elements carry the class that colour is given to. */
121
197
  features?: ReadonlySet<string>;
122
198
  /** How the grass is mown, when the options say (a football field and a ballpark have their own default). */
@@ -0,0 +1,15 @@
1
+ import { type VenueLayout } from "../pool.ts";
2
+ import type { Language, PoolStyle } from "../types.ts";
3
+ import { type BuildOptions, type Surface } from "./surface.ts";
4
+ /** The name each pool of a hall is called on the plan, in a language. */
5
+ export declare function venueName(item: VenueLayout["items"][number], language: Language): string;
6
+ /**
7
+ * Several pools in one hall, to scale: each pool is drawn by its own sport's builder (the competition pool of 8 lanes, a diving well, a warm-up pool, a water polo pool or an artistic swimming area) and set at its place,
8
+ * with the deck round the hall drawn once and the paint of the water and the deck defined once for all of them. The pools stand at least 8 m apart, 10 m by default, as the rules keep a swimming pool and a diving well
9
+ * (Part Two 15.15.1). The water of the whole hall is the surface's `box`.
10
+ */
11
+ export declare function natatorium(o: BuildOptions, style: PoolStyle, look: {
12
+ look: "flat" | "water" | "tiled";
13
+ deck: "plain" | "tiled";
14
+ seed: number;
15
+ }, language: Language): Surface;