@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
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// What a skating drawing says of itself: its sentence about the rink, and what the options mark on it. Cheap (it reads the options, not the plan), so that a title and a description cost nothing.
|
|
2
|
+
import { fill, KYOGIJO_STRINGS } from "../strings.js";
|
|
3
|
+
import { raceById } from "./events.js";
|
|
4
|
+
import { furnitureOf, racesOf, skatingLeague } from "./plan.js";
|
|
5
|
+
import { longOval } from "./track.js";
|
|
6
|
+
/** The words of a race in a language. */
|
|
7
|
+
export const raceName = (id, language) => raceById(id)?.name[language] ?? id;
|
|
8
|
+
/** The sentence about the rink, as a fragment: "a 400 m speed skating track with two lanes of 4 m...". */
|
|
9
|
+
export function skatingAbout(o, language) {
|
|
10
|
+
const words = KYOGIJO_STRINGS[language].skating;
|
|
11
|
+
const league = skatingLeague(o);
|
|
12
|
+
const d = league.dimensions;
|
|
13
|
+
if (d.kind === "long-track") {
|
|
14
|
+
const oval = longOval(o.skating?.innerRadius, o.skating?.laneWidth, d);
|
|
15
|
+
return fill(words.longTrack, { radius: oval.a, outer: oval.w2, straight: Math.round(oval.straight * 100) / 100 });
|
|
16
|
+
}
|
|
17
|
+
if (d.kind === "short-track")
|
|
18
|
+
return fill(words.shortTrack, { length: o.skating?.rink?.length ?? d.rink.length, width: o.skating?.rink?.width ?? d.rink.width, tracks: o.skating?.tracks ?? 5 });
|
|
19
|
+
return fill(words.figure, { length: o.skating?.rink?.length ?? d.length.drawn, width: o.skating?.rink?.width ?? d.width.drawn });
|
|
20
|
+
}
|
|
21
|
+
/** The sentences that follow it: the races marked, the furniture of a figure skating rink and the close-up. Each starts after a gap. */
|
|
22
|
+
export function skatingSentences(o, language) {
|
|
23
|
+
const words = KYOGIJO_STRINGS[language].skating;
|
|
24
|
+
const { gap } = KYOGIJO_STRINGS[language].said;
|
|
25
|
+
const sep = KYOGIJO_STRINGS[language].layers.listSep;
|
|
26
|
+
const discipline = skatingLeague(o).dimensions.kind;
|
|
27
|
+
const out = [];
|
|
28
|
+
if (discipline === "figure") {
|
|
29
|
+
const furniture = furnitureOf(o.skating);
|
|
30
|
+
if (furniture.length > 0)
|
|
31
|
+
out.push(fill(words.furniture, { list: furniture.map((f) => words.furnitureNames[f]).join(sep) }));
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
const races = racesOf(o.skating, discipline);
|
|
35
|
+
if (races.length > 0)
|
|
36
|
+
out.push(fill(words.races, { list: races.map((r) => raceName(r, language)).join(sep) }));
|
|
37
|
+
}
|
|
38
|
+
const range = typeof o.range === "string" ? o.range : typeof o.range === "object" && o.range !== null && "name" in o.range ? o.range.name : undefined;
|
|
39
|
+
if (range === "start" && discipline !== "figure")
|
|
40
|
+
out.push(fill(words.start, { race: raceName(o.skating?.start ?? "500m", language) }));
|
|
41
|
+
if (range === "finish" && discipline !== "figure")
|
|
42
|
+
out.push(words.finish);
|
|
43
|
+
return out.map((t) => `${gap}${t}`).join("");
|
|
44
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { LongTrackRace, RaceInfo, ShortTrackRace, SkatingRace } from "./types.ts";
|
|
2
|
+
/** The races of the long track, in the order a menu lists them. The mass start is 16 laps (6400 m); the team pursuit and the team sprint are marked by their lines in the middle of the straights. */
|
|
3
|
+
export declare const LONG_TRACK_RACES: readonly RaceInfo[];
|
|
4
|
+
/**
|
|
5
|
+
* The races of the short track: the individual 500, 1000 and 1500 m, the 3000 m of the World Cup, and the relays: the women's 3000 m, the men's 5000 m and the mixed 2000 m (Rule 281.1a, which gives the
|
|
6
|
+
* distances of the ISU Championships; the diagram of the tracks gives the laps of each distance).
|
|
7
|
+
*/
|
|
8
|
+
export declare const SHORT_TRACK_RACES: readonly RaceInfo[];
|
|
9
|
+
/** Every race of both tracks, once by id. */
|
|
10
|
+
export declare const RACES: readonly RaceInfo[];
|
|
11
|
+
export declare const LONG_TRACK_RACE_IDS: readonly LongTrackRace[];
|
|
12
|
+
export declare const SHORT_TRACK_RACE_IDS: readonly ShortTrackRace[];
|
|
13
|
+
export declare const RACE_IDS: readonly SkatingRace[];
|
|
14
|
+
/** A race by its id, or `undefined`. */
|
|
15
|
+
export declare const raceById: (id: unknown) => RaceInfo | undefined;
|
|
16
|
+
/** The kinds of element of a figure skating programme a mark can stand for. */
|
|
17
|
+
export declare const ELEMENT_KINDS: readonly ["jump", "spin", "step-sequence", "choreographic-sequence", "lift", "spiral", "twizzle", "death-spiral", "throw"];
|
|
18
|
+
export type ElementKind = (typeof ELEMENT_KINDS)[number];
|
|
19
|
+
/** The words of a kind of element. */
|
|
20
|
+
export declare const ELEMENT_NAMES: Readonly<Record<ElementKind, {
|
|
21
|
+
en: string;
|
|
22
|
+
ja: string;
|
|
23
|
+
fr: string;
|
|
24
|
+
}>>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const race = (r) => Object.freeze(r);
|
|
2
|
+
/** The races of the long track, in the order a menu lists them. The mass start is 16 laps (6400 m); the team pursuit and the team sprint are marked by their lines in the middle of the straights. */
|
|
3
|
+
export const LONG_TRACK_RACES = Object.freeze([
|
|
4
|
+
race({ id: "500m", distance: 500, name: { en: "500 m", ja: "500m", fr: "500\u00a0m" } }),
|
|
5
|
+
race({ id: "1000m", distance: 1000, name: { en: "1000 m", ja: "1000m", fr: "1000\u00a0m" } }),
|
|
6
|
+
race({ id: "1500m", distance: 1500, name: { en: "1500 m", ja: "1500m", fr: "1500\u00a0m" } }),
|
|
7
|
+
race({ id: "3000m", distance: 3000, name: { en: "3000 m", ja: "3000m", fr: "3000\u00a0m" } }),
|
|
8
|
+
race({ id: "5000m", distance: 5000, name: { en: "5000 m", ja: "5000m", fr: "5000\u00a0m" } }),
|
|
9
|
+
race({ id: "10000m", distance: 10000, name: { en: "10,000 m", ja: "10000m", fr: "10\u00a0000\u00a0m" } }),
|
|
10
|
+
race({ id: "mass-start", distance: 6400, name: { en: "Mass start", ja: "マススタート", fr: "Départ groupé" } }),
|
|
11
|
+
race({ id: "team-pursuit", distance: 3200, name: { en: "Team pursuit", ja: "チームパシュート", fr: "Poursuite par équipes" } }),
|
|
12
|
+
race({ id: "team-sprint", distance: 1200, name: { en: "Team sprint", ja: "チームスプリント", fr: "Sprint par équipes" } }),
|
|
13
|
+
]);
|
|
14
|
+
/**
|
|
15
|
+
* The races of the short track: the individual 500, 1000 and 1500 m, the 3000 m of the World Cup, and the relays: the women's 3000 m, the men's 5000 m and the mixed 2000 m (Rule 281.1a, which gives the
|
|
16
|
+
* distances of the ISU Championships; the diagram of the tracks gives the laps of each distance).
|
|
17
|
+
*/
|
|
18
|
+
export const SHORT_TRACK_RACES = Object.freeze([
|
|
19
|
+
race({ id: "500m", distance: 500, name: { en: "500 m", ja: "500m", fr: "500\u00a0m" } }),
|
|
20
|
+
race({ id: "1000m", distance: 1000, name: { en: "1000 m", ja: "1000m", fr: "1000\u00a0m" } }),
|
|
21
|
+
race({ id: "1500m", distance: 1500, name: { en: "1500 m", ja: "1500m", fr: "1500\u00a0m" } }),
|
|
22
|
+
race({ id: "3000m", distance: 3000, name: { en: "3000 m", ja: "3000m", fr: "3000\u00a0m" } }),
|
|
23
|
+
race({ id: "3000m-relay", distance: 3000, name: { en: "3000 m relay", ja: "3000mリレー", fr: "Relais 3000\u00a0m" } }),
|
|
24
|
+
race({ id: "5000m-relay", distance: 5000, name: { en: "5000 m relay", ja: "5000mリレー", fr: "Relais 5000\u00a0m" } }),
|
|
25
|
+
race({ id: "2000m-mixed-relay", distance: 2000, name: { en: "2000 m mixed relay", ja: "2000m混合リレー", fr: "Relais mixte 2000\u00a0m" } }),
|
|
26
|
+
]);
|
|
27
|
+
/** Every race of both tracks, once by id. */
|
|
28
|
+
export const RACES = Object.freeze([...LONG_TRACK_RACES, ...SHORT_TRACK_RACES.filter((r) => !LONG_TRACK_RACES.some((l) => l.id === r.id))]);
|
|
29
|
+
export const LONG_TRACK_RACE_IDS = Object.freeze(LONG_TRACK_RACES.map((r) => r.id));
|
|
30
|
+
export const SHORT_TRACK_RACE_IDS = Object.freeze(SHORT_TRACK_RACES.map((r) => r.id));
|
|
31
|
+
export const RACE_IDS = Object.freeze(RACES.map((r) => r.id));
|
|
32
|
+
/** A race by its id, or `undefined`. */
|
|
33
|
+
export const raceById = (id) => RACES.find((r) => r.id === id);
|
|
34
|
+
/** The kinds of element of a figure skating programme a mark can stand for. */
|
|
35
|
+
export const ELEMENT_KINDS = Object.freeze(["jump", "spin", "step-sequence", "choreographic-sequence", "lift", "spiral", "twizzle", "death-spiral", "throw"]);
|
|
36
|
+
/** The words of a kind of element. */
|
|
37
|
+
export const ELEMENT_NAMES = Object.freeze({
|
|
38
|
+
jump: { en: "Jump", ja: "ジャンプ", fr: "Saut" },
|
|
39
|
+
spin: { en: "Spin", ja: "スピン", fr: "Pirouette" },
|
|
40
|
+
"step-sequence": { en: "Step sequence", ja: "ステップシークエンス", fr: "Séquence de pas" },
|
|
41
|
+
"choreographic-sequence": { en: "Choreographic sequence", ja: "コレオシークエンス", fr: "Séquence chorégraphique" },
|
|
42
|
+
lift: { en: "Lift", ja: "リフト", fr: "Levée" },
|
|
43
|
+
spiral: { en: "Spiral", ja: "スパイラル", fr: "Spirale" },
|
|
44
|
+
twizzle: { en: "Twizzle", ja: "ツイズル", fr: "Twizzle" },
|
|
45
|
+
"death-spiral": { en: "Death spiral", ja: "デススパイラル", fr: "Spirale de la mort" },
|
|
46
|
+
throw: { en: "Throw jump", ja: "スロージャンプ", fr: "Saut lancé" },
|
|
47
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type FigureRinkFigures } from "../leagues/skating.ts";
|
|
2
|
+
import { type MBox, type MMark, type Parts } from "./parts.ts";
|
|
3
|
+
import type { SkatingFurniture } from "./types.ts";
|
|
4
|
+
/** The rink: its length and width. */
|
|
5
|
+
export interface FigureRink {
|
|
6
|
+
length: number;
|
|
7
|
+
width: number;
|
|
8
|
+
}
|
|
9
|
+
/** The furniture of a figure skating rink, in painter's order. */
|
|
10
|
+
export declare const FURNITURE: readonly SkatingFurniture[];
|
|
11
|
+
/** The seats of a panel of judges, side by side at the rinkboard: nine, as at the Olympic Winter Games (ours: the Rules fix the number elsewhere), 1 m apart. */
|
|
12
|
+
export declare const JUDGES = 9;
|
|
13
|
+
/** Where the furniture stands outside the boards of the rink on its long side at the bottom (and the kiss and cry at the end), as boxes in metres. */
|
|
14
|
+
export declare function furnitureBoxes(r: FigureRink, fig?: FigureRinkFigures): Record<SkatingFurniture, MBox[]>;
|
|
15
|
+
export interface FigureOptions {
|
|
16
|
+
fig: FigureRinkFigures;
|
|
17
|
+
furniture: readonly SkatingFurniture[];
|
|
18
|
+
}
|
|
19
|
+
/** The parts of the rink. */
|
|
20
|
+
export declare function figureParts(r: FigureRink, opt: FigureOptions): {
|
|
21
|
+
parts: Parts;
|
|
22
|
+
box: MBox;
|
|
23
|
+
marks: MMark[];
|
|
24
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ISU_FIGURE_RINK } from "../leagues/skating.js";
|
|
2
|
+
import { emptyParts } from "./parts.js";
|
|
3
|
+
/** The furniture of a figure skating rink, in painter's order. */
|
|
4
|
+
export const FURNITURE = Object.freeze(["judges", "referee", "technical-panel", "kiss-and-cry"]);
|
|
5
|
+
/** The seats of a panel of judges, side by side at the rinkboard: nine, as at the Olympic Winter Games (ours: the Rules fix the number elsewhere), 1 m apart. */
|
|
6
|
+
export const JUDGES = 9;
|
|
7
|
+
/** Where the furniture stands outside the boards of the rink on its long side at the bottom (and the kiss and cry at the end), as boxes in metres. */
|
|
8
|
+
export function furnitureBoxes(r, fig = ISU_FIGURE_RINK) {
|
|
9
|
+
const y = r.width / 2 + fig.boards + 0.6;
|
|
10
|
+
const seat = (x) => ({ x0: x - 0.4, y0: y, x1: x + 0.4, y1: y + 0.8 });
|
|
11
|
+
return {
|
|
12
|
+
judges: Array.from({ length: JUDGES }, (_, i) => seat((i - (JUDGES - 1) / 2) * 1)),
|
|
13
|
+
referee: [seat(-(JUDGES - 1) / 2 - 2)],
|
|
14
|
+
"technical-panel": [{ x0: (JUDGES - 1) / 2 + 1.5, y0: y + 1.2, x1: (JUDGES - 1) / 2 + 5.5, y1: y + 2.4 }],
|
|
15
|
+
"kiss-and-cry": [{ x0: -r.length / 2 + 3, y0: y, x1: -r.length / 2 + 6.5, y1: y + 1.6 }],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** The parts of the rink. */
|
|
19
|
+
export function figureParts(r, opt) {
|
|
20
|
+
const { fig } = opt;
|
|
21
|
+
const parts = emptyParts();
|
|
22
|
+
const Lh = r.length / 2;
|
|
23
|
+
const W = r.width / 2;
|
|
24
|
+
const rd = (n) => Math.round(n * 100) / 100;
|
|
25
|
+
const roundD = (hx, hy, k) => `M${rd(-hx + k)} ${rd(-hy)}H${rd(hx - k)}A${k} ${k} 0 0 1 ${rd(hx)} ${rd(-hy + k)}V${rd(hy - k)}A${k} ${k} 0 0 1 ${rd(hx - k)} ${rd(hy)}H${rd(-hx + k)}A${k} ${k} 0 0 1 ${rd(-hx)} ${rd(hy - k)}V${rd(-hy + k)}A${k} ${k} 0 0 1 ${rd(-hx + k)} ${rd(-hy)}Z`;
|
|
26
|
+
parts.areas.push({ id: "ice", feature: "ice", cls: "ky-ska-ice", d: roundD(Lh, W, fig.corner) });
|
|
27
|
+
parts.areas.push({ id: "boards", feature: "boards", cls: "ky-boards", d: `${roundD(Lh + fig.boards, W + fig.boards, fig.corner + fig.boards)}${roundD(Lh, W, fig.corner)}`, evenodd: true });
|
|
28
|
+
const boxes = furnitureBoxes(r, fig);
|
|
29
|
+
const marks = [];
|
|
30
|
+
for (const kind of opt.furniture) {
|
|
31
|
+
boxes[kind].forEach((b, i) => parts.areas.push({ id: `${kind}-${i + 1}`, feature: kind, cls: kind === "kiss-and-cry" ? "ky-ska-kiss" : "ky-ska-desk", rect: [b.x0, b.y0, b.x1, b.y1] }));
|
|
32
|
+
const b = boxes[kind][Math.floor(boxes[kind].length / 2)];
|
|
33
|
+
marks.push({ id: kind, x: (b.x0 + b.x1) / 2, y: b.y1, side: "bottom" });
|
|
34
|
+
}
|
|
35
|
+
const extra = opt.furniture.length === 0 ? 0 : 3.4;
|
|
36
|
+
return { parts, box: { x0: -Lh - fig.boards, y0: -W - fig.boards, x1: Lh + fig.boards, y1: W + fig.boards + extra }, marks };
|
|
37
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Shape } from "../fields.types.ts";
|
|
2
|
+
import { type LongTrackFigures } from "../leagues/skating.ts";
|
|
3
|
+
import { LONG_TRACK_RACES } from "./events.ts";
|
|
4
|
+
import { addParts, type MBox, type Parts } from "./parts.ts";
|
|
5
|
+
import { laneRadius, type Lane, type LongOval } from "./track.ts";
|
|
6
|
+
import type { LongTrackRace, SkatingInfield } from "./types.ts";
|
|
7
|
+
/** Where a skater starts: the lane (or `both` for a line across both lanes) and the point of the start. */
|
|
8
|
+
export interface StartPlacement {
|
|
9
|
+
lane: Lane | "both";
|
|
10
|
+
/** The coordinate along the skater's own way, from the beginning of the finishing straight of the lap the start is in. */
|
|
11
|
+
v0: number;
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
heading: number;
|
|
15
|
+
}
|
|
16
|
+
/** What a race marks: the lines where it starts, the finishing line it ends at, and how its first straight is skated. */
|
|
17
|
+
export interface RaceMarks {
|
|
18
|
+
race: LongTrackRace;
|
|
19
|
+
/** The finishing line: the 400 m mark at the end of the finishing straight or the 1000 m mark in the middle of it. */
|
|
20
|
+
finish: "400m" | "1000m";
|
|
21
|
+
/** True when the first straight keeps its lane (the 1000 m and the 1500 m, Rule 254.5b). */
|
|
22
|
+
noChange: boolean;
|
|
23
|
+
/** The starting lines: one for each lane, or one across both. */
|
|
24
|
+
starts: StartPlacement[];
|
|
25
|
+
}
|
|
26
|
+
/** The marks of a race on the oval. */
|
|
27
|
+
export declare function markRace(o: LongOval, race: LongTrackRace): RaceMarks;
|
|
28
|
+
/** What the long track adds: everything from the ice to the lines. */
|
|
29
|
+
export interface LongOptions {
|
|
30
|
+
fig: LongTrackFigures;
|
|
31
|
+
warmUp: boolean;
|
|
32
|
+
infield: SkatingInfield;
|
|
33
|
+
blocks: boolean;
|
|
34
|
+
coaches: boolean;
|
|
35
|
+
padding: boolean;
|
|
36
|
+
races: readonly LongTrackRace[];
|
|
37
|
+
}
|
|
38
|
+
export declare const LONG_DEFAULTS: {
|
|
39
|
+
warmUp: boolean;
|
|
40
|
+
infield: SkatingInfield;
|
|
41
|
+
blocks: boolean;
|
|
42
|
+
coaches: boolean;
|
|
43
|
+
padding: boolean;
|
|
44
|
+
};
|
|
45
|
+
/** The ice's outer radius: the outer edge of the outer lane and the 3 m the Rules keep clear (Rule 228.2). */
|
|
46
|
+
export declare const iceRadius: (o: LongOval, fig?: LongTrackFigures) => number;
|
|
47
|
+
/** The parts of the long track. */
|
|
48
|
+
export declare function longParts(o: LongOval, opt: LongOptions): {
|
|
49
|
+
parts: Parts;
|
|
50
|
+
marks: RaceMarks[];
|
|
51
|
+
box: MBox;
|
|
52
|
+
};
|
|
53
|
+
export { addParts, laneRadius, LONG_TRACK_RACES };
|
|
54
|
+
export type { Shape };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// The long track: the ice, the padding, the lanes and the warm-up lane, the blocks of the curves, the coaches' area, the finishing lines and the starting lines of each race. Every figure is the ISU's
|
|
2
|
+
// (Special Regulations and Technical Rules Speed Skating, Rules 203, 205, 226, 227, 228, 253, 254 and 256 to 257, and "H. Track"), in metres. Nothing here draws SVG: it makes the shapes.
|
|
3
|
+
import { line, poly } from "../fields.kit.js";
|
|
4
|
+
import { ISU_LONG_TRACK } from "../leagues/skating.js";
|
|
5
|
+
import { LONG_TRACK_RACES } from "./events.js";
|
|
6
|
+
import { addParts, blocksOf, emptyParts, stadiumD } from "./parts.js";
|
|
7
|
+
import { finishAt, laneEdges, laneRadius, outerEdge, pose, startOf } from "./track.js";
|
|
8
|
+
/** How each race starts. `at` is the finishing line's mark, `middle` a start in the middle of the straights (Rules 256 and 257). */
|
|
9
|
+
const SPEC = {
|
|
10
|
+
"500m": { finish: "400m", distance: 500, noChange: false },
|
|
11
|
+
"1000m": { finish: "1000m", distance: 1000, noChange: true },
|
|
12
|
+
"1500m": { finish: "400m", distance: 1500, noChange: true },
|
|
13
|
+
"3000m": { finish: "400m", distance: 3000, noChange: false },
|
|
14
|
+
"5000m": { finish: "400m", distance: 5000, noChange: false },
|
|
15
|
+
"10000m": { finish: "400m", distance: 10000, noChange: false },
|
|
16
|
+
"mass-start": { finish: "400m", distance: 0, noChange: false, start: "middle" },
|
|
17
|
+
"team-pursuit": { finish: "1000m", distance: 0, noChange: false, start: "both-straights" },
|
|
18
|
+
"team-sprint": { finish: "1000m", distance: 0, noChange: false, start: "both-straights" },
|
|
19
|
+
};
|
|
20
|
+
/** The marks of a race on the oval. */
|
|
21
|
+
export function markRace(o, race) {
|
|
22
|
+
const spec = SPEC[race];
|
|
23
|
+
const finish = finishAt(o, spec.finish);
|
|
24
|
+
const place = (lane, v0, p) => ({ lane, v0, x: p.x, y: p.y, heading: p.heading });
|
|
25
|
+
if (spec.start === "middle") {
|
|
26
|
+
const p = pose(o, 0, o.straight / 2);
|
|
27
|
+
return { race, finish: spec.finish, noChange: false, starts: [place("both", o.straight / 2, { ...p, y: o.d })] };
|
|
28
|
+
}
|
|
29
|
+
if (spec.start === "both-straights") {
|
|
30
|
+
const bottom = pose(o, 0, o.straight / 2);
|
|
31
|
+
const top = pose(o, 0, o.straight + Math.PI * o.d + o.crossing / 2);
|
|
32
|
+
return { race, finish: spec.finish, noChange: false, starts: [place(0, o.straight / 2, bottom), place(0, 0, { ...top, y: -o.d })] };
|
|
33
|
+
}
|
|
34
|
+
const starts = [0, 1].map((lane) => {
|
|
35
|
+
const s = startOf(o, lane, spec.distance, finish, spec.noChange);
|
|
36
|
+
return place(lane, s.v0, s.pose);
|
|
37
|
+
});
|
|
38
|
+
// A line across both lanes where the two lanes start at the same place (the 500 m, the 10,000 m).
|
|
39
|
+
const same = Math.abs(starts[0].x - starts[1].x) < 1e-6 && Math.abs(starts[0].y * starts[1].y) > 0 && Math.sign(starts[0].y) === Math.sign(starts[1].y);
|
|
40
|
+
return { race, finish: spec.finish, noChange: spec.noChange, starts: same ? [{ ...starts[0], lane: "both" }] : starts };
|
|
41
|
+
}
|
|
42
|
+
/** The y-range of a lane's band on the top or the bottom side at x, from the edges' radii: on a straight the band is the radii themselves; past the end of a curve's start, the circle's. */
|
|
43
|
+
function bandAt(f, x, rA, rB, side) {
|
|
44
|
+
const out = Math.abs(x) - f / 2;
|
|
45
|
+
const y = (r) => (out <= 0 ? r : Math.sqrt(Math.max(0, r * r - out * out)));
|
|
46
|
+
return [side * y(rA), side * y(rB)];
|
|
47
|
+
}
|
|
48
|
+
export const LONG_DEFAULTS = { warmUp: true, infield: "floor", blocks: true, coaches: true, padding: true };
|
|
49
|
+
/** The ice's outer radius: the outer edge of the outer lane and the 3 m the Rules keep clear (Rule 228.2). */
|
|
50
|
+
export const iceRadius = (o, fig = ISU_LONG_TRACK) => outerEdge(o) + fig.padding.clear;
|
|
51
|
+
/** The parts of the long track. */
|
|
52
|
+
export function longParts(o, opt) {
|
|
53
|
+
const { fig } = opt;
|
|
54
|
+
const f = o.straight;
|
|
55
|
+
const edges = laneEdges(o, opt.warmUp ? fig.warmUpLane : 0);
|
|
56
|
+
const R = iceRadius(o, fig);
|
|
57
|
+
const parts = emptyParts();
|
|
58
|
+
const L = fig.line;
|
|
59
|
+
// The ice, the infield and the padding: the ice out to the padding, the padding a band 0.3 m thick at its edge, and what lies inside the warm-up lane.
|
|
60
|
+
parts.areas.push({ id: "ice", feature: "ice", cls: "ky-ska-ice", d: stadiumD(f, R) });
|
|
61
|
+
if (opt.padding)
|
|
62
|
+
parts.areas.push({ id: "padding", feature: "padding", cls: "ky-ska-pad", d: `${stadiumD(f, R + fig.padding.thick)}${stadiumD(f, R)}`, evenodd: true });
|
|
63
|
+
if (opt.warmUp)
|
|
64
|
+
parts.areas.push({ id: "warm-up", feature: "warm-up", cls: "ky-ska-warm", d: `${stadiumD(f, edges.inner)}${stadiumD(f, edges.warmUp)}`, evenodd: true });
|
|
65
|
+
parts.areas.push({ id: "infield", feature: "infield", cls: opt.infield === "ice" ? "ky-ska-ice" : "ky-ska-floor", d: stadiumD(f, edges.warmUp) });
|
|
66
|
+
// The lanes' lines (Rule 226): fully painted, 5 cm wide, in the straights and in the curves.
|
|
67
|
+
const radii = [...(opt.warmUp ? [edges.warmUp] : []), edges.inner, edges.between, edges.outer];
|
|
68
|
+
radii.forEach((r, i) => parts.shapes.push({ kind: "path", id: `lane-line-${i}`, feature: "lane-lines", d: stadiumD(f, r), w: L, cls: "ky-ska-b" }));
|
|
69
|
+
// The blocks (Rule 226): inside the painted lines of the inner lane's edge and of the line between the lanes, 50 cm apart for the first 15 m of a curve and 2 m after.
|
|
70
|
+
if (opt.blocks) {
|
|
71
|
+
for (const [name, r] of [["inner", edges.inner], ["between", edges.between]]) {
|
|
72
|
+
const rb = r - L - fig.blocks.height;
|
|
73
|
+
for (const side of ["right", "left"])
|
|
74
|
+
parts.shapes.push(...blocksOf(`blocks-${name}-${side}`, f, side, rb, fig.blocks.near, fig.blocks.nearLength, fig.blocks.far, 0.12));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// The coaches' area (Rule 227.3): a line 2 cm wide, 1 m from the outside edge of the track, from 25 m after the end of the curve to 10 m before the next, on the crossing straight.
|
|
78
|
+
if (opt.coaches) {
|
|
79
|
+
const y = -(edges.outer + fig.coaches.offset);
|
|
80
|
+
parts.shapes.push(line("coaches-area", "coaches-area", f / 2 - fig.coaches.after, y, -f / 2 + fig.coaches.before, y, { w: 0.02, cls: "ky-ska-r" }));
|
|
81
|
+
}
|
|
82
|
+
// The finishing lines and the last 5 m before them, marked by the metre (Rule 227.2).
|
|
83
|
+
const marks = opt.races.map((r) => markRace(o, r));
|
|
84
|
+
const finishes = new Set(["400m"]);
|
|
85
|
+
for (const m of marks)
|
|
86
|
+
finishes.add(m.finish);
|
|
87
|
+
const bottom = bandAt(f, 0, edges.inner, edges.outer, 1);
|
|
88
|
+
for (const which of finishes) {
|
|
89
|
+
const x = -f / 2 + finishAt(o, which);
|
|
90
|
+
parts.shapes.push(line(`finish-${which}`, "finish-line", x, bottom[0], x, bottom[1], { w: L, cls: "ky-ska-r" }));
|
|
91
|
+
for (let m = 1; m <= fig.finishMetres; m += 1)
|
|
92
|
+
parts.shapes.push(line(`finish-${which}-metre-${m}`, "finish-marks", x - m, bottom[0], x - m, bottom[1], { w: 0.02, cls: "ky-ska-b" }));
|
|
93
|
+
}
|
|
94
|
+
// The starting lines, each 2 m after a pre-start line (Rule 227.2 and 253.3): across the lane, or across both lanes.
|
|
95
|
+
for (const m of marks) {
|
|
96
|
+
for (const s of m.starts) {
|
|
97
|
+
const side = s.y >= 0 ? 1 : -1;
|
|
98
|
+
const lanes = s.lane === "both" ? [edges.inner, edges.outer] : s.lane === 0 ? [edges.inner, edges.between] : [edges.between, edges.outer];
|
|
99
|
+
const [y0, y1] = bandAt(f, s.x, lanes[0], lanes[1], side);
|
|
100
|
+
parts.shapes.push(line(`start-${m.race}-${String(s.lane)}`, "start-lines", s.x, y0, s.x, y1, { w: L, cls: "ky-ska-r" }));
|
|
101
|
+
// Behind the line: to the left on the finishing straight, to the right on the crossing straight.
|
|
102
|
+
const px = s.x + (side === 1 ? -fig.preStart : fig.preStart);
|
|
103
|
+
const [q0, q1] = bandAt(f, px, lanes[0], lanes[1], side);
|
|
104
|
+
parts.shapes.push(line(`pre-start-${m.race}-${String(s.lane)}`, "pre-start-lines", px, q0, px, q1, { w: 0.02, cls: "ky-ska-b" }));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
void poly;
|
|
108
|
+
const out = R + fig.padding.thick;
|
|
109
|
+
return { parts, marks, box: { x0: -f / 2 - out, y0: -out, x1: f / 2 + out, y1: out } };
|
|
110
|
+
}
|
|
111
|
+
export { addParts, laneRadius, LONG_TRACK_RACES };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { path, line, poly } from "../fields.kit.ts";
|
|
2
|
+
import type { Area, Shape } from "../fields.types.ts";
|
|
3
|
+
/** What a part of a plan adds. */
|
|
4
|
+
export interface Parts {
|
|
5
|
+
areas: Area[];
|
|
6
|
+
shapes: Shape[];
|
|
7
|
+
}
|
|
8
|
+
export declare const emptyParts: () => Parts;
|
|
9
|
+
/** Add `more` to `into`. */
|
|
10
|
+
export declare function addParts(into: Parts, more: Parts): void;
|
|
11
|
+
/** A box in metres. */
|
|
12
|
+
export interface MBox {
|
|
13
|
+
x0: number;
|
|
14
|
+
y0: number;
|
|
15
|
+
x1: number;
|
|
16
|
+
y1: number;
|
|
17
|
+
}
|
|
18
|
+
/** A named part of the plan to crop to, in metres: one box, or one at each end (`left` and `right` as the rink is drawn wide). */
|
|
19
|
+
export type MRange = MBox | {
|
|
20
|
+
left: MBox;
|
|
21
|
+
right: MBox;
|
|
22
|
+
};
|
|
23
|
+
/** A part of the plan to label (`landmarks`): where it is, in metres, and the side of the drawing its label stands on. */
|
|
24
|
+
export interface MMark {
|
|
25
|
+
id: string;
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
side: "top" | "bottom";
|
|
29
|
+
}
|
|
30
|
+
export declare const grow: (b: MBox, m: number) => MBox;
|
|
31
|
+
export declare const union: (boxes: readonly MBox[]) => MBox;
|
|
32
|
+
/** The path of a stadium: two semicircles of radius `r` joined by straights of length `s`, about the origin, drawn from the top left, clockwise as the eye sees it. */
|
|
33
|
+
export declare const stadiumD: (s: number, r: number) => string;
|
|
34
|
+
/** A point of a curve of the oval: `side` is the right-hand curve (centre at x = s/2, entered at the bottom) or the left-hand one (centre at x = -s/2, entered at the top); `t` the angle from the entrance. */
|
|
35
|
+
export declare function curvePoint(s: number, side: "right" | "left", r: number, t: number): [number, number];
|
|
36
|
+
/** The path of an arc of a curve of the oval from the angle `t0` to `t1`, in the way the skaters go (counter-clockwise as the eye sees it). */
|
|
37
|
+
export declare function curveD(s: number, side: "right" | "left", r: number, t0: number, t1: number): string;
|
|
38
|
+
/**
|
|
39
|
+
* The blocks of a curve as dots: a dashed path with a zero dash and round caps, so that a dot of the line's width stands every `near` metres for the first `nearLength` metres from the entrance and
|
|
40
|
+
* every `far` metres after, the cone at the entrance a dot of its own (Rule 226).
|
|
41
|
+
*/
|
|
42
|
+
export declare function blocksOf(id: string, s: number, side: "right" | "left", r: number, near: number, nearLength: number, far: number, width: number): Shape[];
|
|
43
|
+
export { line, poly, path };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// What a part of a skating plan adds, as shapes and areas in painter's order, in metres, and the small functions the parts are made of. Nothing here draws SVG.
|
|
2
|
+
import { path, line, poly } from "../fields.kit.js";
|
|
3
|
+
import { num } from "../format.js";
|
|
4
|
+
export const emptyParts = () => ({ areas: [], shapes: [] });
|
|
5
|
+
/** Add `more` to `into`. */
|
|
6
|
+
export function addParts(into, more) {
|
|
7
|
+
into.areas.push(...more.areas);
|
|
8
|
+
into.shapes.push(...more.shapes);
|
|
9
|
+
}
|
|
10
|
+
export const grow = (b, m) => ({ x0: b.x0 - m, y0: b.y0 - m, x1: b.x1 + m, y1: b.y1 + m });
|
|
11
|
+
export const union = (boxes) => ({ x0: Math.min(...boxes.map((b) => b.x0)), y0: Math.min(...boxes.map((b) => b.y0)), x1: Math.max(...boxes.map((b) => b.x1)), y1: Math.max(...boxes.map((b) => b.y1)) });
|
|
12
|
+
/** The path of a stadium: two semicircles of radius `r` joined by straights of length `s`, about the origin, drawn from the top left, clockwise as the eye sees it. */
|
|
13
|
+
export const stadiumD = (s, r) => `M${num(-s / 2)} ${num(-r)}H${num(s / 2)}A${num(r)} ${num(r)} 0 0 1 ${num(s / 2)} ${num(r)}H${num(-s / 2)}A${num(r)} ${num(r)} 0 0 1 ${num(-s / 2)} ${num(-r)}Z`;
|
|
14
|
+
/** A point of a curve of the oval: `side` is the right-hand curve (centre at x = s/2, entered at the bottom) or the left-hand one (centre at x = -s/2, entered at the top); `t` the angle from the entrance. */
|
|
15
|
+
export function curvePoint(s, side, r, t) {
|
|
16
|
+
return side === "right" ? [s / 2 + r * Math.sin(t), r * Math.cos(t)] : [-s / 2 - r * Math.sin(t), -r * Math.cos(t)];
|
|
17
|
+
}
|
|
18
|
+
/** The path of an arc of a curve of the oval from the angle `t0` to `t1`, in the way the skaters go (counter-clockwise as the eye sees it). */
|
|
19
|
+
export function curveD(s, side, r, t0, t1) {
|
|
20
|
+
const [x0, y0] = curvePoint(s, side, r, t0);
|
|
21
|
+
const [x1, y1] = curvePoint(s, side, r, t1);
|
|
22
|
+
return `M${num(x0)} ${num(y0)}A${num(r)} ${num(r)} 0 ${t1 - t0 > Math.PI ? 1 : 0} 0 ${num(x1)} ${num(y1)}`;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The blocks of a curve as dots: a dashed path with a zero dash and round caps, so that a dot of the line's width stands every `near` metres for the first `nearLength` metres from the entrance and
|
|
26
|
+
* every `far` metres after, the cone at the entrance a dot of its own (Rule 226).
|
|
27
|
+
*/
|
|
28
|
+
export function blocksOf(id, s, side, r, near, nearLength, far, width) {
|
|
29
|
+
const out = [];
|
|
30
|
+
const tNear = Math.min(Math.PI, nearLength / r);
|
|
31
|
+
out.push(path(`${id}-near`, "blocks", curveD(s, side, r, 0, tNear), { w: width, dash: [0, near], cls: "ky-ska-blocks", least: 0.4 }));
|
|
32
|
+
// The far part starts a step after the near one stops, so that no block stands twice.
|
|
33
|
+
const start = tNear + far / r;
|
|
34
|
+
if (start < Math.PI + 1e-9)
|
|
35
|
+
out.push(path(`${id}-far`, "blocks", curveD(s, side, r, start, Math.PI), { w: width, dash: [0, far], cls: "ky-ska-blocks", least: 0.4 }));
|
|
36
|
+
const [cx, cy] = curvePoint(s, side, r, 0);
|
|
37
|
+
out.push({ kind: "dot", id: `${id}-cone`, feature: "cones", cx, cy, r: width * 1.3, paint: "cone" });
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
export { line, poly, path };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Area, Shape } from "../fields.types.ts";
|
|
2
|
+
import { ISU_FIGURE_RINK, ISU_LONG_TRACK, ISU_SHORT_TRACK, type SkatingFigures, type SkatingLeague } from "../leagues/skating.ts";
|
|
3
|
+
import type { SurfaceOptions } from "../types.ts";
|
|
4
|
+
import { LONG_TRACK_RACES, SHORT_TRACK_RACES } from "./events.ts";
|
|
5
|
+
import { type FigureRink } from "./figure.ts";
|
|
6
|
+
import { type RaceMarks } from "./long.ts";
|
|
7
|
+
import { type MBox, type MMark, type MRange } from "./parts.ts";
|
|
8
|
+
import { type ShortOval } from "./short.ts";
|
|
9
|
+
import { type LongOval } from "./track.ts";
|
|
10
|
+
import type { ShortTrackRace, SkatingDiscipline, SkatingFurniture, SkatingRace, SkatingStyle } from "./types.ts";
|
|
11
|
+
export type { MBox, MMark, MRange };
|
|
12
|
+
/** What a skating drawing is. */
|
|
13
|
+
export interface SkatingPlan {
|
|
14
|
+
sport: "skating";
|
|
15
|
+
league: string;
|
|
16
|
+
discipline: SkatingDiscipline;
|
|
17
|
+
figures: SkatingFigures;
|
|
18
|
+
/** The oval of the long track, or `undefined`. */
|
|
19
|
+
oval: LongOval | undefined;
|
|
20
|
+
/** The short track's oval and rink, or `undefined`. */
|
|
21
|
+
short: ShortOval | undefined;
|
|
22
|
+
/** The figure skating rink, or `undefined`. */
|
|
23
|
+
rink: FigureRink | undefined;
|
|
24
|
+
/** The playing surface: the ice out to the padding (the long track), the rink (the others). The data frame's own box. */
|
|
25
|
+
box: MBox;
|
|
26
|
+
/** Everything drawn: the box and what lies beyond it (the furniture of a figure skating rink). */
|
|
27
|
+
extent: MBox;
|
|
28
|
+
areas: Area[];
|
|
29
|
+
shapes: Shape[];
|
|
30
|
+
/** The long track's races marked, with where each starts. */
|
|
31
|
+
races: RaceMarks[];
|
|
32
|
+
/** The short track's races marked. */
|
|
33
|
+
shortRaces: ShortTrackRace[];
|
|
34
|
+
/** The number of tracks of the short track. */
|
|
35
|
+
tracks: 5 | 7;
|
|
36
|
+
furniture: SkatingFurniture[];
|
|
37
|
+
/** The width the ordinary line is painted at by the rules, and the width it is drawn at unless `exactLines` says otherwise. */
|
|
38
|
+
lineWidth: number;
|
|
39
|
+
drawnLine: number;
|
|
40
|
+
ranges: Readonly<Record<string, MRange>>;
|
|
41
|
+
marks: MMark[];
|
|
42
|
+
/** The race a close-up of a start (`range: "start"`) shows. */
|
|
43
|
+
start: SkatingRace;
|
|
44
|
+
}
|
|
45
|
+
/** The league a set of options asks for: the long track (the sport's own), the short track or the figure skating rink. */
|
|
46
|
+
export declare function skatingLeague(o: Pick<SurfaceOptions, "sport" | "league">): SkatingLeague;
|
|
47
|
+
/** The discipline of a set of options. */
|
|
48
|
+
export declare const disciplineOf: (o: Pick<SurfaceOptions, "sport" | "league">) => SkatingDiscipline;
|
|
49
|
+
/** The races the options ask to be marked, for a discipline. @throws Error for a name that is not a race, RangeError for one the discipline does not have. */
|
|
50
|
+
export declare function racesOf(style: SkatingStyle | undefined, discipline: SkatingDiscipline): SkatingRace[];
|
|
51
|
+
/** The furniture the options ask for. @throws Error for a name that is not furniture. */
|
|
52
|
+
export declare function furnitureOf(style: SkatingStyle | undefined): SkatingFurniture[];
|
|
53
|
+
/** The skating plan the options ask for. */
|
|
54
|
+
export declare function skatingPlan(o: Pick<SurfaceOptions, "sport" | "league" | "skating" | "range" | "detail">): SkatingPlan;
|
|
55
|
+
export { LONG_TRACK_RACES, SHORT_TRACK_RACES, ISU_FIGURE_RINK, ISU_LONG_TRACK, ISU_SHORT_TRACK };
|