@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
@@ -1,6 +1,8 @@
1
1
  import { el, num } from "../format.js";
2
2
  import { rulesFor } from "../eras.js";
3
3
  import { NBA_COURT } from "../leagues/basketball.js";
4
+ import { bench, hoops } from "./basketball.gear.js";
5
+ import { hardwoodFloor, hardwoodWords } from "./basketball.floor.js";
4
6
  import { atEnds, edges, ft, quarterTurn, textOn } from "./surface.js";
5
7
  /**
6
8
  * A basketball court: the floor, the keys, the arcs, the circles, the baskets and the small marks. By default the NBA's, as the
@@ -8,14 +10,16 @@ import { atEnds, edges, ft, quarterTurn, textOn } from "./surface.js";
8
10
  */
9
11
  export function basketball(o, era, court = NBA_COURT) {
10
12
  // The NBA's court has a history of its own (eras.ts); a league's, if it has one, is already in its `court`.
11
- const nba = court === NBA_COURT ? (era ?? rulesFor("nba").rules) : undefined;
13
+ const nba = court === NBA_COURT || era !== undefined ? (era ?? rulesFor("nba").rules) : undefined;
12
14
  const { length: L, width: W } = court;
13
15
  const m = o.margin ?? 7;
14
16
  const view0 = { x: -m, y: -m, w: L + 2 * m, h: W + 2 * m };
15
17
  const cy = W / 2;
16
18
  const full = o.detail === "full";
17
19
  const bw = 2.6;
18
- const thin = o.exact ? 1 / 6 : 0.4;
20
+ // A realistic floor (`court.hardwood`) paints its lines nearer to a real court's two inches, unless it is small and they would vanish.
21
+ const hw = o.court?.hardwood;
22
+ const thin = o.exact ? 1 / 6 : hw?.fine === true ? 0.3 : 0.4;
19
23
  const turned = o.portrait;
20
24
  const laneWidth = nba?.keyWidth ?? court.laneWidth;
21
25
  const half = laneWidth / 2;
@@ -29,34 +33,42 @@ export function basketball(o, era, court = NBA_COURT) {
29
33
  const J = three === null ? 0 : bx + Math.sqrt(radius * radius - (W / 2 - inset) * (W / 2 - inset));
30
34
  const threeLeft = () => `M0 ${num(inset)}H${num(J)}A${num(radius)} ${num(radius)} 0 0 1 ${num(J)} ${num(W - inset)}H0`;
31
35
  const threeRight = () => `M${num(L)} ${num(inset)}H${num(L - J)}A${num(radius)} ${num(radius)} 0 0 0 ${num(L - J)} ${num(W - inset)}H${num(L)}`;
36
+ // A realistic floor lays boards under the paint and the seams and the sheen over it; the clean floor is as it always was.
37
+ const floor = hw === undefined ? undefined : hardwoodFloor(hw, { L, W, bw, margin: m, id: o.id }, o);
32
38
  let base = el("rect", { class: ft(o, "apron", "ky-apron"), x: view0.x, y: view0.y, width: view0.w, height: view0.h });
33
- base += el("path", { class: ft(o, "border", "ky-boards"), "fill-rule": "evenodd", d: `M${-bw} ${-bw}H${num(L + bw)}V${num(W + bw)}H${-bw}ZM0 0V${num(W)}H${num(L)}V0Z` });
34
- base += el("rect", { class: ft(o, "floor", "ky-surface"), x: 0, y: 0, width: L, height: W });
35
- if (o.stripes) {
36
- // The planks run along the court: a second tone on every other board.
37
- const planks = [];
38
- for (let y = 1; y < W; y += 2)
39
- planks.push(`M0 ${y}H${num(L)}v1H0z`);
40
- base += el("path", { class: ft(o, "planks", "ky-stripe"), d: planks.join("") });
39
+ if (floor !== undefined)
40
+ base += floor.under;
41
+ else {
42
+ base += el("path", { class: ft(o, "border", "ky-boards"), "fill-rule": "evenodd", d: `M${-bw} ${-bw}H${num(L + bw)}V${num(W + bw)}H${-bw}ZM0 0V${num(W)}H${num(L)}V0Z` });
43
+ base += el("rect", { class: ft(o, "floor", "ky-surface"), x: 0, y: 0, width: L, height: W });
44
+ if (o.stripes) {
45
+ // The planks run along the court: a second tone on every other board.
46
+ const planks = [];
47
+ for (let y = 1; y < W; y += 2)
48
+ planks.push(`M0 ${y}H${num(L)}v1H0z`);
49
+ base += el("path", { class: ft(o, "planks", "ky-stripe"), d: planks.join("") });
50
+ }
41
51
  }
42
52
  // The keys, painted, and the three-point areas when asked.
43
53
  const keyW = court.freeThrowLine;
44
54
  const wide = (court.laneEndWidth ?? laneWidth) / 2;
45
55
  if (o.court?.paintThreePoint && three !== null) {
46
- base += el("path", { class: ft(o, "three-point-area", "ky-key ky-key-left"), d: `${threeLeft()}Z` });
47
- base += el("path", { class: ft(o, "three-point-area", "ky-key ky-key-right"), d: `${threeRight()}Z` });
56
+ base += el("path", { class: ft(o, "three-point-area", "ky-key ky-key-left"), "fill-opacity": hw?.stain, d: `${threeLeft()}Z` });
57
+ base += el("path", { class: ft(o, "three-point-area", "ky-key ky-key-right"), "fill-opacity": hw?.stain, d: `${threeRight()}Z` });
48
58
  }
49
59
  // A lane that is a rectangle, or a trapezoid wider at the baseline.
50
60
  const lane = (x, away) => `M${num(x)} ${num(cy - wide)}L${num(x + away * keyW)} ${num(cy - half)}V${num(cy + half)}L${num(x)} ${num(cy + wide)}`;
51
61
  if (court.laneEndWidth === undefined) {
52
- base += el("rect", { class: ft(o, "key", "ky-key ky-key-left"), x: 0, y: cy - half, width: keyW, height: laneWidth });
53
- base += el("rect", { class: ft(o, "key", "ky-key ky-key-right"), x: L - keyW, y: cy - half, width: keyW, height: laneWidth });
62
+ base += el("rect", { class: ft(o, "key", "ky-key ky-key-left"), "fill-opacity": hw?.stain, x: 0, y: cy - half, width: keyW, height: laneWidth });
63
+ base += el("rect", { class: ft(o, "key", "ky-key ky-key-right"), "fill-opacity": hw?.stain, x: L - keyW, y: cy - half, width: keyW, height: laneWidth });
54
64
  }
55
65
  else {
56
- base += el("path", { class: ft(o, "key", "ky-key ky-key-left"), d: `${lane(0, 1)}Z` });
57
- base += el("path", { class: ft(o, "key", "ky-key ky-key-right"), d: `${lane(L, -1)}Z` });
66
+ base += el("path", { class: ft(o, "key", "ky-key ky-key-left"), "fill-opacity": hw?.stain, d: `${lane(0, 1)}Z` });
67
+ base += el("path", { class: ft(o, "key", "ky-key ky-key-right"), "fill-opacity": hw?.stain, d: `${lane(L, -1)}Z` });
58
68
  }
59
- base += el("circle", { class: ft(o, "centre-paint", "ky-centre"), cx: L / 2, cy, r: court.centreRadius });
69
+ base += el("circle", { class: ft(o, "centre-paint", "ky-centre"), "fill-opacity": hw?.stain, cx: L / 2, cy, r: court.centreRadius });
70
+ if (floor !== undefined)
71
+ base += floor.over;
60
72
  let lines = el("rect", { class: ft(o, "boundary", "ky-line"), x: 0, y: 0, width: L, height: W, "stroke-width": thin * 1.25, fill: "none" });
61
73
  lines += el("path", { class: ft(o, "halfway-line", "ky-line"), d: `M${num(L / 2)} 0V${num(W)}`, "stroke-width": thin, fill: "none" });
62
74
  lines += el("circle", { class: ft(o, "centre-circle", "ky-line"), cx: L / 2, cy, r: court.centreRadius, "stroke-width": thin, fill: "none" });
@@ -103,7 +115,11 @@ export function basketball(o, era, court = NBA_COURT) {
103
115
  return el("path", { class: ft(o, "backboards", "ky-board"), d: `${boardLine}${arm}`, "stroke-width": 0.5, fill: "none" }) + el("circle", { class: ft(o, "rims", "ky-rim"), cx: rimX, cy, r: court.rim, "stroke-width": 0.3, fill: "none" });
104
116
  }
105
117
  };
106
- if (o.goalMarkup === undefined && (o.goals === undefined || o.goals === "box")) {
118
+ if (hw?.hoops === true && o.goalMarkup === undefined && (o.goals === undefined || o.goals === "box")) {
119
+ // The baskets of a hardwood floor: the stanchion, the board and the rim seen from above, with their shadows.
120
+ lines += hoops(o, hw, court, { L, W, bw });
121
+ }
122
+ else if (o.goalMarkup === undefined && (o.goals === undefined || o.goals === "box")) {
107
123
  lines += el("path", { class: ft(o, "backboards", "ky-board"), d: `M${num(back)} ${num(cy - board)}V${num(cy + board)}M${num(L - back)} ${num(cy - board)}V${num(cy + board)}M${num(back)} ${num(cy)}H${num(bx - court.rim)}M${num(L - back)} ${num(cy)}H${num(L - bx + court.rim)}`, "stroke-width": 0.5, fill: "none" });
108
124
  lines += el("circle", { class: ft(o, "rims", "ky-rim"), cx: bx, cy, r: court.rim, "stroke-width": 0.3, fill: "none" });
109
125
  lines += el("circle", { class: ft(o, "rims", "ky-rim"), cx: L - bx, cy, r: court.rim, "stroke-width": 0.3, fill: "none" });
@@ -134,17 +150,22 @@ export function basketball(o, era, court = NBA_COURT) {
134
150
  if (marks.substitution !== null)
135
151
  lines += el("path", { class: ft(o, "court-marks", "ky-line"), d: [L / 2 - marks.substitution, L / 2 + marks.substitution].map((x) => `M${num(x)} ${num(W)}v${num(-side.length)}`).join(""), "stroke-width": thin, fill: "none" });
136
152
  }
137
- // The court's own words and logo: the baseline words in the apron, the logo in the centre circle.
153
+ // The court's own words and logo: the baseline words in the apron, the logo in the centre circle. A hardwood floor sets the words in its border instead.
138
154
  const words = o.court?.baselineText;
139
- if (words !== undefined) {
155
+ if (hw !== undefined)
156
+ lines += hardwoodWords(o, { L, W, bw, margin: m, id: o.id }, turned, o.court?.sidelineText);
157
+ else if (words !== undefined) {
140
158
  const x = -bw - 1.4;
141
159
  lines += textOn(turned, x, cy + 1.1, words[0], { size: 3, anchor: "middle", rot: -90, cls: ft(o, "baseline-text", "ky-baseline"), weight: 800, spacing: 0.3 });
142
160
  lines += textOn(turned, L - x, cy + 1.1, words[1], { size: 3, anchor: "middle", rot: 90, cls: ft(o, "baseline-text", "ky-baseline"), weight: 800, spacing: 0.3 });
143
161
  }
144
162
  if (o.court?.centreLogo !== undefined) {
145
- const s = court.centreRadius * 1.5;
163
+ const s = court.centreRadius * (hw === undefined ? 1.5 : 1.8);
146
164
  lines += el("image", { href: o.court.centreLogo, x: L / 2 - s / 2, y: cy - s / 2, width: s, height: s, preserveAspectRatio: "xMidYMid meet", transform: turned ? `rotate(-90 ${num(L / 2)} ${num(cy)})` : undefined });
147
165
  }
166
+ // Beside the bottom sideline: the coaching box, the scorers' table and the benches.
167
+ if (hw?.bench === true)
168
+ lines += bench(o, { L, W, bw, box: marks?.sideline.fromBaseline ?? L * 0.3, thin }, m);
148
169
  // The teams' places and the score's panel, in proportion to the court (the NBA's is 94 feet by 50).
149
170
  const kx = L / NBA_COURT.length;
150
171
  const ky = W / NBA_COURT.width;
@@ -153,7 +174,7 @@ export function basketball(o, era, court = NBA_COURT) {
153
174
  view: view0,
154
175
  base,
155
176
  lines,
156
- defs: "",
177
+ defs: floor?.defs ?? "",
157
178
  left: { cx: 16.5 * kx, cy, w: 27 * kx, h: 25 * ky, rot: 0, panel: true },
158
179
  right: { cx: L - 16.5 * kx, cy, w: 27 * kx, h: 25 * ky, rot: 0, panel: true },
159
180
  plate: { cx: L / 2, cy, w: 34 * kx, h: 20 * ky },
@@ -162,6 +183,7 @@ export function basketball(o, era, court = NBA_COURT) {
162
183
  };
163
184
  surface.lineRef = { width: thin, feet: 1 };
164
185
  surface.box = { x: 0, y: 0, w: L, h: W };
186
+ surface.centreRadius = court.centreRadius;
165
187
  // The paint: the lane from the baseline to the free-throw line and the circle beyond it, with a little floor round them.
166
188
  const paintDeep = keyW + R + 1.5;
167
189
  const paintHalf = Math.min(cy, Math.max(half, wide, R) + 2);
@@ -0,0 +1,6 @@
1
+ import type { BudoPlan } from "../budo/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 budoSurface(o: BuildOptions, plan: BudoPlan, margin: number): Surface;
@@ -0,0 +1,104 @@
1
+ // A budō surface lying wide, from its plan (budo/plan.ts): the floor and the free zone, the mats in their tones with their heri and their seams, the walls, the clay and the straw of a dohyō, the tape,
2
+ // the targets. 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 surface'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 budoSurface(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
+ let base = el("rect", { class: ft(o, "apron", "ky-apron"), x: view0.x, y: view0.y, width: view0.w, height: view0.h });
23
+ let ground = "";
24
+ let defs = "";
25
+ // The weave of a mat, over the tones and under the heri and the seams: a ridge and its highlight, a repeating gradient that every mat shares (one `<defs>` entry, which a `<pattern>` would
26
+ // have to share with the background's), turned a quarter for a mat that lies across. Drawn only at full detail.
27
+ const pieces = plan.dojo?.pieces ?? plan.judo?.pieces ?? [];
28
+ const weave = full && plan.texture && pieces.length > 0;
29
+ const stripe = (at, colour, opacity, width) => el("stop", { offset: at, "stop-color": colour, "stop-opacity": 0 }) + el("stop", { offset: at, "stop-color": colour, "stop-opacity": opacity }) + el("stop", { offset: at + width, "stop-color": colour, "stop-opacity": opacity }) + el("stop", { offset: at + width, "stop-color": colour, "stop-opacity": 0 });
30
+ const woven = () => {
31
+ defs += el("linearGradient", { id: `${o.id}-weave`, x1: 0, y1: 0, x2: 0, y2: 0.1, gradientUnits: "userSpaceOnUse", spreadMethod: "repeat" }, stripe(0.1, "#000000", 0.16, 0.22) + stripe(0.52, "#ffffff", 0.14, 0.16));
32
+ const rect = (p) => `M${p.x0} ${p.y0}H${p.x1}V${p.y1}H${p.x0}Z`;
33
+ // A mat that lies across is drawn in a group turned a quarter, with its corners turned back, so that its ridges run along it.
34
+ const turned = (p) => `M${p.y0} ${-p.x0}H${p.y1}V${-p.x1}H${p.y0}Z`;
35
+ const across = pieces.filter((p) => p.along === "x");
36
+ const down = pieces.filter((p) => p.along === "y");
37
+ let out = "";
38
+ if (across.length > 0)
39
+ out += el("path", { class: "ky-bud-weave", d: across.map(rect).join(""), fill: `url(#${o.id}-weave)` });
40
+ if (down.length > 0)
41
+ out += el("g", { transform: "rotate(90)" }, el("path", { class: "ky-bud-weave", d: down.map(turned).join(""), fill: `url(#${o.id}-weave)` }));
42
+ // A faint light line along the edge of each mat, where its rounded edge catches the light.
43
+ out += el("path", { class: "ky-bud-edge", d: pieces.map(rect).join(""), fill: "none" });
44
+ return out;
45
+ };
46
+ let pending = false;
47
+ let wove = false;
48
+ for (const a of plan.areas) {
49
+ const tone = a.cls === "ky-bud-t1" || a.cls === "ky-bud-t2" || a.cls === "ky-bud-t3";
50
+ if (weave && !wove && pending && !tone) {
51
+ wove = true;
52
+ ground += woven();
53
+ }
54
+ if (tone)
55
+ pending = true;
56
+ ground += el("path", { class: ft(o, a.feature, a.cls), d: areaD(a), "fill-rule": a.evenodd === true ? "evenodd" : undefined });
57
+ }
58
+ if (weave && !wove)
59
+ ground += woven();
60
+ base += group(ground, "ky-bud-ground");
61
+ const thick = o.exact ? plan.lineWidth : plan.drawnLine;
62
+ // The seams are the plan's finest lines: left out of a drawing that asks for the simple detail.
63
+ const shown = full ? plan : { ...plan, shapes: plan.shapes.filter((s) => s.feature !== "seams" && s.feature !== "boards") };
64
+ const lines = group(strokes(shown, o) + marks(shown, o), "ky-bud-lines");
65
+ const W = (plan.box.x1 - plan.box.x0) * K;
66
+ const H = (plan.box.y1 - plan.box.y0) * K;
67
+ const cx = (plan.centre.x - ox) * K;
68
+ const cy = (plan.centre.y - oy) * K;
69
+ // The score's board lies on the middle of the surface; the teams are named on it, not painted on the mats.
70
+ const plateW = Math.min(W * 0.52, 190);
71
+ // The names a score paints (kyūdō's and a dojo's: the others have a board): on the shajō's floor and over the yamichi, or either side of the middle.
72
+ const kyu = plan.kyudo;
73
+ const places = kyu === undefined ? { left: { cx: W * 0.27, cy: H / 2, w: W * 0.2, h: H * 0.11, rot: 0 }, right: { cx: W * 0.73, cy: H / 2, w: W * 0.2, h: H * 0.11, rot: 0 } } : { left: { cx: ((kyu.shajo.x0 + kyu.shajo.x1) / 2 - ox) * K, cy: H * 0.3, w: (kyu.shajo.x1 - kyu.shajo.x0) * K * 0.5, h: H * 0.09, rot: 0 }, right: { cx: ((kyu.yamichi.x0 + kyu.yamichi.x1) / 2 + (kyu.yamichi.x1 - kyu.yamichi.x0) / 4 - ox) * K, cy: (((kyu.yamichi.y0 + kyu.yamichi.y1) / 2) - oy) * K, w: (kyu.yamichi.x1 - kyu.yamichi.x0) * K * 0.3, h: H * 0.09, rot: 0 } };
74
+ const surface = {
75
+ sport: plan.sport,
76
+ view: view0,
77
+ base,
78
+ lines,
79
+ defs,
80
+ left: places.left,
81
+ right: places.right,
82
+ plate: { cx, cy, w: plateW, h: plateW * 0.55 },
83
+ turned,
84
+ bannersOver: true,
85
+ onColour: false,
86
+ centreRadius: plan.centre.r * K,
87
+ };
88
+ surface.box = { x: 0, y: 0, w: W, h: H };
89
+ surface.lineRef = { width: thick, feet: K };
90
+ const ranges = {};
91
+ for (const [name, r] of Object.entries(plan.ranges))
92
+ ranges[name] = rangeOf(r, ox, oy);
93
+ surface.ranges = ranges;
94
+ if (o.landmarks === true) {
95
+ const m = plan.marks.map((p) => ({ id: p.id, x: (p.x - ox) * K, y: (p.y - oy) * K, side: p.side }));
96
+ surface.marks = full ? m : m.filter((p) => !p.id.startsWith("~"));
97
+ }
98
+ if (turned) {
99
+ const t = quarterTurn(view0);
100
+ surface.view = t.view;
101
+ surface.turn = t.turn;
102
+ }
103
+ return surface;
104
+ }
@@ -12,9 +12,25 @@ export interface SheetLook {
12
12
  end: "left" | "right";
13
13
  /** Which league's marks to draw: the hack line and the courtesy lines are World Curling's. */
14
14
  figures: SheetSpec;
15
+ /** For `show: "folded"`: what each enlarged panel holds, the shape of the whole, and the words that label the panels (the left end's and the right end's). */
16
+ fold?: {
17
+ panels: "half" | "house";
18
+ fit: number;
19
+ labels: readonly [string, string];
20
+ };
15
21
  }
16
22
  /** Where each tee is on the sheet, along it from the left backboard: the left tee and the right tee. */
17
23
  export declare function teesOf(spec: Pick<SheetSpec, "length" | "teeFromMiddle">): [number, number];
24
+ /**
25
+ * The part of a bare sheet's ice that `show` and `end` show, in feet in the sheet's wide frame: all of it, or the half from the hog line to the back line, or the house, at one end,
26
+ * with a little ice beyond the lines. A folded sheet shows all of it, with its houses enlarged beside it.
27
+ */
28
+ export declare function shownIce(spec: Pick<SheetSpec, "length" | "width" | "teeFromMiddle" | "backLine" | "hogLine" | "rings" | "lineWidths">, show: SheetShow, end: "left" | "right"): {
29
+ x: number;
30
+ y: number;
31
+ w: number;
32
+ h: number;
33
+ };
18
34
  /**
19
35
  * A curling sheet in the frame of a drawing lying wide: x along it from the left backboard, y across it from the top sideline. A house at
20
36
  * each end (the 12-, 8- and 4-foot circles and the button), the tee, back, hog and centre lines, the hacks, and, in World Curling's, the
@@ -0,0 +1,67 @@
1
+ import type { SheetSpec } from "../leagues/curling.ts";
2
+ /** A box in the sheet's wide frame, in feet. */
3
+ interface Wide {
4
+ x: number;
5
+ y: number;
6
+ w: number;
7
+ h: number;
8
+ }
9
+ /** One enlarged end. */
10
+ export interface FoldPanel {
11
+ end: "left" | "right";
12
+ /** The part of the sheet it shows, in the sheet's own feet (the boards included across it). */
13
+ crop: Wide;
14
+ /** Where it is drawn, in the wide frame of the whole drawing. */
15
+ box: Wide;
16
+ /** The transform of the part of the sheet that fills it, and the id of the clip that cuts it to `crop`. */
17
+ transform: string;
18
+ clip: string;
19
+ /** Where along the sheet, in the wide frame of the drawing, the blank ice between the house and the hog line is centred: a label stands there. Missing when the panel holds only the house. */
20
+ free?: number;
21
+ }
22
+ /** The folded layout of a sheet. */
23
+ export interface FoldPlan {
24
+ /** How much larger than the strip each panel is drawn. */
25
+ scale: number;
26
+ /** The viewBox of the drawing as it lies wide: the strip is at y = 0 to the sheet's width, and the panels are above and below it. */
27
+ view: Wide;
28
+ /** The left end's panel first and the right end's second. */
29
+ panels: [FoldPanel, FoldPanel];
30
+ }
31
+ /** What the layout is worked out from. */
32
+ export interface FoldOptions {
33
+ /** What each panel holds: the half from the hog line to the back line, or the house. */
34
+ panels: "half" | "house";
35
+ /** The width of the drawing as the eye sees it over its height. */
36
+ fit: number;
37
+ /** The ground round the whole, in feet. */
38
+ margin: number;
39
+ /** True when the drawing lies tall: its panels are then at its left and right, and the left end's is at the left. */
40
+ turned: boolean;
41
+ /** A prefix for the ids of the clips. */
42
+ id: string;
43
+ }
44
+ /** The part of the sheet an end's panel shows, in the sheet's own feet along it (the boards included across it). */
45
+ export declare function foldCrop(spec: SheetSpec, tee: number, end: "left" | "right", panels: "half" | "house"): Wide;
46
+ /**
47
+ * Where everything of a folded sheet goes. The strip keeps its own place (x along the sheet from 0 to its length, y across it from 0), so that a data layer's numbers are the same
48
+ * on the strip as on a sheet drawn whole; each panel is the same drawing at a larger scale, which is chosen so that the whole has the shape asked for (`fit`), and which
49
+ * is never less than the strip's own nor wider than the strip.
50
+ */
51
+ export declare function foldPlan(spec: SheetSpec, tees: readonly [number, number], o: FoldOptions): FoldPlan;
52
+ /** The clips of the panels: each cuts the sheet to the part its panel shows. */
53
+ export declare const foldClips: (plan: FoldPlan) => string;
54
+ /** The sheet's own markup laid in each panel, cut to the part the panel shows. */
55
+ export declare const foldCopies: (plan: FoldPlan, markup: string) => string;
56
+ /**
57
+ * What joins each panel to the part of the strip it enlarges: a faint fan from the panel's edge to that part, and a frame round the part, as an inset on a map is joined to what it shows.
58
+ * The panels' own frames are drawn here too.
59
+ */
60
+ export declare function foldLinks(plan: FoldPlan, sheetWidth: number): string;
61
+ /**
62
+ * The label of each panel, in a small plate that is upright whichever way the drawing lies. It stands in the free ground beyond the panel's far end, in line with the panel's middle (the
63
+ * panels are as long as the strip allows, not as long as the frame, so there is room there); when there is none, over the blank ice of a half between the house and the hog line.
64
+ * `labels` are the words for the left end's panel and the right end's.
65
+ */
66
+ export declare function foldLabels(plan: FoldPlan, labels: readonly [string, string], turned: boolean): string;
67
+ export {};
@@ -0,0 +1,107 @@
1
+ // The folded curling sheet: the whole sheet as a strip, and the two houses enlarged beside it, so that a sheet 150 feet long and 15 wide fills a roughly square frame. As a
2
+ // tall drawing the top house is enlarged on the left of the strip and the bottom house on the right; as a wide one the left house is above the strip and the right house below it.
3
+ // The layout is worked out here, in the sheet's wide frame, and the builder only draws it. Written for this package.
4
+ import { el, num } from "../format.js";
5
+ import { textWidth } from "../text.js";
6
+ /** The ground, in feet, between the strip and each panel, across which the panel is joined to the part of the strip it enlarges. */
7
+ const GAP = 4;
8
+ /** The width of the boards round the ice, as the sheet draws them. */
9
+ const BOARD = 0.7;
10
+ /** The ice kept beyond the lines of the part of an end that a panel shows. */
11
+ const ALONG = 1.5;
12
+ /** The part of the sheet an end's panel shows, in the sheet's own feet along it (the boards included across it). */
13
+ export function foldCrop(spec, tee, end, panels) {
14
+ const [twelve] = spec.rings;
15
+ const hogW = spec.lineWidths.hog;
16
+ const outer = panels === "half" ? spec.hogLine + hogW : twelve;
17
+ const x0 = end === "left" ? tee - spec.backLine - ALONG : tee - outer - ALONG;
18
+ const x1 = end === "left" ? tee + outer + ALONG : tee + spec.backLine + ALONG;
19
+ return { x: x0, y: -BOARD, w: x1 - x0, h: spec.width + 2 * BOARD };
20
+ }
21
+ /**
22
+ * Where everything of a folded sheet goes. The strip keeps its own place (x along the sheet from 0 to its length, y across it from 0), so that a data layer's numbers are the same
23
+ * on the strip as on a sheet drawn whole; each panel is the same drawing at a larger scale, which is chosen so that the whole has the shape asked for (`fit`), and which
24
+ * is never less than the strip's own nor wider than the strip.
25
+ */
26
+ export function foldPlan(spec, tees, o) {
27
+ const across = spec.width + 2 * BOARD;
28
+ const crops = [foldCrop(spec, tees[0], "left", o.panels), foldCrop(spec, tees[1], "right", o.panels)];
29
+ const whole = spec.length + 2 * o.margin;
30
+ // The height of the whole as the drawing lies wide: the strip, two panels and the ground round them. The eye's width over height is `fit`, which a tall drawing turns the other way.
31
+ const ratio = o.turned ? o.fit : 1 / o.fit;
32
+ const wanted = (ratio * whole - 2 * o.margin - 2 * GAP - across) / (2 * across);
33
+ const widest = Math.min(spec.length / crops[0].w, spec.length / crops[1].w);
34
+ const scale = Math.max(1, Math.min(wanted, widest));
35
+ const high = across * scale;
36
+ // The left end is at the bottom of a drawing that lies wide when it is to be turned, because the turn sends the bottom to the left.
37
+ const tops = o.turned ? ["right", "left"] : ["left", "right"];
38
+ const make = (end) => {
39
+ const crop = crops[end === "left" ? 0 : 1];
40
+ const w = crop.w * scale;
41
+ const top = tops[0] === end;
42
+ const x = end === "left" ? -BOARD : spec.length + BOARD - w;
43
+ const y = top ? -BOARD - GAP - high : spec.width + BOARD + GAP;
44
+ // The blank ice of a half: between the outer edge of the house and the hog line, towards the middle of the sheet.
45
+ const toward = end === "left" ? 1 : -1;
46
+ const mid = tees[end === "left" ? 0 : 1] + (toward * (spec.rings[0] + spec.hogLine)) / 2;
47
+ return {
48
+ free: o.panels === "half" ? x + (mid - crop.x) * scale : undefined,
49
+ end,
50
+ crop,
51
+ box: { x, y, w, h: high },
52
+ transform: `matrix(${num(scale)} 0 0 ${num(scale)} ${num(x - crop.x * scale)} ${num(y + BOARD * scale)})`,
53
+ clip: `${o.id}-fold-${end}`,
54
+ };
55
+ };
56
+ const left = make("left");
57
+ const right = make("right");
58
+ const top = Math.min(left.box.y, right.box.y);
59
+ const bottom = Math.max(left.box.y, right.box.y) + high;
60
+ return { scale, view: { x: -o.margin, y: top - o.margin, w: whole, h: bottom - top + 2 * o.margin }, panels: [left, right] };
61
+ }
62
+ /** The clips of the panels: each cuts the sheet to the part its panel shows. */
63
+ export const foldClips = (plan) => plan.panels.map((p) => el("clipPath", { id: p.clip }, el("rect", { x: num(p.crop.x), y: num(p.crop.y), width: num(p.crop.w), height: num(p.crop.h) }))).join("");
64
+ /** The sheet's own markup laid in each panel, cut to the part the panel shows. */
65
+ export const foldCopies = (plan, markup) => plan.panels.map((p) => el("g", { class: "ky-fold", transform: p.transform, "clip-path": `url(#${p.clip})` }, markup)).join("");
66
+ /**
67
+ * What joins each panel to the part of the strip it enlarges: a faint fan from the panel's edge to that part, and a frame round the part, as an inset on a map is joined to what it shows.
68
+ * The panels' own frames are drawn here too.
69
+ */
70
+ export function foldLinks(plan, sheetWidth) {
71
+ let out = "";
72
+ for (const p of plan.panels) {
73
+ const above = p.box.y < 0;
74
+ const edge = above ? p.box.y + p.box.h : p.box.y;
75
+ const meet = above ? -BOARD : sheetWidth + BOARD;
76
+ const { x: a, w } = p.crop;
77
+ out += el("path", { class: "ky-fold-link", d: `M${num(p.box.x)} ${num(edge)}L${num(a)} ${num(meet)}H${num(a + w)}L${num(p.box.x + p.box.w)} ${num(edge)}Z` });
78
+ out += el("path", { class: "ky-fold-edge", fill: "none", d: `M${num(p.box.x)} ${num(edge)}L${num(a)} ${num(meet)}M${num(p.box.x + p.box.w)} ${num(edge)}L${num(a + w)} ${num(meet)}` });
79
+ out += el("rect", { class: "ky-fold-hl", x: num(a), y: num(-BOARD), width: num(w), height: num(sheetWidth + 2 * BOARD) });
80
+ out += el("rect", { class: "ky-fold-frame", x: num(p.box.x), y: num(p.box.y), width: num(p.box.w), height: num(p.box.h) });
81
+ }
82
+ return out;
83
+ }
84
+ /**
85
+ * The label of each panel, in a small plate that is upright whichever way the drawing lies. It stands in the free ground beyond the panel's far end, in line with the panel's middle (the
86
+ * panels are as long as the strip allows, not as long as the frame, so there is room there); when there is none, over the blank ice of a half between the house and the hog line.
87
+ * `labels` are the words for the left end's panel and the right end's.
88
+ */
89
+ export function foldLabels(plan, labels, turned) {
90
+ const size = Math.max(1.2, Math.min(5.5, 0.03 * Math.max(plan.view.w, plan.view.h)));
91
+ const inset = size * 0.6;
92
+ const high = size * 1.9;
93
+ return plan.panels
94
+ .map((p, i) => {
95
+ const wide = textWidth(labels[i]) * size + size * 1.6;
96
+ // The plate's length along the sheet is its height when the drawing is turned.
97
+ const extent = turned ? high : wide;
98
+ const room = p.end === "left" ? plan.view.x + plan.view.w - (p.box.x + p.box.w) : p.box.x - plan.view.x;
99
+ const beyond = room >= extent + 2 * inset;
100
+ const cy = p.box.y + p.box.h / 2;
101
+ const cx = beyond ? (p.end === "left" ? p.box.x + p.box.w + inset + extent / 2 : p.box.x - inset - extent / 2) : (p.free ?? p.box.x + p.box.w / 2);
102
+ const plate = el("rect", { class: "ky-fold-pill", x: num(-wide / 2), y: num(-high / 2), width: num(wide), height: num(high), rx: num(size * 0.4) });
103
+ const text = el("text", { class: "ky-text ky-fold-t", x: 0, y: num(size * 0.36), "text-anchor": "middle", "font-size": num(size), "font-weight": 700 }, labels[i].replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"));
104
+ return el("g", { class: "ky-fold-label", transform: `translate(${num(cx)} ${num(cy)})${turned ? " rotate(-90)" : ""}` }, plate + text);
105
+ })
106
+ .join("");
107
+ }
@@ -1,9 +1,24 @@
1
1
  import { el, num } from "../format.js";
2
2
  import { ft, quarterTurn } from "./surface.js";
3
+ import { foldClips, foldCopies, foldLabels, foldLinks, foldPlan } from "./curling.fold.js";
3
4
  /** Where each tee is on the sheet, along it from the left backboard: the left tee and the right tee. */
4
5
  export function teesOf(spec) {
5
6
  return [spec.length / 2 - spec.teeFromMiddle, spec.length / 2 + spec.teeFromMiddle];
6
7
  }
8
+ /**
9
+ * The part of a bare sheet's ice that `show` and `end` show, in feet in the sheet's wide frame: all of it, or the half from the hog line to the back line, or the house, at one end,
10
+ * with a little ice beyond the lines. A folded sheet shows all of it, with its houses enlarged beside it.
11
+ */
12
+ export function shownIce(spec, show, end) {
13
+ if (show !== "half" && show !== "house")
14
+ return { x: 0, y: 0, w: spec.length, h: spec.width };
15
+ const along = 1.5;
16
+ const [tl, tr] = teesOf(spec);
17
+ const outer = show === "half" ? spec.hogLine + spec.lineWidths.hog : spec.rings[0];
18
+ const x0 = end !== "right" ? tl - spec.backLine - along : tr - outer - along;
19
+ const x1 = end !== "right" ? tl + outer + along : tr + spec.backLine + along;
20
+ return { x: x0, y: 0, w: x1 - x0, h: spec.width };
21
+ }
7
22
  /**
8
23
  * A curling sheet in the frame of a drawing lying wide: x along it from the left backboard, y across it from the top sideline. A house at
9
24
  * each end (the 12-, 8- and 4-foot circles and the button), the tee, back, hog and centre lines, the hacks, and, in World Curling's, the
@@ -22,7 +37,8 @@ export function curling(o, look) {
22
37
  const hogW = spec.lineWidths.hog;
23
38
  const board = 0.7;
24
39
  let defs = "";
25
- let base = el("rect", { class: ft(o, "apron", "ky-apron"), x: -m, y: -m, width: L + 2 * m, height: W + 2 * m });
40
+ const apron = (x, y, w, h) => el("rect", { class: ft(o, "apron", "ky-apron"), x, y, width: w, height: h });
41
+ let base = "";
26
42
  base += el("path", { class: ft(o, "boards", "ky-boards"), "fill-rule": "evenodd", d: `M${num(-board)} ${num(-board)}H${num(L + board)}V${num(W + board)}H${num(-board)}ZM0 0V${num(W)}H${num(L)}V0Z` });
27
43
  base += el("rect", { class: ft(o, "ice", "ky-surface"), x: 0, y: 0, width: L, height: W });
28
44
  if (look.pebble) {
@@ -70,10 +86,20 @@ export function curling(o, look) {
70
86
  lines += el("path", { class: ft(o, "courtesy-lines", "ky-line"), d: xs.map((x) => `M${num(x)} 0v${num(c.length)}M${num(x)} ${num(W)}v${num(-c.length)}`).join(""), "stroke-width": lineW, fill: "none" });
71
87
  }
72
88
  }
73
- // What is in view: all of the sheet, the half from the hog line to the back line, or the house, at one end.
89
+ // What is in view: all of the sheet, the half from the hog line to the back line, or the house, at one end; or all of it with its two houses enlarged beside it.
74
90
  const along = 1.5;
75
91
  let view0 = { x: -m, y: -m, w: L + 2 * m, h: W + 2 * m };
76
92
  const left = look.end !== "right";
93
+ const folded = look.show === "folded" ? foldPlan(spec, [tl, tr], { panels: look.fold?.panels ?? "half", fit: look.fold?.fit ?? 1, margin: m, turned: o.portrait, id: o.id }) : undefined;
94
+ if (folded !== undefined) {
95
+ view0 = folded.view;
96
+ base = `${apron(view0.x, view0.y, view0.w, view0.h)}${base}${foldCopies(folded, base)}`;
97
+ lines = `${lines}${foldCopies(folded, lines)}${foldLinks(folded, W)}`;
98
+ defs += foldClips(folded);
99
+ }
100
+ else {
101
+ base = apron(-m, -m, L + 2 * m, W + 2 * m) + base;
102
+ }
77
103
  if (look.show === "half" || look.show === "house") {
78
104
  const outer = look.show === "half" ? spec.hogLine + hogW : twelve;
79
105
  const x0 = left ? tl - spec.backLine - along : tr - outer - along;
@@ -96,7 +122,15 @@ export function curling(o, look) {
96
122
  };
97
123
  surface.lineRef = { width: lineW, feet: 1 };
98
124
  surface.box = { x: 0, y: 0, w: L, h: W };
99
- if (o.landmarks === true) {
125
+ // A pageant: a picture in each house, the emblem in the middle of the sheet.
126
+ surface.pageant = { left: { cx: tl, cy, w: twelve * 2, h: twelve * 2, disc: twelve * 2 }, right: { cx: tr, cy, w: twelve * 2, h: twelve * 2, disc: twelve * 2 }, centre: { cx: L / 2, cy, r: W * 0.42 } };
127
+ if (folded !== undefined) {
128
+ // The panels hold the data of the strip again, larger; the labels go over it.
129
+ surface.panels = folded.panels.map((p) => ({ transform: p.transform, clip: p.clip }));
130
+ surface.over = foldLabels(folded, look.fold?.labels ?? ["", ""], o.portrait);
131
+ surface.folded = true;
132
+ }
133
+ if (o.landmarks === true && folded === undefined) {
100
134
  const marks = [
101
135
  { id: "hog-line", x: tl + spec.hogLine + hogW / 2, y: 0, side: "top" },
102
136
  { id: "hog-line", x: tr - spec.hogLine - hogW / 2, y: 0, side: "top" },
@@ -0,0 +1,9 @@
1
+ import type { DivePlan } from "../pool.ts";
2
+ import { type PoolCtx } from "../pool.kit.ts";
3
+ import { type BuildOptions, type Surface } from "./surface.ts";
4
+ /**
5
+ * 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
6
+ * 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
7
+ * 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.
8
+ */
9
+ export declare function diving(o: BuildOptions, plan: DivePlan, ctx?: PoolCtx): Surface;