@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,128 @@
1
+ import { el, num } from "../format.js";
2
+ import { feet } from "../pool.js";
3
+ import { box, deckMarkup, leastWidth, paintDefs, poolIds, waterMarkup } from "../pool.kit.js";
4
+ import { DIVING, DIVING_LINES } from "../pool.figures.js";
5
+ import { textWidth } from "../text.js";
6
+ import { GUTTER } from "./pool.js";
7
+ import { edges, ft, quarterTurn, textOn } from "./surface.js";
8
+ /** The name a board goes by in the words of the landmarks. */
9
+ const markId = (b) => (b.kind === "springboard" ? `springboard-${b.height}m` : `platform-${String(b.height).replace(".", "-")}m`);
10
+ /**
11
+ * A diving well seen from above, from World Aquatics' rules (Part Four, Article 15 and Appendix 2): the pool wall the boards stand at on the left, the diving direction towards the right. Every springboard is 4.88 m
12
+ * long and 0.50 m wide with its front edge, the plummet, 2.22 m beyond the wall; each platform has the length and width of the table, its front edge 0.75 to 1.50 m beyond the wall (15.4.9); the boards stand the preferred
13
+ * distance (Appendix 2, B and C) from the side wall and from each other. The three lines across the well (15.6.3.1) are 21 m long, 2.5 m apart, the first under the plummet of the 3 m springboard.
14
+ */
15
+ export function diving(o, plan, ctx = {}) {
16
+ const L = plan.length;
17
+ const W = plan.width;
18
+ const ids = ctx.ids ?? poolIds(o.id);
19
+ const full = o.detail === "full";
20
+ const m = ctx.bare === true ? 0 : (o.margin ?? feet(2.5));
21
+ // The stands, and the names of the springboards on the deck behind them, stand in the room behind the wall.
22
+ const behind = plan.behind + 4;
23
+ let base = box(o, "gutter", "ky-boards", -GUTTER, -GUTTER, L + GUTTER, W + GUTTER);
24
+ base += waterMarkup(o, ids, plan.look, 0, 0, L, W);
25
+ // The three lines across the well, under the plummet of the 3 m springboard and 2.5 m apart.
26
+ const three = plan.boards.find((b) => b.kind === "springboard" && b.height === 3);
27
+ const first = three?.x ?? DIVING.A.springboard3[1];
28
+ const lw = leastWidth(o, DIVING.lines.width, 0.32);
29
+ let lines = "";
30
+ let floor = "";
31
+ const y0 = (W - DIVING.lines.length) / 2;
32
+ for (let k = 0; k < DIVING_LINES; k += 1)
33
+ floor += `M${num(feet(first + k * DIVING.lines.apart))} ${num(feet(y0))}V${num(feet(y0 + DIVING.lines.length))}`;
34
+ lines += el("path", { class: ft(o, "lane-lines", "ky-line"), d: floor, "stroke-width": lw, fill: "none" });
35
+ // The stands behind the wall: one block under the platforms, a pedestal under each springboard.
36
+ const platforms = plan.boards.filter((b) => b.kind === "platform");
37
+ let stands = "";
38
+ if (platforms.length > 0) {
39
+ const ya = Math.min(...platforms.map((b) => b.y - b.width / 2)) - 0.5;
40
+ const yb = Math.max(...platforms.map((b) => b.y + b.width / 2)) + 0.5;
41
+ const xa = -Math.max(...platforms.map((b) => b.length - b.x)) - 0.4;
42
+ stands += el("rect", { class: ft(o, "stands", "ky-pl-sd"), x: feet(xa), y: feet(ya), width: feet(-xa), height: feet(yb - ya), rx: feet(0.2) });
43
+ }
44
+ const springboards = plan.boards.filter((one) => one.kind === "springboard");
45
+ const springStand = springboards.length === 0 ? 0 : Math.min(...springboards.map((b) => b.x - b.length)) - 0.4;
46
+ if (springboards.length > 0) {
47
+ const ya = Math.min(...springboards.map((b) => b.y)) - 1.1;
48
+ const yb = Math.max(...springboards.map((b) => b.y)) + 1.1;
49
+ stands += el("rect", { class: ft(o, "stands", "ky-pl-sd"), x: feet(springStand), y: feet(ya), width: feet(-springStand), height: feet(yb - ya), rx: feet(0.2) });
50
+ }
51
+ base += stands;
52
+ // The boards: a platform is a rectangle with its front edge over the water; a springboard is a bar reaching out from its stand. Tall ones over the low.
53
+ let boards = "";
54
+ let labels = "";
55
+ const size = feet(0.95);
56
+ const ordered = [...plan.boards].sort((a, b) => (a.kind === b.kind ? a.height - b.height : a.kind === "platform" ? -1 : 1));
57
+ for (const b of ordered) {
58
+ const x0 = b.x - b.length;
59
+ if (b.kind === "platform") {
60
+ boards += el("rect", { class: ft(o, "platforms", "ky-pl-pf"), x: feet(x0), y: feet(b.y - b.width / 2), width: feet(b.length), height: feet(b.width), rx: feet(0.1) });
61
+ if (full)
62
+ boards += el("path", { class: "ky-line ky-pl-fe", d: `M${num(feet(b.x))} ${num(feet(b.y - b.width / 2))}V${num(feet(b.y + b.width / 2))}`, "stroke-width": feet(0.12), fill: "none" });
63
+ // Turned back upright on a tall drawing, the word lies across the platform's width: it is made to fit.
64
+ const text = `${String(b.height)} m`;
65
+ const fit = o.portrait ? Math.min(size, (feet(b.width) * 0.9) / textWidth(text)) : size;
66
+ labels += textOn(o.portrait, feet(b.x - Math.min(2.2, b.length * 0.4)), feet(b.y) + fit * 0.36, text, { size: fit, anchor: "middle", cls: "ky-pl-bt", weight: 800 });
67
+ }
68
+ else {
69
+ boards += el("rect", { class: ft(o, "springboards", "ky-pl-spb"), x: feet(x0), y: feet(b.y - b.width / 2), width: feet(b.length), height: feet(b.width), rx: feet(0.12) });
70
+ // The name stands on the deck behind the springboards' stand, level with the board; turned upright on a tall drawing it is made to fit between two boards.
71
+ const word = `${b.height} m`;
72
+ const fit = o.portrait ? Math.min(size * 0.9, (feet(DIVING.C.springboard1[0]) * 0.92) / textWidth(word)) : size * 0.9;
73
+ labels += textOn(o.portrait, feet(springStand - 0.4), feet(b.y) + fit * 0.36, word, { size: fit, anchor: o.portrait ? "middle" : "end", cls: "ky-pl-bu", weight: 800 });
74
+ }
75
+ // The plummet: the vertical line through the middle of the front edge.
76
+ if (full)
77
+ boards += el("circle", { class: "ky-pl-pm", cx: feet(b.x), cy: feet(b.y), r: feet(0.14) });
78
+ }
79
+ lines += boards + labels;
80
+ const marks = [];
81
+ if (o.landmarks === true) {
82
+ const seen = new Set();
83
+ for (const b of plan.boards) {
84
+ const id = markId(b);
85
+ if (seen.has(id))
86
+ continue;
87
+ seen.add(id);
88
+ marks.push({ id, x: feet(b.x), y: feet(b.y - (b.kind === "platform" ? b.width / 2 : b.width)), side: "top" });
89
+ }
90
+ marks.push({ id: "diving-line", x: feet(first + DIVING.lines.apart), y: feet(y0 + DIVING.lines.length), side: "bottom" });
91
+ }
92
+ const view0 = { x: -feet(behind) - m, y: -m - feet(GUTTER), w: feet(L + behind) + 2 * m, h: feet(W) + 2 * m + 2 * feet(GUTTER) };
93
+ if (ctx.bare !== true)
94
+ base = deckMarkup(o, ids, plan.deck, view0.x, view0.y, view0.w, view0.h) + base;
95
+ const Lf = feet(L);
96
+ const Wf = feet(W);
97
+ const cy = Wf / 2;
98
+ const turned = o.portrait;
99
+ const surface = {
100
+ sport: "diving",
101
+ view: view0,
102
+ base,
103
+ lines,
104
+ defs: ctx.bare === true ? "" : paintDefs(ids, plan.look, plan.deck, plan.seed),
105
+ left: { cx: -feet(behind) - m / 2, cy, w: Wf, h: m, rot: -90 },
106
+ right: { cx: Lf + m / 2, cy, w: Wf, h: m, rot: 90 },
107
+ plate: turned ? { cx: Lf * 0.62, cy, w: Wf * 0.9, h: Lf * 0.3 } : { cx: Lf * 0.62, cy, w: Lf * 0.34, h: Wf * 0.5 },
108
+ turned,
109
+ };
110
+ surface.box = { x: 0, y: 0, w: Lf, h: Wf };
111
+ surface.lineRef = { width: lw, feet: 1 };
112
+ // Where the springboards stand and where the platforms: split between the last springboard and the first platform.
113
+ const sb = plan.boards.filter((b) => b.kind === "springboard");
114
+ const pf = plan.boards.filter((b) => b.kind === "platform");
115
+ const split = sb.length > 0 && pf.length > 0 ? (Math.max(...sb.map((b) => b.y)) + Math.min(...pf.map((b) => b.y - b.width / 2))) / 2 : W / 2;
116
+ surface.ranges = {
117
+ springboards: edges(0, 0, Lf * 0.55, feet(split)),
118
+ platforms: edges(0, feet(split), Lf * 0.55, Wf),
119
+ };
120
+ if (o.landmarks === true)
121
+ surface.marks = marks;
122
+ if (turned) {
123
+ const t = quarterTurn(view0);
124
+ surface.view = t.view;
125
+ surface.turn = t.turn;
126
+ }
127
+ return surface;
128
+ }
@@ -1,8 +1,13 @@
1
- import type { FieldPlan } from "../fields.types.ts";
1
+ import type { Area, FieldPlan, Shape } from "../fields.types.ts";
2
2
  import type { GoalStyle } from "../types.ts";
