@johnmorrisdotca/kyogijo-stadiums 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +75 -0
- package/NOTICE.md +4 -0
- package/README.md +71 -16
- package/dist/annotate.js +1 -1
- 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 +4 -2
- package/dist/atlas-entry.js +3 -2
- package/dist/atlas.d.ts +83 -7
- package/dist/atlas.js +108 -8
- 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.curling.heat.d.ts +24 -0
- package/dist/charts.curling.heat.js +116 -0
- package/dist/charts.curling.js +14 -3
- 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.types.d.ts +53 -1
- 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 +154 -0
- package/dist/crowd.draw.d.ts +17 -0
- package/dist/crowd.draw.js +198 -0
- package/dist/crowd.js +465 -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 +318 -35
- package/dist/element-entry.d.ts +1 -1
- package/dist/element-entry.js +1 -1
- package/dist/element.d.ts +13 -2
- package/dist/element.js +77 -3
- package/dist/features.js +131 -0
- package/dist/fields.kit.d.ts +2 -2
- package/dist/fields.types.d.ts +18 -4
- package/dist/frames.providers.js +63 -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/hardwood.d.ts +31 -0
- package/dist/hardwood.js +157 -0
- package/dist/heat.js +4 -3
- package/dist/heat.types.d.ts +5 -0
- package/dist/heat.zones.d.ts +2 -2
- package/dist/heat.zones.js +154 -6
- package/dist/homeside.js +17 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.js +10 -1
- package/dist/layers.d.ts +1 -1
- package/dist/layers.env.d.ts +3 -1
- package/dist/layers.env.js +4 -2
- package/dist/layers.js +4 -4
- package/dist/leagues/athletics.d.ts +63 -0
- package/dist/leagues/athletics.js +87 -0
- package/dist/leagues/courts.d.ts +269 -4
- package/dist/leagues/courts.js +155 -1
- package/dist/leagues/skating.d.ts +145 -0
- package/dist/leagues/skating.js +73 -0
- package/dist/leagues/sources.js +20 -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 +14 -4
- package/dist/leagues.js +146 -5
- package/dist/look.d.ts +3 -3
- package/dist/look.js +39 -6
- package/dist/outline.d.ts +1 -1
- package/dist/outline.js +30 -2
- 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 +207 -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/pool.d.ts +176 -0
- package/dist/pool.figures.d.ts +304 -0
- package/dist/pool.figures.js +144 -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 +257 -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 +47 -3
- package/dist/replay-entry.d.ts +3 -0
- package/dist/replay-entry.js +2 -0
- package/dist/restyle.js +1 -1
- package/dist/score.js +70 -5
- package/dist/sheet.d.ts +3 -0
- package/dist/sheet.js +7 -3
- 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 +296 -0
- package/dist/strings.js +328 -11
- package/dist/style.d.ts +2 -0
- package/dist/style.js +11 -1
- 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 +15 -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 +43 -21
- 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/hockey.js +1 -0
- 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 +44 -0
- package/dist/surfaces/venue.d.ts +15 -0
- package/dist/surfaces/venue.js +95 -0
- package/dist/tenants.d.ts +77 -0
- package/dist/tenants.data.d.ts +8 -0
- package/dist/tenants.data.js +945 -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 +105 -0
- package/dist/turn.camera.js +168 -0
- package/dist/turn.dom.d.ts +40 -0
- package/dist/turn.dom.js +351 -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 +55 -0
- package/dist/types.d.ts +183 -18
- package/dist/units.d.ts +1 -1
- package/dist/units.js +15 -1
- package/dist/uprights.board.d.ts +17 -0
- package/dist/uprights.board.js +59 -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 +160 -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/package.json +19 -2
package/dist/surfaces/field.js
CHANGED
|
@@ -4,7 +4,7 @@ import { mowTurf, oddCells } from "../mow.js";
|
|
|
4
4
|
import { ft, quarterTurn } from "./surface.js";
|
|
5
5
|
const RAD = Math.PI / 180;
|
|
6
6
|
/** The path of one shape that is drawn as a line, or of one filled bar. */
|
|
7
|
-
function pathOf(shape) {
|
|
7
|
+
export function pathOf(shape) {
|
|
8
8
|
switch (shape.kind) {
|
|
9
9
|
case "line":
|
|
10
10
|
return shape.y0 === shape.y1 ? `M${num(shape.x0)} ${num(shape.y0)}H${num(shape.x1)}` : shape.x0 === shape.x1 ? `M${num(shape.x0)} ${num(shape.y0)}V${num(shape.y1)}` : `M${num(shape.x0)} ${num(shape.y0)}L${num(shape.x1)} ${num(shape.y1)}`;
|
|
@@ -27,7 +27,7 @@ function pathOf(shape) {
|
|
|
27
27
|
return "";
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
const areaD = (a) => {
|
|
30
|
+
export const areaD = (a) => {
|
|
31
31
|
if (a.d !== undefined)
|
|
32
32
|
return a.d;
|
|
33
33
|
if (a.frame !== undefined) {
|
|
@@ -61,19 +61,21 @@ export function strokes(plan, o) {
|
|
|
61
61
|
return out;
|
|
62
62
|
}
|
|
63
63
|
/** The filled marks of a plan: the bars in one path to a feature, the dots one each. */
|
|
64
|
-
function marks(plan, o) {
|
|
64
|
+
export function marks(plan, o) {
|
|
65
65
|
const bars = new Map();
|
|
66
66
|
let dots = "";
|
|
67
67
|
const lift = o.exact ? 1 : 1.6;
|
|
68
68
|
for (const shape of plan.shapes) {
|
|
69
69
|
if (shape.kind === "bar")
|
|
70
|
-
bars.set(shape.feature
|
|
70
|
+
bars.set(`${shape.feature}|${shape.cls ?? ""}`, [...(bars.get(`${shape.feature}|${shape.cls ?? ""}`) ?? []), pathOf(shape)]);
|
|
71
71
|
else if (shape.kind === "dot")
|
|
72
|
-
dots += el("circle", { class: ft(o, shape.feature, shape.paint === "post" ? "ky-post-end" : "ky-fill-line"), cx: shape.cx, cy: shape.cy, r: shape.r * lift });
|
|
72
|
+
dots += el("circle", { class: ft(o, shape.feature, shape.paint === "post" ? "ky-post-end" : shape.paint === "block" ? "ky-ska-block" : shape.paint === "cone" ? "ky-ska-cone" : "ky-fill-line"), cx: shape.cx, cy: shape.cy, r: shape.r * lift });
|
|
73
73
|
}
|
|
74
74
|
let out = "";
|
|
75
|
-
for (const [
|
|
76
|
-
|
|
75
|
+
for (const [key, d] of bars) {
|
|
76
|
+
const [feature, cls] = key.split("|");
|
|
77
|
+
out += el("path", { class: ft(o, feature, cls === "" ? "ky-fill-line" : cls), d: d.join("") });
|
|
78
|
+
}
|
|
77
79
|
return out + dots;
|
|
78
80
|
}
|
|
79
81
|
/** One goal as the options ask for it: the net behind the goal line as a box, a line across the mouth, a solid strip, or nothing. */
|
|
@@ -118,8 +120,8 @@ export function fieldSurface(o, plan, style = {}) {
|
|
|
118
120
|
}
|
|
119
121
|
}
|
|
120
122
|
for (const area of plan.areas)
|
|
121
|
-
base += el("path", { class: ft(o, area.feature, area.cls), d: areaD(area), "fill-rule": area.frame === undefined ? undefined : "evenodd" });
|
|
122
|
-
const ends = o.score && plan.ends !== undefined ? plan.ends : undefined;
|
|
123
|
+
base += el("path", { class: ft(o, area.feature, area.cls), d: areaD(area), "fill-rule": area.frame === undefined && area.evenodd !== true ? undefined : "evenodd" });
|
|
124
|
+
const ends = o.score && o.pageant !== true && plan.ends !== undefined ? plan.ends : undefined;
|
|
123
125
|
if (ends !== undefined) {
|
|
124
126
|
for (const side of ["left", "right"]) {
|
|
125
127
|
const b = ends[side];
|
|
@@ -152,6 +154,10 @@ export function fieldSurface(o, plan, style = {}) {
|
|
|
152
154
|
surface.box = { x: 0, y: 0, w: L, h: W };
|
|
153
155
|
surface.lineRef = { width: thick, feet: 1 };
|
|
154
156
|
surface.ranges = plan.ranges;
|
|
157
|
+
// A pageant's pictures keep clear of a cricket pitch, which runs through the middle.
|
|
158
|
+
const pitch = plan.sport === "cricket" ? plan.ranges?.pitch : undefined;
|
|
159
|
+
if (pitch !== undefined && !("left" in pitch))
|
|
160
|
+
surface.pageant = { clear: pitch.w / 2 };
|
|
155
161
|
if (o.landmarks === true)
|
|
156
162
|
surface.marks = full ? [...plan.marks] : plan.marks.filter((mark) => !mark.id.startsWith("~"));
|
|
157
163
|
if (o.portrait) {
|
package/dist/surfaces/hockey.js
CHANGED
|
@@ -128,6 +128,7 @@ export function hockey(o, era, rink = NHL_RINK) {
|
|
|
128
128
|
};
|
|
129
129
|
surface.lineRef = { width: thin, feet: 1 };
|
|
130
130
|
surface.box = { x: 0, y: 0, w: L, h: W };
|
|
131
|
+
surface.centreRadius = rink.circleRadius;
|
|
131
132
|
// The zones of the ice: from the end boards to a blue line, and between the blue lines.
|
|
132
133
|
const zone = atEnds((end) => (end === "left" ? edges(0, 0, blueLine, W) : edges(L - blueLine, 0, L, W)));
|
|
133
134
|
surface.ranges = {
|
package/dist/surfaces/net.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type CourtPlan } from "../court.ts";
|
|
|
2
2
|
import { type BuildOptions, type Surface } from "./surface.ts";
|
|
3
3
|
/**
|
|
4
4
|
* A net sport's court lying wide: the ground, the free zone, the court, its painted areas, its lines, the net with its posts (and, in volleyball, its antennae)
|
|
5
|
-
* and, on a score, the place for the board. Every length that is a rulebook's figure comes from the league (leagues/courts.ts) through `courtGeometry`.
|
|
5
|
+
* and, on a score, the place for the board. Every length that is a rulebook's figure comes from the league (leagues/courts.ts) through `courtGeometry`. The courts of
|
|
6
|
+
* the sports added after the first five may also have walls, rings (circles and arcs), a table or a set, and no net.
|
|
6
7
|
*/
|
|
7
8
|
export declare function netCourt(o: BuildOptions, plan: CourtPlan): Surface;
|
package/dist/surfaces/net.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { courtGeometry, courtSize, sideReach } from "../court.js";
|
|
1
|
+
import { bandReach, courtGeometry, courtSize, sideReach } from "../court.js";
|
|
2
2
|
import { el, num } from "../format.js";
|
|
3
3
|
import { atEnds, edges, ft, quarterTurn } from "./surface.js";
|
|
4
4
|
/** A rectangle as a closed sub-path. */
|
|
@@ -8,9 +8,35 @@ function mark(f, cls) {
|
|
|
8
8
|
const half = f.size / 2;
|
|
9
9
|
return f.shape === "round" ? el("circle", { class: cls, cx: f.x, cy: f.y, r: half }) : el("rect", { class: cls, x: f.x - half, y: f.y - half, width: f.size, height: f.size });
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* A ring or an arc as a stroked line: a circle element for a whole circle, else a path from `a0` to `a1` in pieces of at most a quarter turn (an arc of exactly half a circle,
|
|
13
|
+
* drawn in one piece with rounded numbers, can lose its centre). The line's outer edge is at the radius, so its middle is half a width inside it.
|
|
14
|
+
*/
|
|
15
|
+
function ringMarkup(o, ring) {
|
|
16
|
+
const { cx, cy, w } = ring;
|
|
17
|
+
const r = Math.max(0.001, ring.r - w / 2);
|
|
18
|
+
const cls = ft(o, "lines", "ky-line");
|
|
19
|
+
if (ring.a0 === undefined || ring.a1 === undefined)
|
|
20
|
+
return el("circle", { class: cls, cx, cy, r, "stroke-width": w });
|
|
21
|
+
const turns = Math.max(1, Math.ceil((ring.a1 - ring.a0) / (Math.PI / 2) - 1e-9));
|
|
22
|
+
const at = (a) => `${num(cx + r * Math.cos(a))} ${num(cy + r * Math.sin(a))}`;
|
|
23
|
+
let d = `M${at(ring.a0)}`;
|
|
24
|
+
for (let i = 1; i <= turns; i += 1)
|
|
25
|
+
d += `A${num(r)} ${num(r)} 0 0 1 ${at(ring.a0 + ((ring.a1 - ring.a0) * i) / turns)}`;
|
|
26
|
+
return el("path", { class: cls, d, "stroke-width": w });
|
|
27
|
+
}
|
|
28
|
+
/** What each kind of band is drawn with: the class and the feature it belongs to. */
|
|
29
|
+
const BAND = { wall: ["walls", "ky-wall"], mesh: ["fence", "ky-mesh"], face: ["wall-face", "ky-face"], tin: ["tin", "ky-tin"] };
|
|
30
|
+
/** A solid on the court: a table is a rectangle, a roundnet set a rim round a net. */
|
|
31
|
+
function solid(o, s) {
|
|
32
|
+
if (s.kind === "table")
|
|
33
|
+
return el("path", { class: ft(o, "table", "ky-table"), d: box(s.rect) });
|
|
34
|
+
return el("circle", { class: ft(o, "net", "ky-nt-post"), cx: s.cx, cy: s.cy, r: s.r }) + el("circle", { class: ft(o, "net", "ky-set-net"), cx: s.cx, cy: s.cy, r: s.r * 0.84 });
|
|
35
|
+
}
|
|
11
36
|
/**
|
|
12
37
|
* A net sport's court lying wide: the ground, the free zone, the court, its painted areas, its lines, the net with its posts (and, in volleyball, its antennae)
|
|
13
|
-
* and, on a score, the place for the board. Every length that is a rulebook's figure comes from the league (leagues/courts.ts) through `courtGeometry`.
|
|
38
|
+
* and, on a score, the place for the board. Every length that is a rulebook's figure comes from the league (leagues/courts.ts) through `courtGeometry`. The courts of
|
|
39
|
+
* the sports added after the first five may also have walls, rings (circles and arcs), a table or a set, and no net.
|
|
14
40
|
*/
|
|
15
41
|
export function netCourt(o, plan) {
|
|
16
42
|
const geometry = courtGeometry(plan);
|
|
@@ -23,7 +49,10 @@ export function netCourt(o, plan) {
|
|
|
23
49
|
const side = Math.max(zone?.side ?? 0, sideReach(geometry));
|
|
24
50
|
const end = zone?.end ?? 0;
|
|
25
51
|
const m = o.margin ?? L * 0.035;
|
|
26
|
-
|
|
52
|
+
// Walls and the like stand beyond the court's edges, and may make the ground wider on one side than on the other.
|
|
53
|
+
const beyond = bandReach(geometry);
|
|
54
|
+
const [left, right, top, bottom] = [Math.max(end, beyond.left), Math.max(end, beyond.right), Math.max(side, beyond.top), Math.max(side, beyond.bottom)];
|
|
55
|
+
const view0 = { x: -left - m, y: -top - m, w: L + left + right + 2 * m, h: W + top + bottom + 2 * m };
|
|
27
56
|
// Lines are drawn a little thicker than painted, as the other surfaces' are, unless asked for the true widths: the thin dimension of a line is raised to a least.
|
|
28
57
|
const least = o.exact ? 0 : L * 0.0026;
|
|
29
58
|
const thick = (r) => {
|
|
@@ -39,12 +68,31 @@ export function netCourt(o, plan) {
|
|
|
39
68
|
base += el("rect", { class: ft(o, "court", "ky-surface"), x: 0, y: 0, width: L, height: W });
|
|
40
69
|
if (geometry.areas.length > 0)
|
|
41
70
|
base += el("path", { class: ft(o, "areas", "ky-area"), d: geometry.areas.map(box).join("") });
|
|
71
|
+
for (const band of geometry.bands) {
|
|
72
|
+
const [feature, cls] = BAND[band.kind];
|
|
73
|
+
base += el("path", { class: ft(o, feature, cls), d: box(band.rect) });
|
|
74
|
+
}
|
|
42
75
|
const lineRects = [...geometry.lines, ...(full ? geometry.small : [])].map(thick);
|
|
43
76
|
let lines = el("path", { class: ft(o, "lines", "ky-fill-line"), d: lineRects.map(box).join("") });
|
|
44
|
-
//
|
|
45
|
-
|
|
77
|
+
// Circles and arcs are lines too, drawn at least as thick as the others.
|
|
78
|
+
for (const ring of geometry.rings)
|
|
79
|
+
lines += ringMarkup(o, { ...ring, w: Math.max(ring.w, least) });
|
|
80
|
+
if (geometry.wallLines.length > 0)
|
|
81
|
+
lines += el("path", { class: ft(o, "wall-lines", "ky-wall-line"), d: geometry.wallLines.map(thick).map(box).join("") });
|
|
82
|
+
for (const s of geometry.solids)
|
|
83
|
+
lines += solid(o, s);
|
|
84
|
+
// The net: a line across the court at its middle, from post to post; its posts and, in volleyball, the antennae at the side lines. A court without a net has none.
|
|
46
85
|
const net = geometry.net;
|
|
47
|
-
|
|
86
|
+
// Over a centre line (volleyball's, sepak takraw's) the net is drawn a little wider than the line, so that the line shows between its edges.
|
|
87
|
+
const under = geometry.underNet;
|
|
88
|
+
const nw = under === null ? Math.max(L * 0.004, 0.02) : Math.max(under, least) * 2.4;
|
|
89
|
+
if (net !== null)
|
|
90
|
+
lines += el("rect", { class: ft(o, "net", "ky-nt-line"), x: net.x - nw / 2, y: net.y0, width: nw, height: net.y1 - net.y0 });
|
|
91
|
+
// The centre line runs under the net (volleyball Rule 1.3.3): the net is drawn over it, so the line is drawn again over the net, in a narrower stroke, to show through.
|
|
92
|
+
if (net !== null && under !== null) {
|
|
93
|
+
const core = Math.max(under, least);
|
|
94
|
+
lines += el("rect", { class: ft(o, "lines", "ky-fill-line"), x: net.x - core / 2, y: 0, width: core, height: W });
|
|
95
|
+
}
|
|
48
96
|
for (const post of geometry.posts)
|
|
49
97
|
lines += mark(post, ft(o, "posts", "ky-nt-post"));
|
|
50
98
|
if (full) {
|
|
@@ -54,22 +102,30 @@ export function netCourt(o, plan) {
|
|
|
54
102
|
lines += mark(antenna, ft(o, "antennae", "ky-red-fill"));
|
|
55
103
|
}
|
|
56
104
|
// The teams' names are not painted on these courts: the board names the teams. The board lies over the net, between the lines it needs.
|
|
57
|
-
|
|
58
|
-
const
|
|
105
|
+
// A roundnet court is square, with no sides: it lies the same wherever it is turned.
|
|
106
|
+
const turned = o.portrait && plan.sport !== "roundnet";
|
|
107
|
+
// The board lies over the net, between the lines it needs; on a roundnet court that is the set, so it lies on the ground below the circles.
|
|
108
|
+
const room = plan.sport === "roundnet" ? W / 2 - plan.figures.servingRadius : 0;
|
|
109
|
+
const clear = plan.sport === "roundnet" ? { cx, cy: cy + plan.figures.servingRadius + room / 2, w: L * 0.9, h: room * 0.9 } : undefined;
|
|
110
|
+
// Netball's board has no net to lie over: it lies in the centre third, and the teams' names are painted in the goal thirds.
|
|
111
|
+
const centred = plan.sport === "netball" ? (turned ? { cx, cy, w: W * 0.88, h: L * 0.36 } : { cx, cy, w: L * 0.4, h: W * 0.66 }) : undefined;
|
|
112
|
+
const spot = clear ?? centred ?? (turned ? { cx, cy, w: W * 0.88, h: L * 0.5 } : { cx, cy, w: L * 0.58, h: W * 0.66 });
|
|
113
|
+
const places = plan.sport === "netball" ? { left: { cx: L * 0.15, cy, w: L * 0.2, h: W * 0.4, rot: 0 }, right: { cx: L * 0.85, cy, w: L * 0.2, h: W * 0.4, rot: 0 } } : { left: { cx: L * 0.2, cy, w: L * 0.2, h: W * 0.4, rot: 0 }, right: { cx: L * 0.8, cy, w: L * 0.2, h: W * 0.4, rot: 0 } };
|
|
59
114
|
const surface = {
|
|
60
115
|
sport: plan.sport,
|
|
61
116
|
view: view0,
|
|
62
117
|
base,
|
|
63
118
|
lines,
|
|
64
119
|
defs: "",
|
|
65
|
-
left:
|
|
66
|
-
right:
|
|
120
|
+
left: places.left,
|
|
121
|
+
right: places.right,
|
|
67
122
|
plate: spot,
|
|
68
123
|
turned,
|
|
69
124
|
};
|
|
70
125
|
surface.box = { x: 0, y: 0, w: L, h: W };
|
|
71
126
|
// One side of the net.
|
|
72
|
-
|
|
127
|
+
const middle = net === null ? cx : net.x;
|
|
128
|
+
surface.ranges = { "half-court": atEnds((end) => (end === "left" ? edges(0, 0, middle, W) : edges(middle, 0, L, W))) };
|
|
73
129
|
surface.lineRef = { width: plan.sport === "tabletennis" ? plan.figures.edgeLine : plan.figures.lineWidth, feet: 1 };
|
|
74
130
|
if (o.landmarks === true)
|
|
75
131
|
surface.marks = netMarks(plan, L, W);
|
|
@@ -116,11 +172,12 @@ function netMarks(plan, L, W) {
|
|
|
116
172
|
case "badminton": {
|
|
117
173
|
const f = plan.figures;
|
|
118
174
|
return [
|
|
119
|
-
|
|
175
|
+
// The back boundary line is the singles long service line too (Diagram A); the line inside it is the doubles long service line.
|
|
176
|
+
{ id: "back-boundary-line", x: f.lineWidth / 2, y: W * 0.8, side: "bottom" },
|
|
120
177
|
{ id: "sideline", x: L * 0.12, y: 0, side: "top" },
|
|
121
178
|
{ id: "singles-sideline", x: L * 0.12, y: W - f.singlesInset, side: "bottom" },
|
|
122
|
-
{ id: "long-service-line", x: f.doublesLongServiceInset + f.lineWidth / 2, y: W * 0.2, side: "top" },
|
|
123
|
-
{ id: "short-service-line", x: cx - f.shortServiceLine - f.lineWidth / 2, y: W * 0.3, side: "
|
|
179
|
+
{ id: "doubles-long-service-line", x: f.doublesLongServiceInset + f.lineWidth / 2, y: W * 0.2, side: "top" },
|
|
180
|
+
{ id: "short-service-line", x: cx - f.shortServiceLine - f.lineWidth / 2, y: W * 0.3, side: "bottom" },
|
|
124
181
|
{ id: "centre-line", x: L * 0.2, y: cy, side: "bottom" },
|
|
125
182
|
net,
|
|
126
183
|
];
|
|
@@ -136,7 +193,7 @@ function netMarks(plan, L, W) {
|
|
|
136
193
|
net,
|
|
137
194
|
];
|
|
138
195
|
}
|
|
139
|
-
|
|
196
|
+
case "tabletennis": {
|
|
140
197
|
const f = plan.figures;
|
|
141
198
|
return [
|
|
142
199
|
{ id: "end-line", x: f.edgeLine / 2, y: W * 0.8, side: "bottom" },
|
|
@@ -147,5 +204,93 @@ function netMarks(plan, L, W) {
|
|
|
147
204
|
net,
|
|
148
205
|
];
|
|
149
206
|
}
|
|
207
|
+
case "padel": {
|
|
208
|
+
const f = plan.figures;
|
|
209
|
+
return [
|
|
210
|
+
{ id: "back-wall", x: 0, y: W * 0.8, side: "bottom" },
|
|
211
|
+
{ id: "side-wall", x: f.enclosure.sideWall / 2, y: 0, side: "top" },
|
|
212
|
+
{ id: "metal-fence", x: L * 0.3, y: W, side: "bottom" },
|
|
213
|
+
{ id: "service-line", x: cx - f.serviceLine + f.lineWidth / 2, y: W * 0.25, side: "top" },
|
|
214
|
+
{ id: "centre-service-line", x: cx - f.serviceLine / 2, y: cy, side: "bottom" },
|
|
215
|
+
net,
|
|
216
|
+
];
|
|
217
|
+
}
|
|
218
|
+
case "squash": {
|
|
219
|
+
const f = plan.figures;
|
|
220
|
+
const near = L - f.shortLine;
|
|
221
|
+
return [
|
|
222
|
+
{ id: "tin", x: -f.wall.tin / 2, y: W * 0.15, side: "top" },
|
|
223
|
+
{ id: "wall-service-line", x: -f.wall.serviceLine - f.lineWidth / 2, y: W * 0.45, side: "bottom" },
|
|
224
|
+
{ id: "front-wall-line", x: -f.wall.frontWallLine - f.lineWidth / 2, y: W * 0.15, side: "top" },
|
|
225
|
+
{ id: "short-line", x: near - f.lineWidth / 2, y: W * 0.3, side: "top" },
|
|
226
|
+
{ id: "squash-half-court-line", x: L - f.shortLine / 2, y: cy, side: "bottom" },
|
|
227
|
+
{ id: "service-box", x: near + f.serviceBox / 2, y: f.serviceBox / 2, side: "top" },
|
|
228
|
+
{ id: "back-wall", x: L, y: W * 0.7, side: "bottom" },
|
|
229
|
+
];
|
|
230
|
+
}
|
|
231
|
+
case "racquetball": {
|
|
232
|
+
const f = plan.figures;
|
|
233
|
+
const back = L - f.shortLine;
|
|
234
|
+
const front = back - f.serviceLine;
|
|
235
|
+
return [
|
|
236
|
+
{ id: "front-wall", x: 0, y: W * 0.3, side: "top" },
|
|
237
|
+
{ id: "service-line", x: front + f.lineWidth / 2, y: W * 0.7, side: "bottom" },
|
|
238
|
+
{ id: "service-zone", x: (front + back) / 2, y: cy, side: "top" },
|
|
239
|
+
{ id: "service-box", x: (front + back) / 2, y: W - f.serviceBox / 2, side: "bottom" },
|
|
240
|
+
{ id: "drive-serve-line", x: (front + back) / 2, y: f.driveServe - f.lineWidth / 2, side: "top" },
|
|
241
|
+
{ id: "short-line", x: back - f.lineWidth / 2, y: W * 0.15, side: "top" },
|
|
242
|
+
{ id: "safety-zone", x: back + f.receiving.behind / 2, y: cy, side: "bottom" },
|
|
243
|
+
{ id: "receiving-line", x: back + f.receiving.behind - f.lineWidth / 2, y: W * 0.8, side: "bottom" },
|
|
244
|
+
{ id: "back-wall", x: L, y: cy, side: "bottom" },
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
case "sepaktakraw": {
|
|
248
|
+
const f = plan.figures;
|
|
249
|
+
const k = Math.SQRT1_2 * f.quarterCircle;
|
|
250
|
+
return [
|
|
251
|
+
{ id: "back-line", x: f.lineWidth / 2, y: W * 0.8, side: "bottom" },
|
|
252
|
+
{ id: "sideline", x: L * 0.12, y: 0, side: "top" },
|
|
253
|
+
{ id: "centre-line", x: cx + f.centreLine, y: W * 0.8, side: "bottom" },
|
|
254
|
+
{ id: "quarter-circle", x: cx - k, y: k, side: "top" },
|
|
255
|
+
{ id: "service-circle", x: f.serviceCircle.fromBack, y: cy - f.serviceCircle.radius, side: "top" },
|
|
256
|
+
net,
|
|
257
|
+
];
|
|
258
|
+
}
|
|
259
|
+
case "teqball": {
|
|
260
|
+
const f = plan.figures;
|
|
261
|
+
return [
|
|
262
|
+
{ id: "court-surround", x: 0, y: W * 0.8, side: "bottom" },
|
|
263
|
+
{ id: "halfway-line", x: cx, y: W * 0.15, side: "top" },
|
|
264
|
+
{ id: "service-line", x: cx - f.serviceLine, y: W * 0.7, side: "bottom" },
|
|
265
|
+
{ id: "service-line", x: cx + f.serviceLine, y: W * 0.3, side: "top" },
|
|
266
|
+
{ id: "table", x: cx + f.table.length * 0.3, y: cy + f.table.width * 0.3, side: "bottom" },
|
|
267
|
+
{ id: "court-net", x: cx, y: cy - f.net.width / 2, side: "top" },
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
case "roundnet": {
|
|
271
|
+
const f = plan.figures;
|
|
272
|
+
const k = Math.SQRT1_2;
|
|
273
|
+
return [
|
|
274
|
+
{ id: "roundnet-set", x: cx - f.set.diameter * 0.25, y: cy + f.set.diameter * 0.1, side: "bottom" },
|
|
275
|
+
{ id: "no-hit-zone", x: cx + f.noHitRadius * k, y: cy - f.noHitRadius * k, side: "top" },
|
|
276
|
+
{ id: "service-circle", x: cx - f.servingRadius * k, y: cy - f.servingRadius * k, side: "top" },
|
|
277
|
+
{ id: "service-position", x: cx + f.servingRadius, y: cy, side: "bottom" },
|
|
278
|
+
{ id: "service-position", x: cx - f.servingRadius, y: cy, side: "bottom" },
|
|
279
|
+
{ id: "service-position", x: cx, y: cy - f.servingRadius, side: "top" },
|
|
280
|
+
];
|
|
281
|
+
}
|
|
282
|
+
default: {
|
|
283
|
+
const f = plan.figures;
|
|
284
|
+
const k = Math.SQRT1_2 * f.goalCircle;
|
|
285
|
+
return [
|
|
286
|
+
{ id: "goal-line", x: f.lineWidth / 2, y: W * 0.8, side: "bottom" },
|
|
287
|
+
{ id: "sideline", x: L * 0.12, y: 0, side: "top" },
|
|
288
|
+
{ id: "transverse-line", x: L / 3, y: 0, side: "top" },
|
|
289
|
+
{ id: "centre-circle", x: cx + f.centreCircle / 2, y: cy, side: "bottom" },
|
|
290
|
+
{ id: "goal-circle", x: k, y: cy - k, side: "top" },
|
|
291
|
+
{ id: "goalpost", x: f.post.poleDiameter / 2, y: cy, side: "bottom" },
|
|
292
|
+
{ id: "goal-ring", x: L - f.post.poleDiameter - f.post.ring.bar - f.post.ring.inside / 2, y: cy, side: "top" },
|
|
293
|
+
];
|
|
294
|
+
}
|
|
150
295
|
}
|
|
151
296
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PoloPlan } from "../pool.ts";
|
|
2
|
+
import { type PoolCtx } from "../pool.kit.ts";
|
|
3
|
+
import { type BuildOptions, type Surface } from "./surface.ts";
|
|
4
|
+
/** The space behind each goal line where the goal's net hangs, in metres (CR 18.3.5): the pool is the field of play, 25 m between the goal lines (CR 18.1.2), and this behind each of them. */
|
|
5
|
+
export declare const POLO_BEHIND: 0.3;
|
|
6
|
+
/**
|
|
7
|
+
* A water polo field of play lying wide, from World Aquatics' rules: 25 by 20 m between the goal lines; lane ropes along both sides whose floats are white at the goal line and the half distance line, red to
|
|
8
|
+
* the 2 m mark, yellow from 2 to 6 m and green from 6 m to the half distance line, with a red mark at 5 m (CR 18.3.1 and Appendix 4, 20.4.1.5); the 2 m lines from the goal posts that make the goal areas
|
|
9
|
+
* (20.4.1.2); the goals, 3 m between the posts (18.3.3); the re-entry area 2.0 by 1.08 m in each corner on the side away from the official table (18.3.4); the referees' platforms 1 m wide along both sides, coloured
|
|
10
|
+
* as the ropes are (18.3.2); the official table and the area for flying substitutions on the side of the benches (18.1.8).
|
|
11
|
+
*/
|
|
12
|
+
export declare function waterPolo(o: BuildOptions, plan: PoloPlan, ctx?: PoolCtx): Surface;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { el, num } from "../format.js";
|
|
2
|
+
import { feet } from "../pool.js";
|
|
3
|
+
import { box, deckMarkup, leastWidth, paintDefs, poolIds, ropeMarkup, waterMarkup } from "../pool.kit.js";
|
|
4
|
+
import { WATER_POLO } from "../pool.figures.js";
|
|
5
|
+
import { GUTTER } from "./pool.js";
|
|
6
|
+
import { atEnds, edges, ft, quarterTurn } from "./surface.js";
|
|
7
|
+
/** The space behind each goal line where the goal's net hangs, in metres (CR 18.3.5): the pool is the field of play, 25 m between the goal lines (CR 18.1.2), and this behind each of them. */
|
|
8
|
+
export const POLO_BEHIND = WATER_POLO.goal.netClear;
|
|
9
|
+
/**
|
|
10
|
+
* A water polo field of play lying wide, from World Aquatics' rules: 25 by 20 m between the goal lines; lane ropes along both sides whose floats are white at the goal line and the half distance line, red to
|
|
11
|
+
* the 2 m mark, yellow from 2 to 6 m and green from 6 m to the half distance line, with a red mark at 5 m (CR 18.3.1 and Appendix 4, 20.4.1.5); the 2 m lines from the goal posts that make the goal areas
|
|
12
|
+
* (20.4.1.2); the goals, 3 m between the posts (18.3.3); the re-entry area 2.0 by 1.08 m in each corner on the side away from the official table (18.3.4); the referees' platforms 1 m wide along both sides, coloured
|
|
13
|
+
* as the ropes are (18.3.2); the official table and the area for flying substitutions on the side of the benches (18.1.8).
|
|
14
|
+
*/
|
|
15
|
+
export function waterPolo(o, plan, ctx = {}) {
|
|
16
|
+
const Lp = plan.length;
|
|
17
|
+
const W = plan.width;
|
|
18
|
+
const b = POLO_BEHIND;
|
|
19
|
+
const ids = ctx.ids ?? poolIds(o.id);
|
|
20
|
+
const full = o.detail === "full";
|
|
21
|
+
const m = ctx.bare === true ? 0 : (o.margin ?? feet(WATER_POLO.deck));
|
|
22
|
+
const platform = WATER_POLO.platform.width;
|
|
23
|
+
const down = plan.table === "bottom";
|
|
24
|
+
// The goal lines stand 0.30 m in from the walls; the marks are measured from them.
|
|
25
|
+
const g0 = b;
|
|
26
|
+
const g1 = Lp - b;
|
|
27
|
+
const at = (d, from) => (from === "left" ? g0 + d : g1 - d);
|
|
28
|
+
const deckTop = GUTTER + platform;
|
|
29
|
+
const deckBottom = GUTTER + platform + (down ? WATER_POLO.table.depth + 0.6 : 0);
|
|
30
|
+
let base = box(o, "gutter", "ky-boards", -GUTTER, -GUTTER, Lp + GUTTER, W + GUTTER);
|
|
31
|
+
base += waterMarkup(o, ids, plan.look, 0, 0, Lp, W);
|
|
32
|
+
// The referees' platforms along both sides, in the colours of the ropes by distance from the goal line.
|
|
33
|
+
const bands = [
|
|
34
|
+
["red", 0, WATER_POLO.twoMetre],
|
|
35
|
+
["yellow", WATER_POLO.twoMetre, WATER_POLO.sixMetre],
|
|
36
|
+
["green", WATER_POLO.sixMetre, WATER_POLO.half],
|
|
37
|
+
];
|
|
38
|
+
let walk = "";
|
|
39
|
+
for (const [y0, y1] of [[-GUTTER - platform, -GUTTER], [W + GUTTER, W + GUTTER + platform]]) {
|
|
40
|
+
for (const [colour, a, z] of bands) {
|
|
41
|
+
for (const from of ["left", "right"]) {
|
|
42
|
+
const x0 = from === "left" ? at(a, "left") : at(z, "right");
|
|
43
|
+
const x1 = from === "left" ? at(z, "left") : at(a, "right");
|
|
44
|
+
walk += el("rect", { class: ft(o, "referee-platforms", `ky-pl-rp ky-pl-rp-${colour}`), x: feet(x0), y: feet(y0), width: feet(x1 - x0), height: feet(y1 - y0) });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
base += walk;
|
|
49
|
+
let lines = "";
|
|
50
|
+
const thin = leastWidth(o, 0.06, 0.12);
|
|
51
|
+
// The lines across the field: the goal lines and the half distance line white, the 2 m line red, the 5 m line a dashed red one (a red mark at 5 m), the 6 m line yellow.
|
|
52
|
+
const across = (cls, colour, x, dash, w = thin) => el("path", { class: ft(o, cls, `ky-pl-wl ky-pl-wl-${colour}`), d: `M${num(feet(x))} 0V${num(feet(W))}`, "stroke-width": w, "stroke-dasharray": dash, fill: "none" });
|
|
53
|
+
lines += across("goal-lines", "white", g0, undefined, thin * 1.4) + across("goal-lines", "white", g1, undefined, thin * 1.4) + across("half-distance-line", "white", Lp / 2, undefined, thin * 1.4);
|
|
54
|
+
for (const from of ["left", "right"]) {
|
|
55
|
+
lines += across("two-metre-lines", "red", at(WATER_POLO.twoMetre, from));
|
|
56
|
+
lines += across("six-metre-lines", "yellow", at(WATER_POLO.sixMetre, from));
|
|
57
|
+
lines += across("five-metre-marks", "red", at(WATER_POLO.fiveMetre, from), `${num(feet(0.5))} ${num(feet(0.5))}`);
|
|
58
|
+
}
|
|
59
|
+
// The goal areas: red lines 2 m long from the outer faces of the goal posts (Appendix 4, 20.4.1.2).
|
|
60
|
+
const post = WATER_POLO.goal.post;
|
|
61
|
+
const half = WATER_POLO.goal.width / 2;
|
|
62
|
+
for (const from of ["left", "right"]) {
|
|
63
|
+
const x0 = at(0, from);
|
|
64
|
+
const x1 = at(WATER_POLO.goalAreaLine, from);
|
|
65
|
+
for (const side of [-1, 1]) {
|
|
66
|
+
const y = W / 2 + side * (half + post);
|
|
67
|
+
lines += el("path", { class: ft(o, "goal-area-lines", "ky-pl-wl ky-pl-wl-red"), d: `M${num(feet(x0))} ${num(feet(y))}H${num(feet(x1))}`, "stroke-width": thin, fill: "none" });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// The re-entry areas, 2.0 m along the goal line and 1.08 m into the field, in each corner on the side away from the table.
|
|
71
|
+
const away = down ? 0 : W;
|
|
72
|
+
const dirY = down ? 1 : -1;
|
|
73
|
+
for (const from of ["left", "right"]) {
|
|
74
|
+
const x0 = at(0, from);
|
|
75
|
+
const x1 = at(WATER_POLO.reEntry.depth, from);
|
|
76
|
+
const xa = Math.min(x0, x1);
|
|
77
|
+
const ya = Math.min(away, away + dirY * WATER_POLO.reEntry.length);
|
|
78
|
+
lines += el("rect", { class: ft(o, "re-entry-areas", "ky-pl-ra"), x: feet(xa), y: feet(ya), width: feet(Math.abs(x1 - x0)), height: feet(WATER_POLO.reEntry.length) });
|
|
79
|
+
}
|
|
80
|
+
// The lane ropes along the sides: white to the goal line, then red, yellow and green, with the red mark at 5 m, and white at the half distance line.
|
|
81
|
+
const runs = [];
|
|
82
|
+
const inset = 0.1;
|
|
83
|
+
for (const y of [inset, W - inset]) {
|
|
84
|
+
runs.push(["white", 0, g0, y], ["white", g1, Lp, y]);
|
|
85
|
+
for (const [colour, a, z] of bands) {
|
|
86
|
+
runs.push([colour, at(a, "left"), at(z, "left"), y]);
|
|
87
|
+
runs.push([colour, at(z, "right"), at(a, "right"), y]);
|
|
88
|
+
}
|
|
89
|
+
runs.push(["white", Lp / 2 - 0.45, Lp / 2 + 0.45, y]);
|
|
90
|
+
for (const from of ["left", "right"]) {
|
|
91
|
+
runs.push(["white", at(0, from) - 0.3, at(0, from) + 0.3, y]);
|
|
92
|
+
runs.push(["red", at(WATER_POLO.fiveMetre, from) - 0.3, at(WATER_POLO.fiveMetre, from) + 0.3, y]);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
lines += ropeMarkup(o, runs, WATER_POLO.rope, 0.42);
|
|
96
|
+
// The goals: two posts and the crossbar between them on each goal line, white, with the net behind the line. The posts and the bar are drawn thicker than they stand, as the lines are.
|
|
97
|
+
const drawn = o.exact ? post : Math.max(post, 0.32);
|
|
98
|
+
for (const from of ["left", "right"]) {
|
|
99
|
+
const x = at(0, from);
|
|
100
|
+
const inward = from === "left" ? 1 : -1;
|
|
101
|
+
lines += el("path", { class: ft(o, "goals", "ky-pl-gn"), d: `M${num(feet(x))} ${num(feet(W / 2 - half))}h${num(feet(-inward * b))}v${num(feet(2 * half))}h${num(feet(inward * b))}`, "stroke-width": feet(0.09), fill: "none" });
|
|
102
|
+
lines += el("path", { class: ft(o, "goals", "ky-pl-gb"), d: `M${num(feet(x))} ${num(feet(W / 2 - half))}V${num(feet(W / 2 + half))}`, "stroke-width": feet(drawn) * 0.8, fill: "none" });
|
|
103
|
+
for (const side of [-1, 1]) {
|
|
104
|
+
const y = W / 2 + side * (half + post / 2);
|
|
105
|
+
lines += el("rect", { class: ft(o, "goal-posts", "ky-pl-gp"), x: feet(x - drawn / 2 - (inward * drawn) / 2 + (inward * drawn) / 2), y: feet(y - drawn / 2), width: feet(drawn), height: feet(drawn), rx: feet(drawn) * 0.12 });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// The official table and the flying substitution areas on the side of the benches.
|
|
109
|
+
if (down) {
|
|
110
|
+
const t = WATER_POLO.table;
|
|
111
|
+
base += el("rect", { class: ft(o, "official-table", "ky-pl-tb"), x: feet(Lp / 2 - t.length / 2), y: feet(W + GUTTER + platform + 0.5), width: feet(t.length), height: feet(t.depth), rx: feet(0.12) });
|
|
112
|
+
}
|
|
113
|
+
const sub = WATER_POLO.substitution;
|
|
114
|
+
if (full) {
|
|
115
|
+
const y0 = down ? W + GUTTER + platform : -GUTTER - platform - sub;
|
|
116
|
+
base += el("rect", { class: ft(o, "substitution-areas", "ky-pl-sa-l"), x: feet(g0), y: feet(y0), width: feet(Lp / 2 - g0), height: feet(sub) });
|
|
117
|
+
base += el("rect", { class: ft(o, "substitution-areas", "ky-pl-sa-r"), x: feet(Lp / 2), y: feet(y0), width: feet(g1 - Lp / 2), height: feet(sub) });
|
|
118
|
+
}
|
|
119
|
+
// The names of the parts, for a drawing that labels them.
|
|
120
|
+
const marks = [];
|
|
121
|
+
if (o.landmarks === true) {
|
|
122
|
+
marks.push({ id: "goal-line", x: feet(g0), y: 0, side: "top" }, { id: "two-metre-line", x: feet(at(WATER_POLO.twoMetre, "left")), y: feet(W), side: "bottom" }, { id: "five-metre-mark", x: feet(at(WATER_POLO.fiveMetre, "left")), y: 0, side: "top" }, { id: "six-metre-line", x: feet(at(WATER_POLO.sixMetre, "left")), y: feet(W), side: "bottom" }, { id: "half-distance-line", x: feet(Lp / 2), y: 0, side: "top" }, { id: "goal", x: feet(g1), y: feet(W / 2), side: "bottom" }, { id: "re-entry-area", x: feet(at(0.5, "right")), y: feet(down ? 0.5 : W - 0.5), side: down ? "top" : "bottom" }, { id: "referee-platform", x: feet(Lp * 0.3), y: feet(down ? W + GUTTER + platform / 2 : -GUTTER - platform / 2), side: down ? "bottom" : "top" });
|
|
123
|
+
if (down)
|
|
124
|
+
marks.push({ id: "official-table", x: feet(Lp / 2), y: feet(W + GUTTER + platform + 0.5 + WATER_POLO.table.depth), side: "bottom" });
|
|
125
|
+
}
|
|
126
|
+
const view0 = { x: -m, y: -feet(deckTop) - m, w: feet(Lp) + 2 * m, h: feet(W) + feet(deckTop + deckBottom) + 2 * m };
|
|
127
|
+
// The deck is under everything.
|
|
128
|
+
if (ctx.bare !== true)
|
|
129
|
+
base = deckMarkup(o, ids, plan.deck, view0.x, view0.y, view0.w, view0.h) + base;
|
|
130
|
+
const L = feet(Lp);
|
|
131
|
+
const Wf = feet(W);
|
|
132
|
+
const cy = Wf / 2;
|
|
133
|
+
const turned = o.portrait;
|
|
134
|
+
const surface = {
|
|
135
|
+
sport: "waterpolo",
|
|
136
|
+
view: view0,
|
|
137
|
+
base,
|
|
138
|
+
lines,
|
|
139
|
+
defs: ctx.bare === true ? "" : paintDefs(ids, plan.look, plan.deck, plan.seed),
|
|
140
|
+
left: { cx: -m / 2, cy, w: Wf, h: m, rot: -90 },
|
|
141
|
+
right: { cx: L + m / 2, cy, w: Wf, h: m, rot: 90 },
|
|
142
|
+
plate: turned ? { cx: L / 2, cy, w: Wf * 0.9, h: L * 0.3 } : { cx: L / 2, cy, w: L * 0.3, h: Wf * 0.5 },
|
|
143
|
+
turned,
|
|
144
|
+
};
|
|
145
|
+
surface.box = { x: 0, y: 0, w: L, h: Wf };
|
|
146
|
+
surface.lineRef = { width: thin, feet: 1 };
|
|
147
|
+
surface.ranges = {
|
|
148
|
+
half: atEnds((end) => (end === "left" ? edges(0, 0, L / 2, Wf) : edges(L / 2, 0, L, Wf))),
|
|
149
|
+
"goal-area": atEnds((end) => (end === "left" ? edges(0, 0, feet(g0 + WATER_POLO.twoMetre + 0.5), Wf) : edges(L - feet(Lp - g1 + WATER_POLO.twoMetre + 0.5), 0, L, Wf))),
|
|
150
|
+
};
|
|
151
|
+
if (o.landmarks === true)
|
|
152
|
+
surface.marks = marks;
|
|
153
|
+
if (turned) {
|
|
154
|
+
const t = quarterTurn(view0);
|
|
155
|
+
surface.view = t.view;
|
|
156
|
+
surface.turn = t.turn;
|
|
157
|
+
}
|
|
158
|
+
return surface;
|
|
159
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SwimPlan } from "../pool.ts";
|
|
2
|
+
import { type PoolCtx, type PoolIds } from "../pool.kit.ts";
|
|
3
|
+
import { type BuildOptions, type Mark, type Surface } from "./surface.ts";
|
|
4
|
+
/** The swimming pool lying wide, before its deck and view: the water and the gutter, and the lines, ropes and fixtures over them, in feet from the pool's top left corner. */
|
|
5
|
+
export interface SwimBody {
|
|
6
|
+
/** The gutter and the water. */
|
|
7
|
+
base: string;
|
|
8
|
+
/** The markings on the floor, the ropes and the fixtures. */
|
|
9
|
+
lines: string;
|
|
10
|
+
/** The labels of the parts to name, in feet. */
|
|
11
|
+
marks: Mark[];
|
|
12
|
+
/** Where the lane markings end: kept for the lineRef. */
|
|
13
|
+
line: number;
|
|
14
|
+
}
|
|
15
|
+
/** The gutter round the water, in metres: the width of the coping the pool is drawn with. */
|
|
16
|
+
export declare const GUTTER = 0.4;
|
|
17
|
+
/**
|
|
18
|
+
* The body of a swimming pool: every figure is the rules' (`SWIMMING`, pool.figures.ts). The lane markings on the floor are 46 m (21 m in a 25 m pool) long and end 2 m from the walls in a cross line 1 m long (CR 16.2.15); a
|
|
19
|
+
* cross line 0.5 m long at 15 m from each end; the touch panels 2.4 m wide at the walls with the target lines; the ropes in the rules' colours with red floats within 5 m of the walls and a mark at 15 m and 25 m;
|
|
20
|
+
* the starting blocks with their lane numbers; the backstroke flags 5 m from each end, and the false start rope 15 m from the starting end.
|
|
21
|
+
*/
|
|
22
|
+
export declare function swimBody(o: BuildOptions, plan: SwimPlan, ids: PoolIds): SwimBody;
|
|
23
|
+
/**
|
|
24
|
+
* A swimming pool lying wide with its deck: the body, the deck round it in the colour or the tiles asked for, the data of a race at the walls and over the water, and a side section under the plan if
|
|
25
|
+
* it is asked for. The pool's water is the surface's `box`; the deck is the margin.
|
|
26
|
+
*/
|
|
27
|
+
export declare function swimming(o: BuildOptions, plan: SwimPlan, ctx?: PoolCtx): Surface;
|