@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.
- package/CHANGELOG.md +133 -0
- package/NOTICE.md +182 -2
- package/README.md +106 -19
- package/dist/annotate.js +1 -1
- package/dist/arenas-entry.d.ts +4 -0
- package/dist/arenas-entry.js +3 -0
- package/dist/arenas.d.ts +125 -0
- package/dist/arenas.data.d.ts +2 -0
- package/dist/arenas.data.js +3763 -0
- package/dist/arenas.js +72 -0
- package/dist/athletics/describe.d.ts +8 -0
- package/dist/athletics/describe.js +41 -0
- package/dist/athletics/events.d.ts +17 -0
- package/dist/athletics/events.js +48 -0
- package/dist/athletics/facilities.d.ts +80 -0
- package/dist/athletics/facilities.js +176 -0
- package/dist/athletics/infield.d.ts +79 -0
- package/dist/athletics/infield.js +166 -0
- package/dist/athletics/layout.d.ts +13 -0
- package/dist/athletics/layout.js +98 -0
- package/dist/athletics/oval.d.ts +57 -0
- package/dist/athletics/oval.js +116 -0
- package/dist/athletics/plan.d.ts +97 -0
- package/dist/athletics/plan.js +355 -0
- package/dist/athletics/races.d.ts +102 -0
- package/dist/athletics/races.js +355 -0
- package/dist/athletics/steeplechase.d.ts +72 -0
- package/dist/athletics/steeplechase.js +196 -0
- package/dist/athletics/stencil.d.ts +13 -0
- package/dist/athletics/stencil.js +85 -0
- package/dist/athletics/track.d.ts +54 -0
- package/dist/athletics/track.js +105 -0
- package/dist/athletics/types.d.ts +77 -0
- package/dist/athletics/types.js +1 -0
- package/dist/athletics-entry.d.ts +17 -0
- package/dist/athletics-entry.js +11 -0
- package/dist/athletics.style.d.ts +16 -0
- package/dist/athletics.style.js +51 -0
- package/dist/atlas-entry.d.ts +6 -2
- package/dist/atlas-entry.js +4 -2
- package/dist/atlas.d.ts +117 -23
- package/dist/atlas.js +197 -12
- package/dist/background.js +2 -1
- package/dist/budo/describe.d.ts +13 -0
- package/dist/budo/describe.js +120 -0
- package/dist/budo/dojo.api.d.ts +64 -0
- package/dist/budo/dojo.api.js +46 -0
- package/dist/budo/dojo.d.ts +72 -0
- package/dist/budo/dojo.js +300 -0
- package/dist/budo/judo.d.ts +61 -0
- package/dist/budo/judo.js +109 -0
- package/dist/budo/karate.d.ts +29 -0
- package/dist/budo/karate.js +80 -0
- package/dist/budo/kendo.d.ts +18 -0
- package/dist/budo/kendo.js +44 -0
- package/dist/budo/kimarite.d.ts +19 -0
- package/dist/budo/kimarite.js +33 -0
- package/dist/budo/kit.d.ts +16 -0
- package/dist/budo/kit.js +24 -0
- package/dist/budo/kyudo.d.ts +28 -0
- package/dist/budo/kyudo.js +66 -0
- package/dist/budo/looks.d.ts +28 -0
- package/dist/budo/looks.js +58 -0
- package/dist/budo/parts.d.ts +32 -0
- package/dist/budo/parts.js +10 -0
- package/dist/budo/plan.d.ts +64 -0
- package/dist/budo/plan.js +139 -0
- package/dist/budo/room.d.ts +133 -0
- package/dist/budo/room.js +111 -0
- package/dist/budo/sumo.d.ts +31 -0
- package/dist/budo/sumo.js +71 -0
- package/dist/budo/tatami.d.ts +55 -0
- package/dist/budo/tatami.js +156 -0
- package/dist/budo/types.d.ts +192 -0
- package/dist/budo/types.js +2 -0
- package/dist/budo-entry.d.ts +22 -0
- package/dist/budo-entry.js +13 -0
- package/dist/budo.board.d.ts +28 -0
- package/dist/budo.board.js +81 -0
- package/dist/budo.score.d.ts +18 -0
- package/dist/budo.score.js +52 -0
- package/dist/budo.style.d.ts +6 -0
- package/dist/budo.style.js +67 -0
- package/dist/charts-entry.d.ts +6 -1
- package/dist/charts-entry.js +3 -0
- package/dist/charts.athletics.d.ts +183 -0
- package/dist/charts.athletics.js +455 -0
- package/dist/charts.baseball.js +1 -1
- package/dist/charts.curling.heat.d.ts +24 -0
- package/dist/charts.curling.heat.js +116 -0
- package/dist/charts.curling.js +14 -3
- package/dist/charts.js +4 -4
- package/dist/charts.pool.d.ts +67 -0
- package/dist/charts.pool.js +76 -0
- package/dist/charts.skating.d.ts +204 -0
- package/dist/charts.skating.js +426 -0
- package/dist/charts.soccer.js +3 -3
- package/dist/charts.types.d.ts +63 -1
- package/dist/colour.d.ts +4 -4
- package/dist/colour.js +4 -4
- package/dist/court.d.ts +85 -2
- package/dist/court.js +102 -7
- package/dist/court.more.d.ts +46 -0
- package/dist/court.more.js +219 -0
- package/dist/courts.style.d.ts +14 -2
- package/dist/courts.style.js +51 -4
- package/dist/crowd.d.ts +164 -0
- package/dist/crowd.draw.d.ts +17 -0
- package/dist/crowd.draw.js +204 -0
- package/dist/crowd.js +480 -0
- package/dist/crowd.park.d.ts +19 -0
- package/dist/crowd.park.js +153 -0
- package/dist/curling.stats.d.ts +271 -0
- package/dist/curling.stats.js +301 -0
- package/dist/draw.d.ts +16 -0
- package/dist/draw.js +462 -54
- package/dist/element-entry.d.ts +3 -1
- package/dist/element-entry.js +2 -1
- package/dist/element.d.ts +13 -2
- package/dist/element.js +79 -4
- package/dist/emblems-source.d.ts +50 -0
- package/dist/emblems-source.js +56 -0
- package/dist/eras.d.ts +3 -1
- package/dist/eras.js +8 -3
- package/dist/features.js +204 -0
- package/dist/field.data.d.ts +16 -0
- package/dist/field.data.js +50 -0
- package/dist/fields.kit.d.ts +2 -2
- package/dist/fields.types.d.ts +24 -4
- package/dist/formations.js +18 -7
- package/dist/formations.sports.d.ts +8 -0
- package/dist/formations.sports.js +251 -0
- package/dist/frames.providers.js +71 -8
- package/dist/geojson.js +3 -0
- package/dist/geometry-entry.d.ts +2 -1
- package/dist/geometry-entry.js +2 -1
- package/dist/geometry.d.ts +20 -0
- package/dist/geometry.js +78 -1
- package/dist/grounds.d.ts +4 -0
- package/dist/grounds.data.js +12 -0
- package/dist/halls.d.ts +65 -0
- package/dist/halls.js +122 -0
- package/dist/hardwood.d.ts +31 -0
- package/dist/hardwood.js +157 -0
- package/dist/heat.js +21 -13
- package/dist/heat.types.d.ts +5 -0
- package/dist/heat.zones.d.ts +4 -2
- package/dist/heat.zones.js +207 -7
- package/dist/homeside.js +24 -0
- package/dist/index.d.ts +30 -4
- package/dist/index.js +18 -2
- package/dist/layers-entry.d.ts +2 -1
- package/dist/layers-entry.js +1 -0
- package/dist/layers.d.ts +2 -1
- package/dist/layers.env.d.ts +12 -1
- package/dist/layers.env.js +36 -4
- package/dist/layers.js +32 -13
- package/dist/layers.types.d.ts +9 -2
- package/dist/leagues/athletics.d.ts +63 -0
- package/dist/leagues/athletics.js +87 -0
- package/dist/leagues/budo.d.ts +196 -0
- package/dist/leagues/budo.js +105 -0
- package/dist/leagues/courts.d.ts +269 -4
- package/dist/leagues/courts.js +155 -1
- package/dist/leagues/hockey.js +41 -19
- package/dist/leagues/skating.d.ts +145 -0
- package/dist/leagues/skating.js +73 -0
- package/dist/leagues/sources.js +41 -0
- package/dist/leagues/types.d.ts +1 -1
- package/dist/leagues-entry.d.ts +4 -2
- package/dist/leagues-entry.js +2 -1
- package/dist/leagues.d.ts +17 -4
- package/dist/leagues.js +246 -5
- package/dist/lineup.codes.d.ts +23 -0
- package/dist/lineup.codes.js +122 -0
- package/dist/logo.d.ts +25 -0
- package/dist/logo.js +47 -0
- package/dist/look.d.ts +3 -3
- package/dist/look.js +39 -6
- package/dist/outline.d.ts +1 -1
- package/dist/outline.js +46 -4
- package/dist/pageant.d.ts +120 -0
- package/dist/pageant.draw.d.ts +21 -0
- package/dist/pageant.draw.js +171 -0
- package/dist/pageant.js +211 -0
- package/dist/pageant.style.d.ts +8 -0
- package/dist/pageant.style.js +42 -0
- package/dist/png.d.ts +8 -1
- package/dist/png.js +21 -1
- package/dist/pointer-entry.d.ts +2 -0
- package/dist/pointer-entry.js +2 -0
- package/dist/pointer.d.ts +113 -0
- package/dist/pointer.js +151 -0
- package/dist/pool.d.ts +176 -0
- package/dist/pool.figures.d.ts +306 -0
- package/dist/pool.figures.js +148 -0
- package/dist/pool.js +304 -0
- package/dist/pool.kit.d.ts +48 -0
- package/dist/pool.kit.js +141 -0
- package/dist/pool.labels.d.ts +20 -0
- package/dist/pool.labels.js +258 -0
- package/dist/pool.race.d.ts +65 -0
- package/dist/pool.race.js +140 -0
- package/dist/pool.replay.d.ts +46 -0
- package/dist/pool.replay.js +127 -0
- package/dist/pool.section.d.ts +13 -0
- package/dist/pool.section.js +83 -0
- package/dist/pool.style.d.ts +20 -0
- package/dist/pool.style.js +169 -0
- package/dist/pool.types.d.ts +145 -0
- package/dist/pool.types.js +3 -0
- package/dist/pool.words.d.ts +13 -0
- package/dist/pool.words.js +101 -0
- package/dist/ranges.names.js +53 -3
- package/dist/replay-entry.d.ts +3 -0
- package/dist/replay-entry.js +2 -0
- package/dist/restyle.js +1 -1
- package/dist/road.draw.js +3 -2
- package/dist/road.js +2 -1
- package/dist/road.types.d.ts +3 -0
- package/dist/rules.d.ts +138 -0
- package/dist/rules.js +511 -0
- package/dist/rules.types.d.ts +4 -0
- package/dist/rules.types.js +64 -0
- package/dist/score.d.ts +3 -0
- package/dist/score.js +93 -6
- package/dist/sheet.d.ts +3 -0
- package/dist/sheet.js +7 -3
- package/dist/situation.d.ts +135 -0
- package/dist/situation.draw.d.ts +34 -0
- package/dist/situation.draw.js +202 -0
- package/dist/situation.js +337 -0
- package/dist/situation.style.d.ts +2 -0
- package/dist/situation.style.js +45 -0
- package/dist/situation.types.d.ts +77 -0
- package/dist/situation.types.js +3 -0
- package/dist/situation.widgets.d.ts +39 -0
- package/dist/situation.widgets.js +129 -0
- package/dist/skating/describe.d.ts +7 -0
- package/dist/skating/describe.js +44 -0
- package/dist/skating/events.d.ts +24 -0
- package/dist/skating/events.js +47 -0
- package/dist/skating/figure.d.ts +24 -0
- package/dist/skating/figure.js +37 -0
- package/dist/skating/long.d.ts +54 -0
- package/dist/skating/long.js +111 -0
- package/dist/skating/parts.d.ts +43 -0
- package/dist/skating/parts.js +40 -0
- package/dist/skating/plan.d.ts +55 -0
- package/dist/skating/plan.js +197 -0
- package/dist/skating/short.d.ts +47 -0
- package/dist/skating/short.js +89 -0
- package/dist/skating/track.d.ts +66 -0
- package/dist/skating/track.js +94 -0
- package/dist/skating/types.d.ts +57 -0
- package/dist/skating/types.js +3 -0
- package/dist/skating-entry.d.ts +17 -0
- package/dist/skating-entry.js +11 -0
- package/dist/skating.style.d.ts +4 -0
- package/dist/skating.style.js +24 -0
- package/dist/strings.d.ts +470 -0
- package/dist/strings.js +674 -13
- package/dist/style.d.ts +2 -0
- package/dist/style.js +19 -2
- package/dist/surfaces/artistic.d.ts +9 -0
- package/dist/surfaces/artistic.js +94 -0
- package/dist/surfaces/athletics.d.ts +16 -0
- package/dist/surfaces/athletics.js +132 -0
- package/dist/surfaces/baseball.js +17 -0
- package/dist/surfaces/basketball.floor.d.ts +27 -0
- package/dist/surfaces/basketball.floor.js +83 -0
- package/dist/surfaces/basketball.gear.d.ts +27 -0
- package/dist/surfaces/basketball.gear.js +90 -0
- package/dist/surfaces/basketball.js +44 -22
- package/dist/surfaces/budo.d.ts +6 -0
- package/dist/surfaces/budo.js +104 -0
- package/dist/surfaces/curling.d.ts +16 -0
- package/dist/surfaces/curling.fold.d.ts +67 -0
- package/dist/surfaces/curling.fold.js +107 -0
- package/dist/surfaces/curling.js +37 -3
- package/dist/surfaces/dive.d.ts +9 -0
- package/dist/surfaces/dive.js +128 -0
- package/dist/surfaces/field.d.ts +7 -2
- package/dist/surfaces/field.js +15 -9
- package/dist/surfaces/football.js +7 -0
- package/dist/surfaces/hockey.js +4 -2
- package/dist/surfaces/net.d.ts +2 -1
- package/dist/surfaces/net.js +160 -15
- package/dist/surfaces/polo.d.ts +12 -0
- package/dist/surfaces/polo.js +159 -0
- package/dist/surfaces/pool.d.ts +27 -0
- package/dist/surfaces/pool.js +198 -0
- package/dist/surfaces/skating.d.ts +6 -0
- package/dist/surfaces/skating.js +62 -0
- package/dist/surfaces/soccer.js +2 -1
- package/dist/surfaces/surface.d.ts +76 -0
- package/dist/surfaces/venue.d.ts +15 -0
- package/dist/surfaces/venue.js +95 -0
- package/dist/target-entry.d.ts +2 -0
- package/dist/target-entry.js +2 -0
- package/dist/target.d.ts +200 -0
- package/dist/target.js +525 -0
- package/dist/team.common.d.ts +1 -1
- package/dist/team.common.js +3 -3
- package/dist/team.formation.d.ts +3 -1
- package/dist/team.formation.js +3 -3
- package/dist/team.layers.d.ts +2 -2
- package/dist/team.layers.js +4 -1
- package/dist/team.lineup.d.ts +27 -0
- package/dist/team.lineup.js +207 -0
- package/dist/team.network.js +6 -5
- package/dist/team.shape.js +5 -5
- package/dist/team.types.d.ts +40 -1
- package/dist/tenants.d.ts +77 -0
- package/dist/tenants.data.d.ts +8 -0
- package/dist/tenants.data.js +1793 -0
- package/dist/tenants.js +46 -0
- package/dist/turn-entry.d.ts +12 -0
- package/dist/turn-entry.js +7 -0
- package/dist/turn.camera.d.ts +116 -0
- package/dist/turn.camera.js +184 -0
- package/dist/turn.dom.d.ts +40 -0
- package/dist/turn.dom.js +353 -0
- package/dist/turn.kit.d.ts +86 -0
- package/dist/turn.kit.js +110 -0
- package/dist/turn.markup.d.ts +48 -0
- package/dist/turn.markup.js +108 -0
- package/dist/turn.model.d.ts +78 -0
- package/dist/turn.model.js +91 -0
- package/dist/turn.style.d.ts +5 -0
- package/dist/turn.style.js +56 -0
- package/dist/types.d.ts +221 -20
- package/dist/units.d.ts +1 -1
- package/dist/units.js +22 -1
- package/dist/uprights.board.d.ts +17 -0
- package/dist/uprights.board.js +59 -0
- package/dist/uprights.budo.d.ts +17 -0
- package/dist/uprights.budo.js +87 -0
- package/dist/uprights.d.ts +37 -0
- package/dist/uprights.fields.d.ts +32 -0
- package/dist/uprights.fields.js +172 -0
- package/dist/uprights.goal.d.ts +49 -0
- package/dist/uprights.goal.js +69 -0
- package/dist/uprights.js +169 -0
- package/dist/uprights.more.d.ts +7 -0
- package/dist/uprights.more.js +183 -0
- package/dist/uprights.net.d.ts +12 -0
- package/dist/uprights.net.js +109 -0
- package/dist/uprights.netkit.d.ts +28 -0
- package/dist/uprights.netkit.js +67 -0
- package/dist/uprights.pools.d.ts +8 -0
- package/dist/uprights.pools.js +190 -0
- package/dist/uprights.rinks.d.ts +27 -0
- package/dist/uprights.rinks.js +106 -0
- package/dist/uprights.track.d.ts +50 -0
- package/dist/uprights.track.js +246 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/zones-entry.d.ts +2 -0
- package/dist/zones-entry.js +2 -0
- package/package.json +56 -3
package/dist/pool.js
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// What a pool is for a set of options: the sport's figures from the rules (pool.figures.ts) with the lanes, the ropes, the boards and the look the options ask for, in metres, in the frame of a drawing
|
|
2
|
+
// lying wide (x along the pool from the starting wall on the left, y across it from the top side wall). Kept apart from the drawing so that the outline, the description, the replay and the charts agree.
|
|
3
|
+
import { ARTISTIC, DIVING, SWIMMING, SWIMMING_WIDTHS, WATER_POLO } from "./pool.figures.js";
|
|
4
|
+
import { racePlan } from "./pool.race.js";
|
|
5
|
+
import { FEET_PER_METRE } from "./geometry.js";
|
|
6
|
+
/** The sports of the pool, in the order a menu lists them. */
|
|
7
|
+
export const POOL_SPORTS = Object.freeze(["swimming", "waterpolo", "diving", "artisticswimming"]);
|
|
8
|
+
/** True for a sport whose surface is a pool. */
|
|
9
|
+
export const isPoolSport = (sport) => typeof sport === "string" && POOL_SPORTS.includes(sport);
|
|
10
|
+
/** Metres as feet, the unit a drawing is made in. */
|
|
11
|
+
export const feet = (metres) => metres * FEET_PER_METRE;
|
|
12
|
+
const LOOKS = ["flat", "water", "tiled"];
|
|
13
|
+
const DECKS = ["plain", "tiled"];
|
|
14
|
+
const PLATFORMS = [1, 3, 5, 7.5, 10];
|
|
15
|
+
/** The size of the water of a pool in metres: `length` along the drawing and `width` across it. */
|
|
16
|
+
export const poolSize = (plan) => ({ length: plan.length, width: plan.width });
|
|
17
|
+
const num = (value, what, least, most) => {
|
|
18
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < least || value > most)
|
|
19
|
+
throw new RangeError(`${what} must be a number from ${least} to ${most}: ${String(value)} is not`);
|
|
20
|
+
return value;
|
|
21
|
+
};
|
|
22
|
+
/** The look the options ask for. @throws Error for a look or a deck the pool does not have. */
|
|
23
|
+
export function commonOf(style) {
|
|
24
|
+
const look = style.look ?? "flat";
|
|
25
|
+
if (!LOOKS.includes(look))
|
|
26
|
+
throw new Error(`Unknown pool look "${String(style.look)}": use one of ${LOOKS.join(", ")}`);
|
|
27
|
+
const deck = style.deck ?? "plain";
|
|
28
|
+
if (!DECKS.includes(deck))
|
|
29
|
+
throw new Error(`Unknown deck "${String(style.deck)}": use one of ${DECKS.join(", ")}`);
|
|
30
|
+
const seed = style.seed === undefined ? 1 : Math.trunc(num(style.seed, "The pool's seed", -1e9, 1e9));
|
|
31
|
+
return { look, deck, seed };
|
|
32
|
+
}
|
|
33
|
+
/** The colour of rope `i` of `n + 1`: green outside, yellow round the middle, blue between (Part Two 15.7.6.1 and 15.7.6.3 for eight lanes; 15.7.6.2 for ten). */
|
|
34
|
+
export function ropeColour(i, lanes) {
|
|
35
|
+
if (i === 0 || i === lanes)
|
|
36
|
+
return "green";
|
|
37
|
+
return Math.abs(i - lanes / 2) <= 1 ? "yellow" : "blue";
|
|
38
|
+
}
|
|
39
|
+
/** The width of a swimming pool of this length and this many lanes, the space outside the first and the last lane, and the width of those two lanes. */
|
|
40
|
+
export function laneWidths(course, lanes, temporary) {
|
|
41
|
+
const lane = SWIMMING.lane;
|
|
42
|
+
if (lanes === 10)
|
|
43
|
+
return temporary ? { width: SWIMMING_WIDTHS.temporary, space: SWIMMING.spaceTemporaryWorld, edge: lane } : { width: SWIMMING_WIDTHS.permanent, space: SWIMMING.spacePermanentWorld, edge: SWIMMING.edgeLane };
|
|
44
|
+
if (lanes === 8 && course === "long")
|
|
45
|
+
return { width: SWIMMING_WIDTHS.permanent, space: SWIMMING.spaceOlympic, edge: lane };
|
|
46
|
+
const space = SWIMMING.spaceMinimum;
|
|
47
|
+
return { width: lanes * lane + 2 * space, space, edge: lane };
|
|
48
|
+
}
|
|
49
|
+
/** The lanes and the ropes of a pool of this width, from the top. */
|
|
50
|
+
export function lanesOf(width, lanes, space) {
|
|
51
|
+
const lane = SWIMMING.lane;
|
|
52
|
+
// From the bottom wall: the ropes at the edges of the lanes. Ten lanes in the permanent pool: the end ropes 0.1 m from the walls, the others 2.5 m apart from 2.5 m, so that the markings stay 2.5 m apart (Part Two 15.13.2).
|
|
53
|
+
const fromBottom = [];
|
|
54
|
+
for (let k = 0; k <= lanes; k += 1)
|
|
55
|
+
fromBottom.push(lanes === 10 && width === SWIMMING_WIDTHS.permanent ? (k === 0 ? space : k === 10 ? width - space : k * lane) : space + k * lane);
|
|
56
|
+
const first = lanes === 10 ? 0 : 1;
|
|
57
|
+
const list = [];
|
|
58
|
+
for (let k = 0; k < lanes; k += 1) {
|
|
59
|
+
const mid = lanes === 10 && width === SWIMMING_WIDTHS.permanent ? lane / 2 + k * lane : space + lane / 2 + k * lane;
|
|
60
|
+
list.push({ number: first + k, y: width - mid, top: width - fromBottom[k + 1], bottom: width - fromBottom[k] });
|
|
61
|
+
}
|
|
62
|
+
const ropes = fromBottom.map((v, i) => ({ y: width - v, colour: ropeColour(i, lanes) }));
|
|
63
|
+
return { lanes: list.reverse(), ropes: ropes.reverse() };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The swimming pool the options ask for: `pool.course` (`long`, 50 m, or `short`, 25 m) and `pool.lanes` (8 by default). The width is the rules' for that many lanes: 25 m for eight in a 50 m
|
|
67
|
+
* pool (Part Two 15.6.1), 25 m for ten with lanes 0 and 9 of 2.4 m (Part Two 15.6.2.1), or 26 m for a temporary pool (Part Two 15.6.2.2), and lanes of 2.5 m with spaces of 0.5 m for the other pools.
|
|
68
|
+
* @throws RangeError for a lane count that is not 1 to 10, @throws Error for an unknown look.
|
|
69
|
+
*/
|
|
70
|
+
export function swimPlan(o, defaults = {}) {
|
|
71
|
+
const style = o.pool ?? {};
|
|
72
|
+
const kind = (style.kind ?? defaults.kind) === "warm-up" ? "warm-up" : "competition";
|
|
73
|
+
const course = (style.course ?? defaults.course) === "short" ? "short" : "long";
|
|
74
|
+
const count = style.lanes === undefined && defaults.lanes === undefined ? 8 : Math.trunc(num(style.lanes ?? defaults.lanes, "The number of lanes", 1, 10));
|
|
75
|
+
const { width, space } = laneWidths(course, count, style.temporary === true && count === 10);
|
|
76
|
+
const { lanes, ropes } = lanesOf(width, count, space);
|
|
77
|
+
const length = course === "long" ? SWIMMING.longCourse : SWIMMING.shortCourse;
|
|
78
|
+
const competition = kind === "competition";
|
|
79
|
+
const on = (value) => (value === undefined ? competition : value);
|
|
80
|
+
const depth = style.section !== undefined && typeof style.section === "object" && style.section.depth !== undefined ? num(style.section.depth, "The section's depth", 0.5, 10) : course === "long" ? SWIMMING.depth.olympic : SWIMMING.depth.shortWorld;
|
|
81
|
+
const section = style.section === undefined || style.section === false ? undefined : { exaggerate: typeof style.section === "object" && style.section.exaggerate !== undefined ? num(style.section.exaggerate, "The section's exaggeration", 1, 8) : 2.5 };
|
|
82
|
+
const plan = {
|
|
83
|
+
sport: "swimming",
|
|
84
|
+
...commonOf(style),
|
|
85
|
+
kind,
|
|
86
|
+
course,
|
|
87
|
+
length,
|
|
88
|
+
width,
|
|
89
|
+
lanes,
|
|
90
|
+
space,
|
|
91
|
+
ropes,
|
|
92
|
+
blocks: on(style.blocks),
|
|
93
|
+
pads: on(style.pads),
|
|
94
|
+
flags: on(style.flags),
|
|
95
|
+
falseStart: on(style.falseStart),
|
|
96
|
+
depth,
|
|
97
|
+
section,
|
|
98
|
+
race: undefined,
|
|
99
|
+
};
|
|
100
|
+
if (style.race !== undefined)
|
|
101
|
+
plan.race = racePlan(style.race, lanes.map((l) => l.number), { splitEvery: course === "long" ? 50 : 25, length });
|
|
102
|
+
return plan;
|
|
103
|
+
}
|
|
104
|
+
/** The water polo field the options ask for: 25 by 20 m (Part Six 20.2.2). */
|
|
105
|
+
export function poloPlan(o) {
|
|
106
|
+
const style = o.pool ?? {};
|
|
107
|
+
const table = style.table === "top" ? "top" : "bottom";
|
|
108
|
+
return { sport: "waterpolo", ...commonOf(style), length: WATER_POLO.length + 2 * WATER_POLO.goal.netClear, width: WATER_POLO.width, table };
|
|
109
|
+
}
|
|
110
|
+
/** The measure of a diving table row: the preferred figure. */
|
|
111
|
+
const pref = (pair) => pair[1];
|
|
112
|
+
const BOARDS = {
|
|
113
|
+
"1": { b: DIVING.B.platform1, c: DIVING.C.platform1, a: DIVING.A.platform1 },
|
|
114
|
+
"3": { b: DIVING.B.platform3, c: DIVING.C.platform3, a: DIVING.A.platform3 },
|
|
115
|
+
"5": { b: DIVING.B.platform5, c: DIVING.C.platform5, a: DIVING.A.platform5 },
|
|
116
|
+
"7.5": { b: DIVING.B.platform75, c: DIVING.C.platform75, a: DIVING.A.platform75 },
|
|
117
|
+
"10": { b: DIVING.B.platform10, c: DIVING.C.platform10, a: DIVING.A.platform10 },
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* The diving well the options ask for. The springboards (1 m and 3 m, `pool.boards` of each, 2 by default so that a synchronised pair stands side by side) stand along the left wall, 3 m boards outside, each the preferred
|
|
121
|
+
* distance (Part Four, Appendix 2, B and C) from the wall and from the next; the platforms (5, 7.5 and 10 m by default) stand towards the right wall, the tallest the distance B from it and each lower one the distance C of the
|
|
122
|
+
* platform above it from that one. The well is 25 m across, wider if the boards need more room than that.
|
|
123
|
+
* @throws RangeError for a platform the rules do not have or a count of boards that is not 1 to 4.
|
|
124
|
+
*/
|
|
125
|
+
export function divePlan(o) {
|
|
126
|
+
const style = o.pool ?? {};
|
|
127
|
+
const asked = style.platforms === undefined ? [5, 7.5, 10] : [...style.platforms];
|
|
128
|
+
for (const h of asked)
|
|
129
|
+
if (!PLATFORMS.includes(h))
|
|
130
|
+
throw new RangeError(`The rules have no ${String(h)} m platform: use ${PLATFORMS.join(", ")}`);
|
|
131
|
+
const heights = [...new Set(asked)].sort((a, b) => a - b);
|
|
132
|
+
const count = style.boards === undefined ? 2 : Math.trunc(num(style.boards, "The number of boards of each height", 1, 4));
|
|
133
|
+
const boards = [];
|
|
134
|
+
let left = 0;
|
|
135
|
+
if (style.springboards !== false) {
|
|
136
|
+
const sb = DIVING.springboard;
|
|
137
|
+
let y = pref(DIVING.B.springboard3);
|
|
138
|
+
for (let i = 0; i < count; i += 1) {
|
|
139
|
+
boards.push({ kind: "springboard", height: 3, x: pref(DIVING.A.springboard3), y, length: sb.length, width: sb.width });
|
|
140
|
+
left = y;
|
|
141
|
+
y += i < count - 1 ? pref(DIVING.C.springboard3) : pref(DIVING.C.springboard3);
|
|
142
|
+
}
|
|
143
|
+
for (let i = 0; i < count; i += 1) {
|
|
144
|
+
boards.push({ kind: "springboard", height: 1, x: pref(DIVING.A.springboard1), y, length: sb.length, width: sb.width });
|
|
145
|
+
left = y;
|
|
146
|
+
y += pref(DIVING.C.springboard1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// The platforms from the tallest, the distance B from the right wall.
|
|
150
|
+
const tops = [];
|
|
151
|
+
let width = DIVING.well.width;
|
|
152
|
+
const place = (width0) => {
|
|
153
|
+
const out = [];
|
|
154
|
+
let y;
|
|
155
|
+
let above = 0;
|
|
156
|
+
for (const h of [...heights].reverse()) {
|
|
157
|
+
const spec = DIVING.platforms[String(h)];
|
|
158
|
+
const info = BOARDS[String(h)];
|
|
159
|
+
// From the plummet of the platform above, the distance C of that platform.
|
|
160
|
+
y = y === undefined ? width0 - pref(info.b) : y - above;
|
|
161
|
+
above = pref(info.c);
|
|
162
|
+
out.push({ kind: "platform", height: h, x: pref(info.a), y, length: spec.length, width: pref(spec.width) });
|
|
163
|
+
}
|
|
164
|
+
return out;
|
|
165
|
+
};
|
|
166
|
+
let platforms = place(width);
|
|
167
|
+
// Room between the last springboard and the first platform, so that their stands do not touch: widen the well if they would crowd.
|
|
168
|
+
const clear = 2;
|
|
169
|
+
if (platforms.length > 0 && boards.length > 0) {
|
|
170
|
+
const first = platforms[platforms.length - 1];
|
|
171
|
+
const need = left + DIVING.springboard.width / 2 + clear + first.width / 2 - first.y;
|
|
172
|
+
if (need > 0) {
|
|
173
|
+
width += need;
|
|
174
|
+
platforms = place(width);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
tops.push(...platforms);
|
|
178
|
+
const all = [...boards, ...tops];
|
|
179
|
+
const behind = Math.max(0, ...all.map((b) => b.length - b.x));
|
|
180
|
+
return { sport: "diving", ...commonOf(style), length: DIVING.well.length, width, boards: all, behind };
|
|
181
|
+
}
|
|
182
|
+
/** The artistic swimming area the options ask for: 30 by 20 m for the Olympic Games and the World Championships, or the least, 25 by 15 m with a 12 by 12 m deep part (Part Seven 12.2.2, 12.1.1). */
|
|
183
|
+
export function artisticPlan(o) {
|
|
184
|
+
const style = o.pool ?? {};
|
|
185
|
+
const area = style.area === "minimum" ? "minimum" : "olympic";
|
|
186
|
+
if (area === "olympic")
|
|
187
|
+
return { sport: "artisticswimming", ...commonOf(style), area, length: ARTISTIC.olympic.length, width: ARTISTIC.olympic.width, deep: undefined };
|
|
188
|
+
const m = ARTISTIC.minimum;
|
|
189
|
+
return { sport: "artisticswimming", ...commonOf(style), area, length: m.length, width: m.width, deep: { x: (m.length - m.deepLength) / 2, y: (m.width - m.deepWidth) / 2, length: m.deepLength, width: m.deepWidth } };
|
|
190
|
+
}
|
|
191
|
+
/** The pool the options ask for, of the sport's kind. */
|
|
192
|
+
export function poolPlan(o) {
|
|
193
|
+
switch (o.sport) {
|
|
194
|
+
case "swimming":
|
|
195
|
+
return swimPlan(o);
|
|
196
|
+
case "waterpolo":
|
|
197
|
+
return poloPlan(o);
|
|
198
|
+
case "diving":
|
|
199
|
+
return divePlan(o);
|
|
200
|
+
case "artisticswimming":
|
|
201
|
+
return artisticPlan(o);
|
|
202
|
+
default:
|
|
203
|
+
throw new Error(`"${String(o.sport)}" is not a pool sport`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** The hall of a natatorium asked for, with `natatorium` standing for the usual three pools. */
|
|
207
|
+
export function venueOf(style) {
|
|
208
|
+
if (style.venue === undefined)
|
|
209
|
+
return undefined;
|
|
210
|
+
if (style.venue === "natatorium") {
|
|
211
|
+
return {
|
|
212
|
+
pools: [
|
|
213
|
+
{ sport: "swimming", name: "Competition pool", course: "long" },
|
|
214
|
+
{ sport: "diving", name: "Diving well" },
|
|
215
|
+
{ sport: "swimming", name: "Warm-up pool", course: "short", kind: "warm-up" },
|
|
216
|
+
],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
if (typeof style.venue !== "object" || style.venue === null || !Array.isArray(style.venue.pools) || style.venue.pools.length === 0)
|
|
220
|
+
throw new TypeError(`venue is "natatorium" or { pools: [...] } with at least one pool`);
|
|
221
|
+
return style.venue;
|
|
222
|
+
}
|
|
223
|
+
/** What each plan reaches beyond its water, in metres on each side: the blocks and flag standards, the platforms and table, the stands behind the diving boards, the judges' platform. */
|
|
224
|
+
export function reachOf(plan) {
|
|
225
|
+
switch (plan.sport) {
|
|
226
|
+
case "swimming":
|
|
227
|
+
return { left: 1.3, right: 1.3, top: 1.2, bottom: 1.2 };
|
|
228
|
+
case "waterpolo":
|
|
229
|
+
return { left: 0.6, right: 0.6, top: 1.8, bottom: 4.2 };
|
|
230
|
+
case "diving":
|
|
231
|
+
return { left: plan.behind + 4, right: 0.5, top: 0.5, bottom: 0.5 };
|
|
232
|
+
default:
|
|
233
|
+
return { left: 0.5, right: 0.5, top: 2.8, bottom: 2.2 };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/** The most a row of pools is laid out to, in metres, when the pools have no places of their own. */
|
|
237
|
+
const ROW = 125;
|
|
238
|
+
/**
|
|
239
|
+
* The pools of a hall (`pool.venue`) laid out to scale. A pool with an `x` and a `y` stands there; the others are set in rows, left to right, each `apart` metres (10, the distance the rules prefer between
|
|
240
|
+
* a swimming pool and a diving well, Part Two 15.15.1; never less than the 8 they require) from the one before, and a new row when a row would be longer than 125 m.
|
|
241
|
+
* @throws RangeError when two pools are nearer each other than `apart`, for the pools given places.
|
|
242
|
+
*/
|
|
243
|
+
export function venueLayout(style) {
|
|
244
|
+
const venue = venueOf(style);
|
|
245
|
+
if (venue === undefined)
|
|
246
|
+
return undefined;
|
|
247
|
+
const apart = venue.apart === undefined ? SWIMMING.poolsApartPreferred : num(venue.apart, "The distance between pools", SWIMMING.poolsApart, 100);
|
|
248
|
+
const common = { look: style.look, deck: style.deck, seed: style.seed };
|
|
249
|
+
const items = [];
|
|
250
|
+
let x = 0;
|
|
251
|
+
let y = 0;
|
|
252
|
+
let rowHeight = 0;
|
|
253
|
+
for (const pool of venue.pools) {
|
|
254
|
+
if (typeof pool !== "object" || pool === null || !isPoolSport(pool.sport))
|
|
255
|
+
throw new TypeError(`A pool of a venue is { sport: ${POOL_SPORTS.map((s) => `"${s}"`).join(" | ")}, ... }`);
|
|
256
|
+
const pooled = { ...common, ...(pool.sport === "swimming" ? { course: pool.course, lanes: pool.lanes, kind: pool.kind } : {}) };
|
|
257
|
+
const plan = poolPlan({ sport: pool.sport, pool: pooled });
|
|
258
|
+
const placed = pool.x !== undefined && pool.y !== undefined;
|
|
259
|
+
if (!placed) {
|
|
260
|
+
if (items.length > 0 && x + plan.length > ROW) {
|
|
261
|
+
x = 0;
|
|
262
|
+
y += rowHeight + apart;
|
|
263
|
+
rowHeight = 0;
|
|
264
|
+
}
|
|
265
|
+
items.push({ pool, plan, x, y });
|
|
266
|
+
x += plan.length + apart;
|
|
267
|
+
rowHeight = Math.max(rowHeight, plan.width);
|
|
268
|
+
}
|
|
269
|
+
else
|
|
270
|
+
items.push({ pool, plan, x: num(pool.x, "A pool's x", -1000, 1000), y: num(pool.y, "A pool's y", -1000, 1000) });
|
|
271
|
+
}
|
|
272
|
+
// The box round all the water, moved to the origin.
|
|
273
|
+
const x0 = Math.min(...items.map((i) => i.x));
|
|
274
|
+
const y0 = Math.min(...items.map((i) => i.y));
|
|
275
|
+
for (const item of items) {
|
|
276
|
+
item.x -= x0;
|
|
277
|
+
item.y -= y0;
|
|
278
|
+
}
|
|
279
|
+
for (const [i, a] of items.entries()) {
|
|
280
|
+
for (const b of items.slice(i + 1)) {
|
|
281
|
+
const gapX = Math.max(a.x - (b.x + b.plan.length), b.x - (a.x + a.plan.length));
|
|
282
|
+
const gapY = Math.max(a.y - (b.y + b.plan.width), b.y - (a.y + a.plan.width));
|
|
283
|
+
if (Math.max(gapX, gapY) < apart - 1e-9)
|
|
284
|
+
throw new RangeError(`Two pools of the venue are nearer than ${apart} m: the rules keep a swimming pool and a diving well at least ${SWIMMING.poolsApart} m apart`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const width = Math.max(...items.map((i) => i.x + i.plan.length));
|
|
288
|
+
const height = Math.max(...items.map((i) => i.y + i.plan.width));
|
|
289
|
+
const reach = {
|
|
290
|
+
left: Math.max(...items.map((i) => reachOf(i.plan).left - i.x), 0),
|
|
291
|
+
right: Math.max(...items.map((i) => reachOf(i.plan).right + i.x + i.plan.length - width), 0),
|
|
292
|
+
top: Math.max(...items.map((i) => reachOf(i.plan).top - i.y), 0),
|
|
293
|
+
bottom: Math.max(...items.map((i) => reachOf(i.plan).bottom + i.y + i.plan.width - height), 0),
|
|
294
|
+
};
|
|
295
|
+
return { items, width, height, apart, labels: venue.labels !== false, seating: venue.seating !== false, reach };
|
|
296
|
+
}
|
|
297
|
+
/** The box round the water the options draw, in feet: the pool's, or the whole hall's. */
|
|
298
|
+
export function poolBox(o) {
|
|
299
|
+
const hall = o.sport === "swimming" && o.pool?.venue !== undefined ? venueLayout(o.pool) : undefined;
|
|
300
|
+
if (hall !== undefined)
|
|
301
|
+
return { w: feet(hall.width), h: feet(hall.height) };
|
|
302
|
+
const plan = poolPlan(o);
|
|
303
|
+
return { w: feet(plan.length), h: feet(plan.width) };
|
|
304
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { DeckLook, PoolLook } from "./pool.types.ts";
|
|
2
|
+
import { type BuildOptions } from "./surfaces/surface.ts";
|
|
3
|
+
/** A small generator of numbers from 0 up to 1 that starts from a seed (mulberry32): the same seed gives the same list. */
|
|
4
|
+
export declare function seeded(seed: number): () => number;
|
|
5
|
+
/** The side of the tile of light, in feet (about 5 m). */
|
|
6
|
+
export declare const CAUSTIC_TILE = 16;
|
|
7
|
+
/**
|
|
8
|
+
* The light on the water: a tile of closed, softly bent cells like the net of light at the bottom of a pool, from the seed. The cells at the tile's edge are drawn again on the far side, so that the
|
|
9
|
+
* tile repeats without a seam. Returns the path of all of them.
|
|
10
|
+
*/
|
|
11
|
+
export declare function causticPath(seed: number, size?: number): string;
|
|
12
|
+
/** The ids of what a pool defines for its paint. */
|
|
13
|
+
export interface PoolIds {
|
|
14
|
+
caustic: string;
|
|
15
|
+
floor: string;
|
|
16
|
+
deck: string;
|
|
17
|
+
water: string;
|
|
18
|
+
}
|
|
19
|
+
/** What a pool is drawn with when it is one of several in a hall: the ids of the paint the hall defines once, and no deck, no defs, no plates and no section of its own. */
|
|
20
|
+
export interface PoolCtx {
|
|
21
|
+
ids?: PoolIds;
|
|
22
|
+
bare?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** The ids a drawing's pools share: they follow the drawing's own prefix. */
|
|
25
|
+
export declare const poolIds: (id: string) => PoolIds;
|
|
26
|
+
/** The metres of a tile of the floor (drawn; real tiles are finer) and of the deck. */
|
|
27
|
+
export declare const FLOOR_TILE = 0.5;
|
|
28
|
+
export declare const DECK_TILE = 0.6;
|
|
29
|
+
/**
|
|
30
|
+
* The patterns and the gradient a pool's paint needs, for a look and a deck. Each is defined once, however many pools use it. A flat pool with a plain deck needs none.
|
|
31
|
+
*/
|
|
32
|
+
export declare function paintDefs(ids: PoolIds, look: PoolLook, deck: DeckLook, seed: number): string;
|
|
33
|
+
/** A rectangle in metres from its corners, as feet. */
|
|
34
|
+
export declare function box(o: Pick<BuildOptions, "features">, names: string | readonly string[], cls: string, x0: number, y0: number, x1: number, y1: number, more?: Record<string, string | number | undefined>): string;
|
|
35
|
+
/**
|
|
36
|
+
* The water of a pool, in metres from the corner `(x, y)`: the colour, then, for a look that has it, the gradient across the lanes, the floor's tiles and the light on the water. Tiles and light are
|
|
37
|
+
* laid at the pool's own place, so that two pools side by side show one floor.
|
|
38
|
+
*/
|
|
39
|
+
export declare function waterMarkup(o: Pick<BuildOptions, "features">, ids: PoolIds, look: PoolLook, x: number, y: number, length: number, width: number, clip?: string): string;
|
|
40
|
+
/** The deck as a rectangle in feet: the colour, then its tiles. */
|
|
41
|
+
export declare function deckMarkup(o: Pick<BuildOptions, "features">, ids: PoolIds, deck: DeckLook, x: number, y: number, w: number, h: number): string;
|
|
42
|
+
/** The widths a pool draws at: a rope is at least this wide on the page unless the true widths are asked for. */
|
|
43
|
+
export declare function leastWidth(o: Pick<BuildOptions, "exact">, trueMetres: number, drawnMetres: number): number;
|
|
44
|
+
/**
|
|
45
|
+
* A row of lane ropes as one path per colour: each rope's runs of floats as round dots at the pitch of the floats. `runs` are `[colour, x0, x1, y]` in metres. Round caps on a dash of no length make a dot,
|
|
46
|
+
* so a rope of 330 floats is one subpath. A float is `metres` across, drawn at least `drawn` metres across.
|
|
47
|
+
*/
|
|
48
|
+
export declare function ropeMarkup(o: Pick<BuildOptions, "features" | "exact">, runs: readonly (readonly [string, number, number, number])[], metres: number, drawn: number): string;
|
package/dist/pool.kit.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// What every pool is painted with: the light on the water (a few seeded curves in a tile, defined once and repeated by a pattern), the tiles of the floor and of the deck, the lane ropes as rows
|
|
2
|
+
// of floats, and the small helpers of a drawing made in feet from figures in metres. No random number: the curves come from a small generator that starts from the pool's `seed`, so the same options
|
|
3
|
+
// draw the same light. The rules say nothing of how water or a deck looks; these colours and patterns are ours.
|
|
4
|
+
import { el, num } from "./format.js";
|
|
5
|
+
import { feet } from "./pool.js";
|
|
6
|
+
import { ft } from "./surfaces/surface.js";
|
|
7
|
+
/** A small generator of numbers from 0 up to 1 that starts from a seed (mulberry32): the same seed gives the same list. */
|
|
8
|
+
export function seeded(seed) {
|
|
9
|
+
let a = (Math.trunc(seed) ^ 0x9e3779b9) >>> 0;
|
|
10
|
+
return () => {
|
|
11
|
+
a = (a + 0x6d2b79f5) >>> 0;
|
|
12
|
+
let t = a;
|
|
13
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
14
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
15
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** The side of the tile of light, in feet (about 5 m). */
|
|
19
|
+
export const CAUSTIC_TILE = 16;
|
|
20
|
+
/** A smooth closed curve through points (Catmull-Rom as cubic curves), as a path. */
|
|
21
|
+
function loop(points) {
|
|
22
|
+
const n = points.length;
|
|
23
|
+
let d = `M${num(points[0][0])} ${num(points[0][1])}`;
|
|
24
|
+
for (let i = 0; i < n; i += 1) {
|
|
25
|
+
const p0 = points[(i - 1 + n) % n];
|
|
26
|
+
const p1 = points[i];
|
|
27
|
+
const p2 = points[(i + 1) % n];
|
|
28
|
+
const p3 = points[(i + 2) % n];
|
|
29
|
+
const c1 = [p1[0] + (p2[0] - p0[0]) / 6, p1[1] + (p2[1] - p0[1]) / 6];
|
|
30
|
+
const c2 = [p2[0] - (p3[0] - p1[0]) / 6, p2[1] - (p3[1] - p1[1]) / 6];
|
|
31
|
+
d += `C${num(c1[0])} ${num(c1[1])} ${num(c2[0])} ${num(c2[1])} ${num(p2[0])} ${num(p2[1])}`;
|
|
32
|
+
}
|
|
33
|
+
return `${d}Z`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The light on the water: a tile of closed, softly bent cells like the net of light at the bottom of a pool, from the seed. The cells at the tile's edge are drawn again on the far side, so that the
|
|
37
|
+
* tile repeats without a seam. Returns the path of all of them.
|
|
38
|
+
*/
|
|
39
|
+
export function causticPath(seed, size = CAUSTIC_TILE) {
|
|
40
|
+
const rand = seeded(seed);
|
|
41
|
+
const cells = 4;
|
|
42
|
+
const step = size / cells;
|
|
43
|
+
const parts = [];
|
|
44
|
+
for (let i = 0; i < cells; i += 1) {
|
|
45
|
+
for (let j = 0; j < cells; j += 1) {
|
|
46
|
+
const cx = (i + 0.5 + (rand() - 0.5) * 0.45) * step;
|
|
47
|
+
const cy = (j + 0.5 + (rand() - 0.5) * 0.45) * step;
|
|
48
|
+
const sides = 6;
|
|
49
|
+
const turn = rand() * Math.PI;
|
|
50
|
+
const points = [];
|
|
51
|
+
for (let k = 0; k < sides; k += 1) {
|
|
52
|
+
const angle = turn + (k / sides) * 2 * Math.PI + (rand() - 0.5) * 0.5;
|
|
53
|
+
const r = step * (0.42 + rand() * 0.16);
|
|
54
|
+
points.push([cx + Math.cos(angle) * r, cy + Math.sin(angle) * r * 0.9]);
|
|
55
|
+
}
|
|
56
|
+
const reach = step * 0.7;
|
|
57
|
+
for (const dx of [-size, 0, size]) {
|
|
58
|
+
for (const dy of [-size, 0, size]) {
|
|
59
|
+
if (cx + dx + reach < 0 || cx + dx - reach > size || cy + dy + reach < 0 || cy + dy - reach > size)
|
|
60
|
+
continue;
|
|
61
|
+
parts.push(loop(points.map(([x, y]) => [x + dx, y + dy])));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return parts.join("");
|
|
67
|
+
}
|
|
68
|
+
/** The ids a drawing's pools share: they follow the drawing's own prefix. */
|
|
69
|
+
export const poolIds = (id) => ({ caustic: `${id}-lt`, floor: `${id}-ft`, deck: `${id}-dt`, water: `${id}-wg` });
|
|
70
|
+
/** The metres of a tile of the floor (drawn; real tiles are finer) and of the deck. */
|
|
71
|
+
export const FLOOR_TILE = 0.5;
|
|
72
|
+
export const DECK_TILE = 0.6;
|
|
73
|
+
/**
|
|
74
|
+
* The patterns and the gradient a pool's paint needs, for a look and a deck. Each is defined once, however many pools use it. A flat pool with a plain deck needs none.
|
|
75
|
+
*/
|
|
76
|
+
export function paintDefs(ids, look, deck, seed) {
|
|
77
|
+
let defs = "";
|
|
78
|
+
if (look !== "flat") {
|
|
79
|
+
defs += el("linearGradient", { id: ids.water, x1: 0, y1: 0, x2: 0, y2: 1 }, el("stop", { offset: 0, class: "ky-pl-sa" }) + el("stop", { offset: 0.5, class: "ky-pl-sb" }) + el("stop", { offset: 1, class: "ky-pl-sa" }));
|
|
80
|
+
defs += el("pattern", { id: ids.caustic, width: CAUSTIC_TILE, height: CAUSTIC_TILE, patternUnits: "userSpaceOnUse" }, el("path", { class: "ky-pl-lt", d: causticPath(seed) }));
|
|
81
|
+
}
|
|
82
|
+
if (look === "tiled") {
|
|
83
|
+
const t = feet(FLOOR_TILE);
|
|
84
|
+
defs += el("pattern", { id: ids.floor, width: t, height: t, patternUnits: "userSpaceOnUse" }, el("path", { class: "ky-pl-ft", d: `M0 0H${num(t)}M0 0V${num(t)}` }));
|
|
85
|
+
}
|
|
86
|
+
if (deck === "tiled") {
|
|
87
|
+
const t = feet(DECK_TILE);
|
|
88
|
+
defs += el("pattern", { id: ids.deck, width: t * 2, height: t * 2, patternUnits: "userSpaceOnUse" }, el("rect", { class: "ky-pl-da", x: 0, y: 0, width: t, height: t }) + el("rect", { class: "ky-pl-da", x: t, y: t, width: t, height: t }) + el("path", { class: "ky-pl-dt", d: `M0 0H${num(t * 2)}M0 ${num(t)}H${num(t * 2)}M0 0V${num(t * 2)}M${num(t)} 0V${num(t * 2)}` }));
|
|
89
|
+
}
|
|
90
|
+
return defs;
|
|
91
|
+
}
|
|
92
|
+
/** A rectangle in metres from its corners, as feet. */
|
|
93
|
+
export function box(o, names, cls, x0, y0, x1, y1, more = {}) {
|
|
94
|
+
return el("rect", { class: ft(o, names, cls), x: feet(x0), y: feet(y0), width: feet(x1 - x0), height: feet(y1 - y0), ...more });
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The water of a pool, in metres from the corner `(x, y)`: the colour, then, for a look that has it, the gradient across the lanes, the floor's tiles and the light on the water. Tiles and light are
|
|
98
|
+
* laid at the pool's own place, so that two pools side by side show one floor.
|
|
99
|
+
*/
|
|
100
|
+
export function waterMarkup(o, ids, look, x, y, length, width, clip) {
|
|
101
|
+
const at = { x: feet(x), y: feet(y), width: feet(length), height: feet(width), "clip-path": clip };
|
|
102
|
+
let out = el("rect", { class: ft(o, "water", "ky-surface"), x: at.x, y: at.y, width: at.width, height: at.height });
|
|
103
|
+
if (look !== "flat")
|
|
104
|
+
out += el("rect", { class: "ky-pl-wg", ...at, fill: `url(#${ids.water})` });
|
|
105
|
+
if (look === "tiled")
|
|
106
|
+
out += el("rect", { class: "ky-pl-tl", ...at, fill: `url(#${ids.floor})` });
|
|
107
|
+
if (look !== "flat")
|
|
108
|
+
out += el("rect", { class: "ky-pl-ls", ...at, fill: `url(#${ids.caustic})` });
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
/** The deck as a rectangle in feet: the colour, then its tiles. */
|
|
112
|
+
export function deckMarkup(o, ids, deck, x, y, w, h) {
|
|
113
|
+
let out = el("rect", { class: ft(o, "apron", "ky-apron"), x, y, width: w, height: h });
|
|
114
|
+
if (deck === "tiled")
|
|
115
|
+
out += el("rect", { class: "ky-pl-dk", x, y, width: w, height: h, fill: `url(#${ids.deck})` });
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
/** The widths a pool draws at: a rope is at least this wide on the page unless the true widths are asked for. */
|
|
119
|
+
export function leastWidth(o, trueMetres, drawnMetres) {
|
|
120
|
+
return feet(o.exact ? trueMetres : Math.max(trueMetres, drawnMetres));
|
|
121
|
+
}
|
|
122
|
+
/** The order ropes' colours are drawn in: the lane's own colour, then the red ends and the distinct marks over it. */
|
|
123
|
+
const ROPE_ORDER = ["green", "blue", "yellow", "red", "white"];
|
|
124
|
+
/**
|
|
125
|
+
* A row of lane ropes as one path per colour: each rope's runs of floats as round dots at the pitch of the floats. `runs` are `[colour, x0, x1, y]` in metres. Round caps on a dash of no length make a dot,
|
|
126
|
+
* so a rope of 330 floats is one subpath. A float is `metres` across, drawn at least `drawn` metres across.
|
|
127
|
+
*/
|
|
128
|
+
export function ropeMarkup(o, runs, metres, drawn) {
|
|
129
|
+
const by = new Map();
|
|
130
|
+
for (const [colour, x0, x1, y] of runs)
|
|
131
|
+
by.set(colour, [...(by.get(colour) ?? []), `M${num(feet(x0))} ${num(feet(y))}H${num(feet(x1))}`]);
|
|
132
|
+
const width = leastWidth(o, metres, drawn);
|
|
133
|
+
let out = "";
|
|
134
|
+
for (const colour of ROPE_ORDER) {
|
|
135
|
+
const d = by.get(colour);
|
|
136
|
+
if (d === undefined)
|
|
137
|
+
continue;
|
|
138
|
+
out += el("path", { class: ft(o, [`rope-${colour}`, "lane-ropes"], `ky-pl-r ky-pl-r-${colour}`), d: d.join(""), "stroke-width": width, "stroke-dasharray": `0 ${num(width * 0.86)}`, fill: "none" });
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type SwimPlan } from "./pool.ts";
|
|
2
|
+
import { type BuildOptions } from "./surfaces/surface.ts";
|
|
3
|
+
/** What the data of a race draws: the plates at each end, the title above, the pace bars on the water, and how far each reaches beyond the pool, in feet. */
|
|
4
|
+
export interface RaceArt {
|
|
5
|
+
/** The plates at the starting end (the names), at the turning end (the results), and the event and the distances above the pool. */
|
|
6
|
+
left: string;
|
|
7
|
+
right: string;
|
|
8
|
+
top: string;
|
|
9
|
+
/** The pace bars, over the water and under the ropes' labels. */
|
|
10
|
+
pace: string;
|
|
11
|
+
reachLeft: number;
|
|
12
|
+
reachRight: number;
|
|
13
|
+
reachTop: number;
|
|
14
|
+
}
|
|
15
|
+
/** The medal each of the first three places wins: gold, silver and bronze, in these colours whatever the look. */
|
|
16
|
+
export declare const MEDALS: Readonly<Record<number, string>>;
|
|
17
|
+
/** The colour of a pace between the field's slowest (0) and fastest (1). */
|
|
18
|
+
export declare function paceColour(t: number): string;
|
|
19
|
+
/** Everything the data of the race draws. */
|
|
20
|
+
export declare function raceArt(plan: SwimPlan, o: Pick<BuildOptions, "portrait" | "features" | "exact">): RaceArt;
|