3
3
  import { type BuildOptions, type Surface } from "./surface.ts";
4
+ /** The path of one shape that is drawn as a line, or of one filled bar. */
5
+ export declare function pathOf(shape: Shape): string;
6
+ export declare const areaD: (a: Area) => string;
4
7
  /** The painted lines of a plan: the shapes drawn with the line's paint, those alike in feature, width and dash put in one path. */
5
- export declare function strokes(plan: FieldPlan, o: Pick<BuildOptions, "exact" | "features">): string;
8
+ export declare function strokes(plan: Pick<FieldPlan, "shapes" | "lineWidth" | "drawnLine">, o: Pick<BuildOptions, "exact" | "features">): string;
9
+ /** The filled marks of a plan: the bars in one path to a feature, the dots one each. */
10
+ export declare function marks(plan: Pick<FieldPlan, "shapes">, o: Pick<BuildOptions, "exact" | "features">): string;
6
11
  /**
7
12
  * A field sport's surface lying wide, from the sport's plan: the ground, the mown turf, the painted areas, the lines, the goals, and, on a score, the
8
13
  * ends painted in the teams' colours and the place for the board. The markings are the plan's, which are the rulebook's (leagues/*.ts).
@@ -4,7 +4,7 @@ import { mowTurf, oddCells } from "../mow.js";
4
4
  import { ft, quarterTurn } from "./surface.js";
5
5
  const RAD = Math.PI / 180;
6
6
  /** The path of one shape that is drawn as a line, or of one filled bar. */
