@johnmorrisdotca/kyogijo-stadiums 0.18.0 → 0.18.1
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 +11 -0
- package/README.md +2 -2
- package/dist/judging.d.ts +2 -0
- package/dist/judging.draw.js +6 -4
- package/dist/judging.js +3 -0
- package/dist/judging.turn.js +8 -8
- package/dist/judging.types.d.ts +5 -0
- package/dist/judging.words.d.ts +1 -0
- package/dist/judging.words.js +2 -2
- package/dist/surfaces/budo.d.ts +2 -0
- package/dist/surfaces/budo.js +17 -1
- package/dist/turn.kit.d.ts +6 -1
- package/dist/turn.kit.js +6 -2
- package/dist/turn.markup.js +1 -1
- package/dist/turn.model.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ All notable changes to this project are written here. The format follows
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.18.1] - 2026-10-20
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **The seats show their number, and the names are in the chart.** By default every seat of a judging assignment is a large round numbered badge (the chart's own), in 2D and in 3D, and the chart beside it is the key (`chart: "auto"` now always draws it, unless `seatNames` is on and every name fits). Putting the names on the seats is the new option `seatNames: true` (off by default), which sets them in the chart's own face and weight, scaled evenly, in the biggest size that fits and never below a readable floor, and falls back to the number alone when a name does not fit. The demo has a "Names on the seats too" box (`seatnames=1`).
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **Nothing is squeezed or stretched in a judging picture.** The 3D badges, cards and the mat's sign stood upright, so a tipped view foreshortened them and their type looked squeezed: they now lean back to face the eye squarely (`Plane.lean`, turned and never stretched), at every viewpoint. A test measures each one's shape on the screen against its own, and that no text anywhere has `textLength`, `lengthAdjust` or a two-number `scale`.
|
|
19
|
+
- **The tapes of a judo area were hairlines.** The kata area's centre and starting tapes (5 cm by 50 cm) and a contest area's two start tapes, and the sumo's shikiri-sen, are drawn at least 0.75 % of the plan's width across (10.5 cm on a kata area's 14 m, about 4 pixels on a plan 560 pixels across) unless `exactLines: true`, which draws the rule's 5 cm. The floor is a readability aid, not a measurement: a tape's length is always the rule's, and it is widened about its middle. In 3D (the same floor) and in tournament layouts too.
|
|
20
|
+
|
|
10
21
|
## [0.18.0] - 2026-10-20
|
|
11
22
|
|
|
12
23
|
### Added
|
package/README.md
CHANGED
|
@@ -550,9 +550,9 @@ The [demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/halls.html) turns
|
|
|
550
550
|
<p align="center">
|
|
551
551
|
<picture>
|
|
552
552
|
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/judging-3d-desk-dark.webp">
|
|
553
|
-
<img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/judging-3d-desk-light.webp" alt="A kata area's judges' table in 3D,
|
|
553
|
+
<img src="https://raw.githubusercontent.com/johnmorrisdotca/kyogijo-stadiums/main/docs/images/judging-3d-desk-light.webp" alt="A kata area's judges' table in 3D, numbered badges, and the chart." width="640">
|
|
554
554
|
</picture>
|
|
555
|
-
<br><em>Judges in 3D.</em>
|
|
555
|
+
<br><em>Judges, in 3D.</em>
|
|
556
556
|
</p>
|
|
557
557
|
|
|
558
558
|
|
package/dist/judging.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface JudgingSetting {
|
|
|
28
28
|
orientation: "landscape" | "portrait";
|
|
29
29
|
focus: "mat" | "bench";
|
|
30
30
|
width: number;
|
|
31
|
+
/** Whether the names are on the seats (`seatNames`). */
|
|
32
|
+
seatNames: boolean;
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
33
35
|
* Hold the options to what they may be and put each official in a seat.
|
package/dist/judging.draw.js
CHANGED
|
@@ -42,7 +42,7 @@ export function judgingText(setting, options, chart) {
|
|
|
42
42
|
const mats = setting.total === 1 ? words.say.matsOne : fill(words.say.matsMany, { n: setting.total });
|
|
43
43
|
const title = options.title ?? fill(words.say.title, { style, mats });
|
|
44
44
|
const sentences = setting.mats.map((m) => fill(words.say.mat, { label: m.label, seats: m.seats.map((a) => seatSentence(a, setting)).join(words.say.join) }));
|
|
45
|
-
const description = options.description ?? [...sentences, chart ? words.say.names : words.say.chartOnly].join(setting.language === "ja" ? "" : " ");
|
|
45
|
+
const description = options.description ?? [...sentences, chart && setting.seatNames ? words.say.names : words.say.chartOnly].join(setting.language === "ja" ? "" : " ");
|
|
46
46
|
return { title, description };
|
|
47
47
|
}
|
|
48
48
|
const metricsOf = (width) => {
|
|
@@ -126,7 +126,8 @@ function planMarkup(mat, surface, geom, region, box, m, setting, clip) {
|
|
|
126
126
|
const r = clamp(cw * 0.2, 6 * m.k, 14 * m.k);
|
|
127
127
|
return { cw, ch, pad, r, width: cw - 2 * pad, height: ch - 2 * pad - r, max: Math.min(cw * 0.3, 26 * m.k) };
|
|
128
128
|
});
|
|
129
|
-
|
|
129
|
+
// The names are on the seats only when asked for; otherwise every seat is its number, and the chart is the key.
|
|
130
|
+
const fitted = setting.seatNames ? fitSeats(mat.seats, boxes, { min: m.cardMin, idLabel: words.chart.idLabel }) : mat.seats.map(() => undefined);
|
|
130
131
|
// A seat outside the picture (the referee in a close-up of the bench) has its name in the chart only: it does not fit.
|
|
131
132
|
const shown = mat.seats.map((a) => seatIn(geom.places[a.seat.n - 1], region));
|
|
132
133
|
const fits = mat.seats.map((a, i) => !a.assigned || (shown[i] && fitted[i] !== undefined));
|
|
@@ -140,7 +141,7 @@ function planMarkup(mat, surface, geom, region, box, m, setting, clip) {
|
|
|
140
141
|
if (fit !== undefined)
|
|
141
142
|
g += card(PLAN_CARD, fit, a.seat.n, X(place.card.x0), Y(place.card.y0), b.cw, b.ch, b.pad, b.r, 3 * m.k);
|
|
142
143
|
else {
|
|
143
|
-
const br = clamp(Math.min(b.cw, 1.25 * s) * 0.
|
|
144
|
+
const br = clamp(Math.min(b.cw, 1.25 * s) * 0.42, 10 * m.k, 30 * m.k);
|
|
144
145
|
g += badge(PLAN_CARD, X((place.chair.x0 + place.chair.x1) / 2), Y((place.chair.y0 + place.chair.y1) / 2), br, a.seat.n, a.assigned);
|
|
145
146
|
}
|
|
146
147
|
markup += el("g", { class: "ky-ju-seat", "data-mat": mat.n, "data-seat": a.seat.id, "data-card": fit === undefined ? "no" : "yes" }, g);
|
|
@@ -180,8 +181,9 @@ export function judgingDrawing(options, mode = "full") {
|
|
|
180
181
|
return setting.mats.flatMap((mat, i) => planMarkup(mat, surfaces[i], geom, region, { x: 0, y: 0, ...box }, m, setting, "t").fits);
|
|
181
182
|
};
|
|
182
183
|
let chart = mode === "chart" || setting.chart === "always";
|
|
184
|
+
// `auto`: the chart is the key to the numbers, so it is drawn unless the names are on the seats and every one fits there.
|
|
183
185
|
if (mode === "full" && setting.chart === "auto")
|
|
184
|
-
chart = !trial(false).every(Boolean);
|
|
186
|
+
chart = !setting.seatNames || !trial(false).every(Boolean);
|
|
185
187
|
if (mode === "full" && setting.chart === "never")
|
|
186
188
|
chart = false;
|
|
187
189
|
const cardsFit = mode === "chart" ? [] : trial(chart);
|
package/dist/judging.js
CHANGED
|
@@ -85,6 +85,8 @@ export function judgingSetting(options) {
|
|
|
85
85
|
});
|
|
86
86
|
if (options.sideJudges !== undefined && typeof options.sideJudges !== "boolean")
|
|
87
87
|
throw new TypeError("sideJudges is true or false");
|
|
88
|
+
if (options.seatNames !== undefined && typeof options.seatNames !== "boolean")
|
|
89
|
+
throw new TypeError("seatNames is true or false");
|
|
88
90
|
// The seats that are drawn: every one that is not optional, and an optional one when asked for or when a mat names it.
|
|
89
91
|
const shown = seats.filter((seat) => seat.optional !== true || options.sideJudges === true || given.some((m) => m.has(seat.id)));
|
|
90
92
|
const all = options.mats.map((mat, i) => ({ n: i + 1, label: matLabel(mat, i + 1, language), seats: shown.map((seat) => given[i].get(seat.id) ?? { seat, name: "", id: "", assigned: false }) }));
|
|
@@ -106,6 +108,7 @@ export function judgingSetting(options) {
|
|
|
106
108
|
orientation: oneOf(options.orientation, ["landscape", "portrait"], "landscape", "orientation"),
|
|
107
109
|
focus: oneOf(options.focus, ["mat", "bench"], "mat", "focus"),
|
|
108
110
|
width,
|
|
111
|
+
seatNames: options.seatNames === true,
|
|
109
112
|
};
|
|
110
113
|
}
|
|
111
114
|
const plans = new Map();
|
package/dist/judging.turn.js
CHANGED
|
@@ -89,16 +89,16 @@ export function buildJudgingTurn(options, settings = {}) {
|
|
|
89
89
|
const base = atTable ? tableTop : ft(H.post);
|
|
90
90
|
if (!atTable)
|
|
91
91
|
stands.push(...pole(`m${mat.n}-post-${a.seat.n}`, "pole", cx, cy, 0, base, ft(0.06), "u-pole"));
|
|
92
|
-
const fit = fitted[si];
|
|
92
|
+
const fit = setting.seatNames ? fitted[si] : undefined;
|
|
93
93
|
if (fit !== undefined) {
|
|
94
94
|
const p = plane(`m${mat.n}-card-${a.seat.n}`, "card", [cx - b.w / 2, cy, base + cardH], [b.w, 0, 0], [0, 0, -cardH], card(TURN_CARD, fit, a.seat.n, 0, 0, b.w, cardH, b.pad, b.r, 0.1));
|
|
95
|
-
cards.push({ ...p, billboard: true });
|
|
95
|
+
cards.push({ ...p, billboard: true, lean: true });
|
|
96
96
|
}
|
|
97
97
|
else {
|
|
98
|
-
//
|
|
99
|
-
const br = 0.
|
|
98
|
+
// The names are not on the seats, or this one does not fit a card: the seat's number alone, on a round badge like the chart's, with the name in the chart.
|
|
99
|
+
const br = 0.9;
|
|
100
100
|
const p = plane(`m${mat.n}-badge-${a.seat.n}`, "card", [cx - br, cy, base + 2 * br], [2 * br, 0, 0], [0, 0, -2 * br], badge(TURN_CARD, br, br, br * 0.92, a.seat.n, a.assigned));
|
|
101
|
-
cards.push({ ...p, billboard: true });
|
|
101
|
+
cards.push({ ...p, billboard: true, lean: true });
|
|
102
102
|
}
|
|
103
103
|
});
|
|
104
104
|
// The mat's name on a sign over the whole table, on a pole at each end of it.
|
|
@@ -111,7 +111,7 @@ export function buildJudgingTurn(options, settings = {}) {
|
|
|
111
111
|
const lines = text?.name ?? [mat.label];
|
|
112
112
|
const size = text?.nameSize ?? signH * 0.3;
|
|
113
113
|
const label = lines.map((line, i) => `<text class="u-sign-text" x="${n(signW / 2)}" y="${n(signH / 2 + (i - (lines.length - 1) / 2) * size * 1.1 + size * 0.34)}" font-size="${n(size)}">${line.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")}</text>`).join("");
|
|
114
|
-
const sign = { ...plane(`m${mat.n}-sign`, "sign", [midX - signW / 2, midY, signBottom + signH], [signW, 0, 0], [0, 0, -signH], `<rect class="u-sign" width="${n(signW)}" height="${n(signH)}" rx="${n(signH * 0.12)}"/>${label}`), billboard: true };
|
|
114
|
+
const sign = { ...plane(`m${mat.n}-sign`, "sign", [midX - signW / 2, midY, signBottom + signH], [signW, 0, 0], [0, 0, -signH], `<rect class="u-sign" width="${n(signW)}" height="${n(signH)}" rx="${n(signH * 0.12)}"/>${label}`), billboard: true, lean: true };
|
|
115
115
|
const poles = [table.x0, table.x1].flatMap((px, k) => pole(`m${mat.n}-sign-pole-${k + 1}`, "pole", ft(px), midY, tableTop, signBottom + signH * 0.5, ft(0.06), "u-pole"));
|
|
116
116
|
items.push({ id: `m${mat.n}-furniture`, kind: "officials", at: [midX, midY], height: tableTop, planes: furniture });
|
|
117
117
|
items.push({ id: `m${mat.n}-judges`, kind: "judges", at: [midX, midY], height: tableTop + cardH, planes: [...cards, ...stands, ...poles, sign] });
|
|
@@ -130,8 +130,8 @@ export function buildJudgingTurn(options, settings = {}) {
|
|
|
130
130
|
language,
|
|
131
131
|
options: surfaceLike,
|
|
132
132
|
title,
|
|
133
|
-
description: `${description}${language === "ja" ? "" : " "}${words.turn.standing}`,
|
|
134
|
-
standing: words.turn.standing,
|
|
133
|
+
description: `${description}${language === "ja" ? "" : " "}${setting.seatNames ? words.turn.standing : words.turn.standingBadges}`,
|
|
134
|
+
standing: setting.seatNames ? words.turn.standing : words.turn.standingBadges,
|
|
135
135
|
floor,
|
|
136
136
|
viewBox: { x: vx, y: vy, w: vw, h: vh },
|
|
137
137
|
scale,
|
package/dist/judging.types.d.ts
CHANGED
|
@@ -28,6 +28,11 @@ export interface JudgingOptions {
|
|
|
28
28
|
chart?: "auto" | "always" | "never";
|
|
29
29
|
/** Shiai: draw the two side judges' seats too (the IJF's rules for an event with no CARE system use them; its other events have none), as not assigned where nobody is named. A seat a mat names is drawn whatever this says. Off by default. */
|
|
30
30
|
sideJudges?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Put the names on the seats as well as in the chart. Off by default: a seat then shows its number alone, on the round badge the chart's rows have, and the chart is the key. On, a name is set in the chart's own face, scaled
|
|
33
|
+
* evenly (never stretched or condensed), in the biggest size that fits its card and no smaller than a readable one; a seat whose name does not fit shows its number alone.
|
|
34
|
+
*/
|
|
35
|
+
seatNames?: boolean;
|
|
31
36
|
/** Where the chart is laid: beside the plan (`landscape`, the default) or under it (`portrait`). */
|
|
32
37
|
orientation?: "landscape" | "portrait";
|
|
33
38
|
/** What the plan shows: the whole `mat` (the default) or the judges' `bench`, close up, so that the names in the seats are as large as they can be. */
|
package/dist/judging.words.d.ts
CHANGED
package/dist/judging.words.js
CHANGED
|
@@ -16,7 +16,7 @@ export const JUDGING_WORDS = {
|
|
|
16
16
|
join: "; ",
|
|
17
17
|
},
|
|
18
18
|
table: { caption: "{title}", mat: "Mat", seat: "Seat", name: "Name", id: "ID" },
|
|
19
|
-
turn: { standing: "A name card stands at each judge's seat, turned to face the eye, and the name of each mat stands over its judges' table.", board: "Mat" },
|
|
19
|
+
turn: { standing: "A name card stands at each judge's seat, turned to face the eye, and the name of each mat stands over its judges' table.", standingBadges: "A numbered badge stands at each judge's seat, leaning back to face the eye, and the name of each mat stands over its judges' table.", board: "Mat" },
|
|
20
20
|
},
|
|
21
21
|
ja: {
|
|
22
22
|
mat: "第{n}試合場",
|
|
@@ -35,6 +35,6 @@ export const JUDGING_WORDS = {
|
|
|
35
35
|
join: "、",
|
|
36
36
|
},
|
|
37
37
|
table: { caption: "{title}", mat: "試合場", seat: "席", name: "氏名", id: "ID" },
|
|
38
|
-
turn: { standing: "各審判員の席に名札を置き、見ている方向に向くようにしています。各試合場の名前は審判員席の上に掲げています。", board: "試合場" },
|
|
38
|
+
turn: { standing: "各審判員の席に名札を置き、見ている方向に向くようにしています。各試合場の名前は審判員席の上に掲げています。", standingBadges: "各審判員の席に番号の丸い札を置き、見ている方向に向くようにしています。各試合場の名前は審判員席の上に掲げています。", board: "試合場" },
|
|
39
39
|
},
|
|
40
40
|
};
|
package/dist/surfaces/budo.d.ts
CHANGED
|
@@ -3,4 +3,6 @@ import { type BuildOptions, type Surface } from "./surface.ts";
|
|
|
3
3
|
/** Feet in a metre. */
|
|
4
4
|
export declare const K: number;
|
|
5
5
|
/** The surface of the plan lying wide. `margin` is the ground round what is drawn, in feet. */
|
|
6
|
+
/** The least width a tape is drawn at, as a share of the plan's width (0.75 %: about 4 pixels on a plan 560 pixels across), unless `exactLines` is set. */
|
|
7
|
+
export declare const TAPE_LEAST = 0.0075;
|
|
6
8
|
export declare function budoSurface(o: BuildOptions, plan: BudoPlan, margin: number): Surface;
|
package/dist/surfaces/budo.js
CHANGED
|
@@ -14,6 +14,18 @@ function rangeOf(r, ox, oy) {
|
|
|
14
14
|
return "left" in r ? atEnds((end) => feetBox(end === "left" ? r.left : r.right, ox, oy)) : feetBox(r, ox, oy);
|
|
15
15
|
}
|
|
16
16
|
/** The surface of the plan lying wide. `margin` is the ground round what is drawn, in feet. */
|
|
17
|
+
/** The least width a tape is drawn at, as a share of the plan's width (0.75 %: about 4 pixels on a plan 560 pixels across), unless `exactLines` is set. */
|
|
18
|
+
export const TAPE_LEAST = 0.0075;
|
|
19
|
+
/** A bar widened across its shorter side to at least `least` metres, evenly about its middle; a bar that is wide enough is the same bar. */
|
|
20
|
+
function widen(bar, least) {
|
|
21
|
+
const w = bar.x1 - bar.x0;
|
|
22
|
+
const h = bar.y1 - bar.y0;
|
|
23
|
+
if (Math.min(w, h) >= least)
|
|
24
|
+
return bar;
|
|
25
|
+
if (w < h)
|
|
26
|
+
return { ...bar, x0: (bar.x0 + bar.x1) / 2 - least / 2, x1: (bar.x0 + bar.x1) / 2 + least / 2 };
|
|
27
|
+
return { ...bar, y0: (bar.y0 + bar.y1) / 2 - least / 2, y1: (bar.y0 + bar.y1) / 2 + least / 2 };
|
|
28
|
+
}
|
|
17
29
|
export function budoSurface(o, plan, margin) {
|
|
18
30
|
const ox = plan.box.x0;
|
|
19
31
|
const oy = plan.box.y0;
|
|
@@ -81,7 +93,11 @@ export function budoSurface(o, plan, margin) {
|
|
|
81
93
|
const thick = o.exact ? plan.lineWidth : plan.drawnLine;
|
|
82
94
|
// The seams are the plan's finest lines: left out of a drawing that asks for the simple detail.
|
|
83
95
|
const shown = full ? plan : { ...plan, shapes: plan.shapes.filter((s) => s.feature !== "seams" && s.feature !== "boards") };
|
|
84
|
-
|
|
96
|
+
// A tape is 5 cm to 6 cm wide: at the size of a drawing that is a hairline. Unless the lines are asked for at their true widths (`exactLines`), a tape is drawn at least `TAPE_LEAST` of the plan's width across (a readability floor,
|
|
97
|
+
// not a measurement: its length is always the rule's), widened evenly about its middle.
|
|
98
|
+
const floor = (plan.box.x1 - plan.box.x0) * TAPE_LEAST;
|
|
99
|
+
const taped = o.exact ? shown : { ...shown, shapes: shown.shapes.map((s) => (s.kind === "bar" ? widen(s, floor) : s)) };
|
|
100
|
+
const lines = group(strokes(taped, o) + marks(taped, o), "ky-bud-lines");
|
|
85
101
|
const W = (plan.box.x1 - plan.box.x0) * K;
|
|
86
102
|
const H = (plan.box.y1 - plan.box.y0) * K;
|
|
87
103
|
const cx = (plan.centre.x - ox) * K;
|
package/dist/turn.kit.d.ts
CHANGED
|
@@ -16,6 +16,11 @@ export interface Plane {
|
|
|
16
16
|
* place as it lies at a yaw of 0; `v` must be vertical.
|
|
17
17
|
*/
|
|
18
18
|
billboard?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* A billboard that also leans back to face the eye squarely, as the pitch tips the view: a card that stands upright is seen foreshortened from above, and its type looks squeezed, so a plane that carries type leans
|
|
21
|
+
* till its face is square to the eye. It is turned only (never stretched): its width and height stay what they are. At an overhead view it lies flat. `v` must be vertical, as for a billboard.
|
|
22
|
+
*/
|
|
23
|
+
lean?: boolean;
|
|
19
24
|
/**
|
|
20
25
|
* A wall round the surface that gives way when it is between the eye and what it surrounds: the horizontal direction (x, y, a unit vector) that points from the surface out through the
|
|
21
26
|
* wall. The nearer the eye is to that side, and the lower it is, the more the wall is cut away (see `fadeOf`), so that the court is never hidden behind its own glass.
|
|
@@ -56,7 +61,7 @@ export declare const cross: (a: V3, b: V3) => V3;
|
|
|
56
61
|
/** A number for markup: millimetres of a foot, no more. */
|
|
57
62
|
export declare const n: (value: number) => string;
|
|
58
63
|
/** A billboard turned to face an eye whose view is turned `yaw` degrees: its width runs along the screen's x, which in the scene is (cos yaw, -sin yaw, 0). */
|
|
59
|
-
export declare function facing(p: Plane, yaw: number): Plane;
|
|
64
|
+
export declare function facing(p: Plane, yaw: number, pitch?: number): Plane;
|
|
60
65
|
/** A plane. */
|
|
61
66
|
export declare const plane: (id: string, kind: PartKind, o: V3, u: V3, v: V3, svg: string) => Plane;
|
|
62
67
|
/**
|
package/dist/turn.kit.js
CHANGED
|
@@ -18,14 +18,18 @@ export const cross = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] *
|
|
|
18
18
|
/** A number for markup: millimetres of a foot, no more. */
|
|
19
19
|
export const n = (value) => String(Math.round(value * 1000) / 1000);
|
|
20
20
|
/** A billboard turned to face an eye whose view is turned `yaw` degrees: its width runs along the screen's x, which in the scene is (cos yaw, -sin yaw, 0). */
|
|
21
|
-
export function facing(p, yaw) {
|
|
21
|
+
export function facing(p, yaw, pitch = 0) {
|
|
22
22
|
if (p.billboard !== true)
|
|
23
23
|
return p;
|
|
24
24
|
const [w, h] = size(p);
|
|
25
25
|
const a = (yaw * Math.PI) / 180;
|
|
26
26
|
const u = [w * Math.cos(a), -w * Math.sin(a), 0];
|
|
27
27
|
const middle = [p.o[0] + (p.u[0] + p.v[0]) / 2, p.o[1] + (p.u[1] + p.v[1]) / 2, p.o[2] + (p.u[2] + p.v[2]) / 2];
|
|
28
|
-
|
|
28
|
+
// A plane that leans has its height along the screen's vertical, which at a pitch tips back from the vertical: (cos p sin yaw, cos p cos yaw, -sin p) is the way down the screen, in the scene.
|
|
29
|
+
const b = (pitch * Math.PI) / 180;
|
|
30
|
+
const down = p.lean === true ? [Math.cos(b) * Math.sin(a), Math.cos(b) * Math.cos(a), -Math.sin(b)] : [0, 0, -1];
|
|
31
|
+
const sign = p.v[2] < 0 ? 1 : -1;
|
|
32
|
+
const v = [down[0] * h * sign, down[1] * h * sign, down[2] * h * sign];
|
|
29
33
|
return { ...p, o: [middle[0] - (u[0] + v[0]) / 2, middle[1] - (u[1] + v[1]) / 2, middle[2] - (u[2] + v[2]) / 2], u, v };
|
|
30
34
|
}
|
|
31
35
|
/** A plane. */
|
package/dist/turn.markup.js
CHANGED
|
@@ -27,7 +27,7 @@ export function layoutOf(model, view, width, height, lens) {
|
|
|
27
27
|
const centre = model.centre;
|
|
28
28
|
const floor = { id: "floor", kind: "ground", o: [model.viewBox.x, model.viewBox.y, model.uprights.lift], u: [model.viewBox.w, 0, 0], v: [0, model.viewBox.h, 0], svg: "" };
|
|
29
29
|
// A billboard faces the eye at this view.
|
|
30
|
-
const planes = model.planes.map((p) => facing(p, view.yaw));
|
|
30
|
+
const planes = model.planes.map((p) => facing(p, view.yaw, view.pitch));
|
|
31
31
|
const transforms = [floor, ...planes].map((p) => matrix3d(multiply(camera, planeMatrix(p, s, centre))));
|
|
32
32
|
// Far to near: a plane's depth is that of its middle. The ground of a hall is under everything. The floor is next, unless it stands raised on something (a table tennis table's top, on its legs): then it is
|
|
33
33
|
// sorted with the rest, by its middle, so that the legs and the skirt on the far side are drawn under it and the ones on the near side over it, and nothing under the top shows through it.
|
package/dist/turn.model.js
CHANGED
|
@@ -42,7 +42,7 @@ export function sceneBox(planes, area, lift, centre, scale) {
|
|
|
42
42
|
let top = lift;
|
|
43
43
|
for (const p of planes) {
|
|
44
44
|
// A billboard turns about its middle: whichever way it faces, it lies within a circle as wide as it is.
|
|
45
|
-
const reach = p.billboard === true ? Math.hypot(...p.u) / 2 : 0;
|
|
45
|
+
const reach = p.billboard === true ? (p.lean === true ? Math.hypot(...p.u, ...p.v) / 2 : Math.hypot(...p.u) / 2) : 0;
|
|
46
46
|
for (const [a, b] of [[0, 0], [1, 0], [0, 1], [1, 1]]) {
|
|
47
47
|
const point = [p.o[0] + a * p.u[0] + b * p.v[0], p.o[1] + a * p.u[1] + b * p.v[1], p.o[2] + a * p.u[2] + b * p.v[2]];
|
|
48
48
|
if (reach > 0) {
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** The package's version: the same as package.json's, and a test holds them to each other. */
|
|
2
|
-
export declare const VERSION = "0.18.
|
|
2
|
+
export declare const VERSION = "0.18.1";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** The package's version: the same as package.json's, and a test holds them to each other. */
|
|
2
|
-
export const VERSION = "0.18.
|
|
2
|
+
export const VERSION = "0.18.1";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnmorrisdotca/kyogijo-stadiums",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Kyogijo Stadiums 競技場, 39 sports' surfaces as SVG, drawn to scale from the rulebooks: NFL, CFL and IFAF football fields, an NHL rink, an NBA court, MLB ballparks, soccer pitches, curling sheets, athletics stadiums, speed skating, short track and figure skating rinks, swimming pools, water polo fields, diving wells, artistic swimming areas, and the courts and fields of tennis, volleyball (indoor, beach and sitting), badminton, pickleball, table tennis, padel, squash, racquetball, sepak takraw, teqball, roundnet, netball, lacrosse, rugby, Australian football, Gaelic games, cricket, field hockey and handball, and the budo of judo, karate, kendo, sumo and kyudo with a dojo of tatami laid as real units, with each team's name and logo on its side and the score in the middle, stands and a crowd, a pageant of flags, and a 3D view. Light and dark, accessible, a compact badge for lists, a helper for React and a custom element. Zero dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sports",
|