7
- function pathOf(shape) {
7
+ export function pathOf(shape) {
8
8
  switch (shape.kind) {
9
9
  case "line":
10
10
  return shape.y0 === shape.y1 ? `M${num(shape.x0)} ${num(shape.y0)}H${num(shape.x1)}` : shape.x0 === shape.x1 ? `M${num(shape.x0)} ${num(shape.y0)}V${num(shape.y1)}` : `M${num(shape.x0)} ${num(shape.y0)}L${num(shape.x1)} ${num(shape.y1)}`;
@@ -27,7 +27,7 @@ function pathOf(shape) {
27
27
  return "";
28
28
  }
29
29
  }
30
- const areaD = (a) => {
30
+ export const areaD = (a) => {
31
31
  if (a.d !== undefined)
32
32
  return a.d;
33
33
  if (a.frame !== undefined) {
@@ -61,19 +61,21 @@ export function strokes(plan, o) {
61
61
  return out;
62
62
  }
63
63
  /** The filled marks of a plan: the bars in one path to a feature, the dots one each. */
64
- function marks(plan, o) {
64
+ export function marks(plan, o) {
65
65
  const bars = new Map();
66
66
  let dots = "";
67
67
  const lift = o.exact ? 1 : 1.6;
68
68
  for (const shape of plan.shapes) {
69
69
  if (shape.kind === "bar")
70
- bars.set(shape.feature, [...(bars.get(shape.feature) ?? []), pathOf(shape)]);
70
+ bars.set(`${shape.feature}|${shape.cls ?? ""}`, [...(bars.get(`${shape.feature}|${shape.cls ?? ""}`) ?? []), pathOf(shape)]);
71
71
  else if (shape.kind === "dot")
72
- dots += el("circle", { class: ft(o, shape.feature, shape.paint === "post" ? "ky-post-end" : "ky-fill-line"), cx: shape.cx, cy: shape.cy, r: shape.r * lift });
72
+ dots += el("circle", { class: ft(o, shape.feature, shape.paint === "post" ? "ky-post-end" : shape.paint === "block" ? "ky-ska-block" : shape.paint === "cone" ? "ky-ska-cone" : "ky-fill-line"), cx: shape.cx, cy: shape.cy, r: shape.r * lift });
73
73
  }
74
74
  let out = "";
75
- for (const [feature, d] of bars)
76
- out += el("path", { class: ft(o, feature, "ky-fill-line"), d: d.join("") });
75
+ for (const [key, d] of bars) {
76
+ const [feature, cls] = key.split("|");
77
+ out += el("path", { class: ft(o, feature, cls === "" ? "ky-fill-line" : cls), d: d.join("") });
78
+ }
77
79
  return out + dots;
78
80
  }
79
81
  /** One goal as the options ask for it: the net behind the goal line as a box, a line across the mouth, a solid strip, or nothing. */
@@ -118,8 +120,8 @@ export function fieldSurface(o, plan, style = {}) {
118
120
  }
119
121
  }
120
122
  for (const area of plan.areas)
121
- base += el("path", { class: ft(o, area.feature, area.cls), d: areaD(area), "fill-rule": area.frame === undefined ? undefined : "evenodd" });
122
- const ends = o.score && plan.ends !== undefined ? plan.ends : undefined;
123
+ base += el("path", { class: ft(o, area.feature, area.cls), d: areaD(area), "fill-rule": area.frame === undefined && area.evenodd !== true ? undefined : "evenodd" });
124
+ const ends = o.score && o.pageant !== true && plan.ends !== undefined ? plan.ends : undefined;
123
125
  if (ends !== undefined) {
124
126
  for (const side of ["left", "right"]) {
125
127
  const b = ends[side];
@@ -152,6 +154,10 @@ export function fieldSurface(o, plan, style = {}) {
152
154
  surface.box = { x: 0, y: 0, w: L, h: W };
153
155
  surface.lineRef = { width: thick, feet: 1 };
154
156
  surface.ranges = plan.ranges;
157
+ // A pageant's pictures keep clear of a cricket pitch, which runs through the middle.
158
+ const pitch = plan.sport === "cricket" ? plan.ranges?.pitch : undefined;
159
+ if (pitch !== undefined && !("left" in pitch))
160
+ surface.pageant = { clear: pitch.w / 2 };
155
161
  if (o.landmarks === true)
156
162
  surface.marks = full ? [...plan.marks] : plan.marks.filter((mark) => !mark.id.startsWith("~"));
157
163
  if (o.portrait) {
@@ -93,6 +93,8 @@ export function football(spec, o, marks = { hashMarks: true, postShape: "slingsh
93
93
  if (numbered && spec.id === "cfl" && (yardsLong / 2) % 10 !== 0)
94
94
  positions.push({ x: e + (yardsLong / 2) * 3, label: spec.centre, pos: yardsLong / 2 });
95
95
  let numbers = "";
96
+ const arrows = [];
97
+ const numerals = [];
96
98
  for (const side of ["near", "far"]) {
97
99
  const y = side === "near" ? W - centreY : centreY;
98
100
  for (const p of positions) {
@@ -100,6 +102,9 @@ export function football(spec, o, marks = { hashMarks: true, postShape: "slingsh
100
102
  const centre = p.label === spec.centre || p.pos === yardsLong / 2;
101
103
  const digits = [...p.label];
102
104
  const flip = side === "far";
105
+ // The numeral's box in the wide frame: the digits stand along the field, or across it when the field is turned.
106
+ const spread = digits.length === 1 ? 3 : 5.9;
107
+ numerals.push([p.x, y, turned ? DIGIT_HEIGHT / 2 : spread, turned ? spread : DIGIT_HEIGHT / 2]);
103
108
  if (digits.length === 1)
104
109
  numbers += textOn(turned, p.x, y + DIGIT_HEIGHT / 2, p.label, { size, anchor: "middle", cls: numCls, flip, weight: 700 });
105
110
  else {
@@ -118,6 +123,7 @@ export function football(spec, o, marks = { hashMarks: true, postShape: "slingsh
118
123
  if (full && !centre && Number(p.label) < 50) {
119
124
  const ax = nearGoalLeft ? p.x - 8.4 : p.x + 8.4;
120
125
  const dir = nearGoalLeft ? -1 : 1;
126
+ arrows.push([ax, y, 1.5, 1.6]);
121
127
  numbers += el("path", { class: ft(o, "numbers", "ky-fill-line"), d: `M${num(ax + dir * 1.5)} ${num(y)}L${num(ax - dir * 1.5)} ${num(y - 1.6)}V${num(y + 1.6)}Z` });
122
128
  }
123
129
  }
@@ -169,6 +175,7 @@ export function football(spec, o, marks = { hashMarks: true, postShape: "slingsh
169
175
  };
170
176
  surface.lineRef = { width: lw, feet: 1 };
171
177
  surface.box = { x: 0, y: 0, w: L, h: W };
178
+ surface.anchors = { kind: "football", length: spec.length, endZone: e, width: W, arrows, numerals };
172
179
  // The red zone is the 20 yards before a goal line and the end zone behind it; the half is from an end line to midfield.
173
180
  const twenty = 60;
174
181
  const half = atEnds((end) => (end === "left" ? edges(0, 0, L / 2, W) : edges(L / 2, 0, L, W)));
@@ -15,8 +15,8 @@ export function boardsY(x, radius = NHL.cornerRadius) {
15
15
  * season (today's by default) marked it; with a `rink` (leagues.ts), that league's, in feet.
16
16
  */
17
17
  export function hockey(o, era, rink = NHL_RINK) {
18
- // The NHL's rink has a history of its own (eras.ts); a league's, if it has one, is already in its `rink`.
19
- const nhl = rink === NHL_RINK ? (era ?? rulesFor("nhl").rules) : undefined;
18
+ // The NHL's rink has a history of its own (eras.ts), and so has an arena's rink of another size (an `era` given with it); a league's, if it has one, is already in its `rink`.
19
+ const nhl = rink === NHL_RINK || era !== undefined ? (era ?? rulesFor("nhl").rules) : undefined;
20
20
  const { length: L, width: W, cornerRadius: r } = rink;
21
21
  const g = nhl?.goalLine ?? rink.goalLine;
22
22
  const blueLine = nhl?.blueLine ?? rink.blueLine;
@@ -128,6 +128,8 @@ export function hockey(o, era, rink = NHL_RINK) {
128
128
  };
129
129
  surface.lineRef = { width: thin, feet: 1 };
130
130
  surface.box = { x: 0, y: 0, w: L, h: W };
131
+ surface.centreRadius = rink.circleRadius;
132
+ surface.anchors = { kind: "hockey", length: L, width: W, blueLine, goalLine: g, cornerRadius: r, net: net_ };
131
133
  // The zones of the ice: from the end boards to a blue line, and between the blue lines.
132
134
  const zone = atEnds((end) => (end === "left" ? edges(0, 0, blueLine, W) : edges(L - blueLine, 0, L, W)));
133
135
  surface.ranges = {
@@ -2,6 +2,7 @@ import { type CourtPlan } from "../court.ts";
2
2
  import { type BuildOptions, type Surface } from "./surface.ts";
3
3
  /**
4
4
  * A net sport's court lying wide: the ground, the free zone, the court, its painted areas, its lines, the net with its posts (and, in volleyball, its antennae)
5
- * and, on a score, the place for the board. Every length that is a rulebook's figure comes from the league (leagues/courts.ts) through `courtGeometry`.
5
+ * and, on a score, the place for the board. Every length that is a rulebook's figure comes from the league (leagues/courts.ts) through `courtGeometry`. The courts of
6
+ * the sports added after the first five may also have walls, rings (circles and arcs), a table or a set, and no net.
6
7
  */
7
8
  export declare function netCourt(o: BuildOptions, plan: CourtPlan): Surface;
@@ -1,4 +1,4 @@
1
- import { courtGeometry, courtSize, sideReach } from "../court.js";
1
+ import { bandReach, courtGeometry, courtSize, sideReach } from "../court.js";
2
2
  import { el, num } from "../format.js";
3
3
  import { atEnds, edges, ft, quarterTurn } from "./surface.js";
4
4
  /** A rectangle as a closed sub-path. */
@@ -8,9 +8,35 @@ function mark(f, cls) {
8
8
  const half = f.size / 2;
9
9
  return f.shape === "round" ? el("circle", { class: cls, cx: f.x, cy: f.y, r: half }) : el("rect", { class: cls, x: f.x - half, y: f.y - half, width: f.size, height: f.size });
10
10
  }
11
+ /**
12
+ * A ring or an arc as a stroked line: a circle element for a whole circle, else a path from `a0` to `a1` in pieces of at most a quarter turn (an arc of exactly half a circle,
13
+ * drawn in one piece with rounded numbers, can lose its centre). The line's outer edge is at the radius, so its middle is half a width inside it.
14
+ */
15
+ function ringMarkup(o, ring) {
16
+ const { cx, cy, w } = ring;
17
+ const r = Math.max(0.001, ring.r - w / 2);
18
+ const cls = ft(o, "lines", "ky-line");
19
+ if (ring.a0 === undefined || ring.a1 === undefined)
20
+ return el("circle", { class: cls, cx, cy, r, "stroke-width": w });
21
+ const turns = Math.max(1, Math.ceil((ring.a1 - ring.a0) / (Math.PI / 2) - 1e-9));
22
+ const at = (a) => `${num(cx + r * Math.cos(a))} ${num(cy + r * Math.sin(a))}`;
23
+ let d = `M${at(ring.a0)}`;
24
+ for (let i = 1; i <= turns; i += 1)
25
+ d += `A${num(r)} ${num(r)} 0 0 1 ${at(ring.a0 + ((ring.a1 - ring.a0) * i) / turns)}`;
26
+ return el("path", { class: cls, d, "stroke-width": w });
27
+ }
28
+ /** What each kind of band is drawn with: the class and the feature it belongs to. */
29
+ const BAND = { wall: ["walls", "ky-wall"], mesh: ["fence", "ky-mesh"], face: ["wall-face", "ky-face"], tin: ["tin", "ky-tin"] };
30
+ /** A solid on the court: a table is a rectangle, a roundnet set a rim round a net. */
31
+ function solid(o, s) {
32
+ if (s.kind === "table")
33
+ return el("path", { class: ft(o, "table", "ky-table"), d: box(s.rect) });
34
+ return el("circle", { class: ft(o, "net", "ky-nt-post"), cx: s.cx, cy: s.cy, r: s.r }) + el("circle", { class: ft(o, "net", "ky-set-net"), cx: s.cx, cy: s.cy, r: s.r * 0.84 });
35
+ }
11
36
  /**
12
37
  * A net sport's court lying wide: the ground, the free zone, the court, its painted areas, its lines, the net with its posts (and, in volleyball, its antennae)
13
- * and, on a score, the place for the board. Every length that is a rulebook's figure comes from the league (leagues/courts.ts) through `courtGeometry`.
38
+ * and, on a score, the place for the board. Every length that is a rulebook's figure comes from the league (leagues/courts.ts) through `courtGeometry`. The courts of
39
+ * the sports added after the first five may also have walls, rings (circles and arcs), a table or a set, and no net.
14
40
  */
15
41
  export function netCourt(o, plan) {
16
42
  const geometry = courtGeometry(plan);
@@ -23,7 +49,10 @@ export function netCourt(o, plan) {
23
49
  const side = Math.max(zone?.side ?? 0, sideReach(geometry));
24
50
  const end = zone?.end ?? 0;
25
51
  const m = o.margin ?? L * 0.035;
26
- const view0 = { x: -end - m, y: -side - m, w: L + 2 * (end + m), h: W + 2 * (side + m) };
52
+ // Walls and the like stand beyond the court's edges, and may make the ground wider on one side than on the other.
53
+ const beyond = bandReach(geometry);
54
+ const [left, right, top, bottom] = [Math.max(end, beyond.left), Math.max(end, beyond.right), Math.max(side, beyond.top), Math.max(side, beyond.bottom)];
55
+ const view0 = { x: -left - m, y: -top - m, w: L + left + right + 2 * m, h: W + top + bottom + 2 * m };
27
56
  // Lines are drawn a little thicker than painted, as the other surfaces' are, unless asked for the true widths: the thin dimension of a line is raised to a least.
28
57
  const least = o.exact ? 0 : L * 0.0026;
29
58
  const thick = (r) => {
@@ -39,12 +68,31 @@ export function netCourt(o, plan) {
39
68
  base += el("rect", { class: ft(o, "court", "ky-surface"), x: 0, y: 0, width: L, height: W });
40
69
  if (geometry.areas.length > 0)
41
70
  base += el("path", { class: ft(o, "areas", "ky-area"), d: geometry.areas.map(box).join("") });
71
+ for (const band of geometry.bands) {
72
+ const [feature, cls] = BAND[band.kind];
73
+ base += el("path", { class: ft(o, feature, cls), d: box(band.rect) });
74
+ }
42
75
  const lineRects = [...geometry.lines, ...(full ? geometry.small : [])].map(thick);
43
76
  let lines = el("path", { class: ft(o, "lines", "ky-fill-line"), d: lineRects.map(box).join("") });
44
- // The net: a line across the court at its middle, from post to post; its posts and, in volleyball, the antennae at the side lines.
45
- const nw = Math.max(L * 0.004, 0.02);
77
+ // Circles and arcs are lines too, drawn at least as thick as the others.
78
+ for (const ring of geometry.rings)
79
+ lines += ringMarkup(o, { ...ring, w: Math.max(ring.w, least) });
80
+ if (geometry.wallLines.length > 0)
81
+ lines += el("path", { class: ft(o, "wall-lines", "ky-wall-line"), d: geometry.wallLines.map(thick).map(box).join("") });
82
+ for (const s of geometry.solids)
83
+ lines += solid(o, s);
84
+ // The net: a line across the court at its middle, from post to post; its posts and, in volleyball, the antennae at the side lines. A court without a net has none.
46
85
  const net = geometry.net;
47
- lines += el("rect", { class: ft(o, "net", "ky-nt-line"), x: net.x - nw / 2, y: net.y0, width: nw, height: net.y1 - net.y0 });
86
+ // Over a centre line (volleyball's, sepak takraw's) the net is drawn a little wider than the line, so that the line shows between its edges.
87
+ const under = geometry.underNet;
88
+ const nw = under === null ? Math.max(L * 0.004, 0.02) : Math.max(under, least) * 2.4;
89
+ if (net !== null)
90
+ lines += el("rect", { class: ft(o, "net", "ky-nt-line"), x: net.x - nw / 2, y: net.y0, width: nw, height: net.y1 - net.y0 });
91
+ // The centre line runs under the net (volleyball Rule 1.3.3): the net is drawn over it, so the line is drawn again over the net, in a narrower stroke, to show through.
92
+ if (net !== null && under !== null) {
93
+ const core = Math.max(under, least);
94
+ lines += el("rect", { class: ft(o, "lines", "ky-fill-line"), x: net.x - core / 2, y: 0, width: core, height: W });
95
+ }
48
96
  for (const post of geometry.posts)
49
97
  lines += mark(post, ft(o, "posts", "ky-nt-post"));
50
98
  if (full) {
@@ -54,22 +102,30 @@ export function netCourt(o, plan) {
54
102
  lines += mark(antenna, ft(o, "antennae", "ky-red-fill"));
55
103
  }
56
104
  // The teams' names are not painted on these courts: the board names the teams. The board lies over the net, between the lines it needs.
57
- const turned = o.portrait;
58
- const spot = turned ? { cx, cy, w: W * 0.88, h: L * 0.5 } : { cx, cy, w: L * 0.58, h: W * 0.66 };
105
+ // A roundnet court is square, with no sides: it lies the same wherever it is turned.
106
+ const turned = o.portrait && plan.sport !== "roundnet";
107
+ // The board lies over the net, between the lines it needs; on a roundnet court that is the set, so it lies on the ground below the circles.
108
+ const room = plan.sport === "roundnet" ? W / 2 - plan.figures.servingRadius : 0;
109
+ const clear = plan.sport === "roundnet" ? { cx, cy: cy + plan.figures.servingRadius + room / 2, w: L * 0.9, h: room * 0.9 } : undefined;
110
+ // Netball's board has no net to lie over: it lies in the centre third, and the teams' names are painted in the goal thirds.
111
+ const centred = plan.sport === "netball" ? (turned ? { cx, cy, w: W * 0.88, h: L * 0.36 } : { cx, cy, w: L * 0.4, h: W * 0.66 }) : undefined;
112
+ const spot = clear ?? centred ?? (turned ? { cx, cy, w: W * 0.88, h: L * 0.5 } : { cx, cy, w: L * 0.58, h: W * 0.66 });
113
+ const places = plan.sport === "netball" ? { left: { cx: L * 0.15, cy, w: L * 0.2, h: W * 0.4, rot: 0 }, right: { cx: L * 0.85, cy, w: L * 0.2, h: W * 0.4, rot: 0 } } : { left: { cx: L * 0.2, cy, w: L * 0.2, h: W * 0.4, rot: 0 }, right: { cx: L * 0.8, cy, w: L * 0.2, h: W * 0.4, rot: 0 } };
59
114
  const surface = {
60
115
  sport: plan.sport,
61
116
  view: view0,
62
117
  base,
63
118
  lines,
64
119
  defs: "",
65
- left: { cx: L * 0.2, cy, w: L * 0.2, h: W * 0.4, rot: 0 },
66
- right: { cx: L * 0.8, cy, w: L * 0.2, h: W * 0.4, rot: 0 },
120
+ left: places.left,
121
+ right: places.right,
67
122
  plate: spot,
68
123
  turned,
69
124
  };
70
125
  surface.box = { x: 0, y: 0, w: L, h: W };
71
126
  // One side of the net.
72
- surface.ranges = { "half-court": atEnds((end) => (end === "left" ? edges(0, 0, net.x, W) : edges(net.x, 0, L, W))) };
127
+ const middle = net === null ? cx : net.x;
128
+ surface.ranges = { "half-court": atEnds((end) => (end === "left" ? edges(0, 0, middle, W) : edges(middle, 0, L, W))) };
73
129
  surface.lineRef = { width: plan.sport === "tabletennis" ? plan.figures.edgeLine : plan.figures.lineWidth, feet: 1 };
74
130
  if (o.landmarks === true)
75
131
  surface.marks = netMarks(plan, L, W);
@@ -116,11 +172,12 @@ function netMarks(plan, L, W) {
116
172
  case "badminton": {
117
173
  const f = plan.figures;
118
174
  return [
119
- { id: "end-line", x: f.lineWidth / 2, y: W * 0.8, side: "bottom" },
175
+ // The back boundary line is the singles long service line too (Diagram A); the line inside it is the doubles long service line.
176
+ { id: "back-boundary-line", x: f.lineWidth / 2, y: W * 0.8, side: "bottom" },
120
177
  { id: "sideline", x: L * 0.12, y: 0, side: "top" },
121
178
  { id: "singles-sideline", x: L * 0.12, y: W - f.singlesInset, side: "bottom" },
122
- { id: "long-service-line", x: f.doublesLongServiceInset + f.lineWidth / 2, y: W * 0.2, side: "top" },
123
- { id: "short-service-line", x: cx - f.shortServiceLine - f.lineWidth / 2, y: W * 0.3, side: "top" },
179
+ { id: "doubles-long-service-line", x: f.doublesLongServiceInset + f.lineWidth / 2, y: W * 0.2, side: "top" },
180
+ { id: "short-service-line", x: cx - f.shortServiceLine - f.lineWidth / 2, y: W * 0.3, side: "bottom" },
124
181
  { id: "centre-line", x: L * 0.2, y: cy, side: "bottom" },
125
182
  net,
126
183
  ];
@@ -136,7 +193,7 @@ function netMarks(plan, L, W) {
136
193
  net,
137
194
  ];
138
195
  }
139
- default: {
196
+ case "tabletennis": {
140
197
  const f = plan.figures;
141
198
  return [
142
199
  { id: "end-line", x: f.edgeLine / 2, y: W * 0.8, side: "bottom" },
@@ -147,5 +204,93 @@ function netMarks(plan, L, W) {
147
204
  net,
148
205
  ];
149
206
  }
207
+ case "padel": {
208
+ const f = plan.figures;
209
+ return [
210
+ { id: "back-wall", x: 0, y: W * 0.8, side: "bottom" },
211
+ { id: "side-wall", x: f.enclosure.sideWall / 2, y: 0, side: "top" },
212
+ { id: "metal-fence", x: L * 0.3, y: W, side: "bottom" },
213
+ { id: "service-line", x: cx - f.serviceLine + f.lineWidth / 2, y: W * 0.25, side: "top" },
214
+ { id: "centre-service-line", x: cx - f.serviceLine / 2, y: cy, side: "bottom" },
215
+ net,
216
+ ];
217
+ }
218
+ case "squash": {
219
+ const f = plan.figures;
220
+ const near = L - f.shortLine;
221
+ return [
222
+ { id: "tin", x: -f.wall.tin / 2, y: W * 0.15, side: "top" },
223
+ { id: "wall-service-line", x: -f.wall.serviceLine - f.lineWidth / 2, y: W * 0.45, side: "bottom" },
224
+ { id: "front-wall-line", x: -f.wall.frontWallLine - f.lineWidth / 2, y: W * 0.15, side: "top" },
225
+ { id: "short-line", x: near - f.lineWidth / 2, y: W * 0.3, side: "top" },
226
+ { id: "squash-half-court-line", x: L - f.shortLine / 2, y: cy, side: "bottom" },
227
+ { id: "service-box", x: near + f.serviceBox / 2, y: f.serviceBox / 2, side: "top" },
228
+ { id: "back-wall", x: L, y: W * 0.7, side: "bottom" },
229
+ ];
230
+ }
231
+ case "racquetball": {
232
+ const f = plan.figures;
233
+ const back = L - f.shortLine;
234
+ const front = back - f.serviceLine;
235
+ return [
236
+ { id: "front-wall", x: 0, y: W * 0.3, side: "top" },
237
+ { id: "service-line", x: front + f.lineWidth / 2, y: W * 0.7, side: "bottom" },
238
+ { id: "service-zone", x: (front + back) / 2, y: cy, side: "top" },
239
+ { id: "service-box", x: (front + back) / 2, y: W - f.serviceBox / 2, side: "bottom" },
240
+ { id: "drive-serve-line", x: (front + back) / 2, y: f.driveServe - f.lineWidth / 2, side: "top" },
241
+ { id: "short-line", x: back - f.lineWidth / 2, y: W * 0.15, side: "top" },
242
+ { id: "safety-zone", x: back + f.receiving.behind / 2, y: cy, side: "bottom" },
243
+ { id: "receiving-line", x: back + f.receiving.behind - f.lineWidth / 2, y: W * 0.8, side: "bottom" },
244
+ { id: "back-wall", x: L, y: cy, side: "bottom" },
245
+ ];
246
+ }
247
+ case "sepaktakraw": {
248
+ const f = plan.figures;
249
+ const k = Math.SQRT1_2 * f.quarterCircle;
250
+ return [
251
+ { id: "back-line", x: f.lineWidth / 2, y: W * 0.8, side: "bottom" },
252
+ { id: "sideline", x: L * 0.12, y: 0, side: "top" },
253
+ { id: "centre-line", x: cx + f.centreLine, y: W * 0.8, side: "bottom" },
254
+ { id: "quarter-circle", x: cx - k, y: k, side: "top" },
255
+ { id: "service-circle", x: f.serviceCircle.fromBack, y: cy - f.serviceCircle.radius, side: "top" },
256
+ net,
257
+ ];
258
+ }
259
+ case "teqball": {
260
+ const f = plan.figures;
261
+ return [
262
+ { id: "court-surround", x: 0, y: W * 0.8, side: "bottom" },
263
+ { id: "halfway-line", x: cx, y: W * 0.15, side: "top" },
264
+ { id: "service-line", x: cx - f.serviceLine, y: W * 0.7, side: "bottom" },
265
+ { id: "service-line", x: cx + f.serviceLine, y: W * 0.3, side: "top" },
266
+ { id: "table", x: cx + f.table.length * 0.3, y: cy + f.table.width * 0.3, side: "bottom" },
267
+ { id: "court-net", x: cx, y: cy - f.net.width / 2, side: "top" },
268
+ ];
269
+ }
270
+ case "roundnet": {
271
+ const f = plan.figures;
272
+ const k = Math.SQRT1_2;
273
+ return [
274
+ { id: "roundnet-set", x: cx - f.set.diameter * 0.25, y: cy + f.set.diameter * 0.1, side: "bottom" },
275
+ { id: "no-hit-zone", x: cx + f.noHitRadius * k, y: cy - f.noHitRadius * k, side: "top" },
276
+ { id: "service-circle", x: cx - f.servingRadius * k, y: cy - f.servingRadius * k, side: "top" },
277
+ { id: "service-position", x: cx + f.servingRadius, y: cy, side: "bottom" },
278
+ { id: "service-position", x: cx - f.servingRadius, y: cy, side: "bottom" },
279
+ { id: "service-position", x: cx, y: cy - f.servingRadius, side: "top" },
280
+ ];
281
+ }
282
+ default: {
283
+ const f = plan.figures;
284
+ const k = Math.SQRT1_2 * f.goalCircle;
285
+ return [
286
+ { id: "goal-line", x: f.lineWidth / 2, y: W * 0.8, side: "bottom" },
287
+ { id: "sideline", x: L * 0.12, y: 0, side: "top" },
288
+ { id: "transverse-line", x: L / 3, y: 0, side: "top" },
289
+ { id: "centre-circle", x: cx + f.centreCircle / 2, y: cy, side: "bottom" },
290
+ { id: "goal-circle", x: k, y: cy - k, side: "top" },
291
+ { id: "goalpost", x: f.post.poleDiameter / 2, y: cy, side: "bottom" },
292
+ { id: "goal-ring", x: L - f.post.poleDiameter - f.post.ring.bar - f.post.ring.inside / 2, y: cy, side: "top" },
293
+ ];
294
+ }
150
295
  }
151
296
  }
@@ -0,0 +1,12 @@
1
+ import type { PoloPlan } from "../pool.ts";
2
+ import { type PoolCtx } from "../pool.kit.ts";
3
+ import { type BuildOptions, type Surface } from "./surface.ts";
4
+ /** 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. */
5
+ export declare const POLO_BEHIND: 0.3;
6
+ /**
7
+ * 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
8
+ * 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
9
+ * (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
10
+ * 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).
11
+ */
12
+ export declare function waterPolo(o: BuildOptions, plan: PoloPlan, ctx?: PoolCtx): Surface;