@johnmorrisdotca/uchusen-rockets 0.19.0 → 0.19.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 +7 -0
- package/README.md +2 -2
- package/dist/element/probe.d.ts +15 -0
- package/dist/element/probe.js +46 -0
- package/dist/element/scene.js +3 -9
- package/dist/element.d.ts +2 -0
- package/dist/element.js +16 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.19.1] - 2026-11-12
|
|
6
|
+
|
|
7
|
+
- **The concept build budget holds work on CI** (MODULES-1134): `every concept builds inside the budget` failed on a shared runner at 3059 ms against 3000. On the Mac it still holds the time budget. Under `CI` it holds what does not vary, the pieces and triangles each concept builds (within 15 per cent), plus a cap of five times the budget on the time.
|
|
8
|
+
|
|
9
|
+
- **The WebGL probe runs once per page, and only when a view is about to draw** (MODULES-1102): every element asked at connect, and every 3D view made another test context for the name of the renderer, each a few seconds of the main thread in software drawing, and in headless WebKit a load now and then stalled for minutes. The page now makes one test context, when the first element comes on screen, answers both questions from it (does WebGL work, does it draw in software) and lets it go at once. The hero says `3d` where WebGL works and `fallback` where it does not, as before; an element far off screen no longer asks, so it says `picture` until it nears. A test holds the count at one.
|
|
10
|
+
- **The played-timeline freeze test counts the graphics card at rest** (MODULES-853). "Grid fins, legs and flaps" read 210 buffers before its forty rounds and 213 after in about one run in seven in the CI image. The three were the pad's smoke, one instanced draw kept for the flight, which reaches the card a frame or two after the first draw at ignition: a slow run moved on before it did, and a later round made it. The warm-up now holds at ignition until the count stands still, and both counts wait for the camera's glide and the left-behind craft to end. The allowance is unchanged; 15 of 15 in the CI image.
|
|
11
|
+
|
|
5
12
|
## [0.19.0] - 2026-11-11
|
|
6
13
|
|
|
7
14
|
- **A tap on the build bar's Pause is no longer lost in Safari** (MODULES-1066): the playing bar redrew the button's icon and word every tenth of a second, and WebKit sends no click when the node a press began on has been replaced before the finger lifts. The bar now leaves a button, a text or the stills alone when a redraw would not change them. A test holds a press across redraws.
|
package/README.md
CHANGED
|
@@ -442,7 +442,7 @@ variantOf("starship", "2019").facts.diameter.value; // null
|
|
|
442
442
|
|
|
443
443
|
## How to cite
|
|
444
444
|
|
|
445
|
-
> Morris, J. (2026). *Uchusen Rockets* (version 0.19.
|
|
445
|
+
> Morris, J. (2026). *Uchusen Rockets* (version 0.19.1) [Computer software]. https://github.com/johnmorrisdotca/uchusen-rockets
|
|
446
446
|
|
|
447
447
|
GitHub's "Cite this repository" button reads [CITATION.cff](CITATION.cff) and gives BibTeX and APA. Cite the version you used (`npm ls @johnmorrisdotca/uchusen-rockets`), and credit the people behind the models and the numbers: [NOTICE.md](NOTICE.md) says who made each one and under what terms.
|
|
448
448
|
|
|
@@ -494,7 +494,7 @@ pnpm test:usage # every way to use it (CDN, Vite, webpack, React, Vue, Svelt
|
|
|
494
494
|
pnpm test:usage # every way to use it (CDN, Vite, webpack, React, Vue, Svelte, Angular, iframe, TypeScript, Node), built and opened from the package as npm packs it
|
|
495
495
|
pnpm docs:check # the reference site's checks: every export documented, every example run
|
|
496
496
|
pnpm test:demo # the demo in a real browser
|
|
497
|
-
pnpm dev # the demo on http://127.0.0.1:
|
|
497
|
+
pnpm dev # the demo on http://127.0.0.1:8275/uchusen-rockets/
|
|
498
498
|
pnpm screenshots # the pictures in docs/images
|
|
499
499
|
```
|
|
500
500
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** What one look at the browser's WebGL said. */
|
|
2
|
+
export interface GlProbe {
|
|
3
|
+
/** Whether a WebGL context can be made at all. */
|
|
4
|
+
works: boolean;
|
|
5
|
+
/** Whether the context draws in software (SwiftShader, llvmpipe, softpipe), where smooth edges cost a frame. */
|
|
6
|
+
software: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** How many test contexts this page has made: 1 once the answer is known, 0 before it is asked for. */
|
|
9
|
+
export declare function probeCount(): number;
|
|
10
|
+
/**
|
|
11
|
+
* Says whether WebGL works here and whether it draws in software. The first call makes one test context and releases it at once; every later call, from any element or viewer, gets the same answer without making another.
|
|
12
|
+
*
|
|
13
|
+
* @returns The kept answer.
|
|
14
|
+
*/
|
|
15
|
+
export declare function glProbe(): GlProbe;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// What the browser's WebGL is, asked once per page. A test context in software drawing takes seconds of the main thread, so the answer is kept for every element and every viewer on the page, and the context is let go the moment it has answered.
|
|
2
|
+
let answer = null;
|
|
3
|
+
let asked = 0;
|
|
4
|
+
/** How many test contexts this page has made: 1 once the answer is known, 0 before it is asked for. */
|
|
5
|
+
export function probeCount() {
|
|
6
|
+
return asked;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Says whether WebGL works here and whether it draws in software. The first call makes one test context and releases it at once; every later call, from any element or viewer, gets the same answer without making another.
|
|
10
|
+
*
|
|
11
|
+
* @returns The kept answer.
|
|
12
|
+
*/
|
|
13
|
+
export function glProbe() {
|
|
14
|
+
if (answer)
|
|
15
|
+
return answer;
|
|
16
|
+
asked++;
|
|
17
|
+
let works = false;
|
|
18
|
+
let software = false;
|
|
19
|
+
let canvas = null;
|
|
20
|
+
try {
|
|
21
|
+
canvas = document.createElement("canvas");
|
|
22
|
+
const two = canvas.getContext("webgl2");
|
|
23
|
+
const gl = two ?? canvas.getContext("webgl");
|
|
24
|
+
works = Boolean(gl);
|
|
25
|
+
if (gl) {
|
|
26
|
+
try {
|
|
27
|
+
const info = gl.getExtension("WEBGL_debug_renderer_info");
|
|
28
|
+
software = /swiftshader|llvmpipe|softpipe/i.test(info ? String(gl.getParameter(info.UNMASKED_RENDERER_WEBGL)) : "");
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// The name is only a hint for smooth edges.
|
|
32
|
+
}
|
|
33
|
+
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
works = false;
|
|
38
|
+
}
|
|
39
|
+
if (canvas) {
|
|
40
|
+
// A canvas of no size lets its drawing buffer go with it.
|
|
41
|
+
canvas.width = 0;
|
|
42
|
+
canvas.height = 0;
|
|
43
|
+
}
|
|
44
|
+
answer = { works, software };
|
|
45
|
+
return answer;
|
|
46
|
+
}
|
package/dist/element/scene.js
CHANGED
|
@@ -31,6 +31,7 @@ import { placeNameLabels } from "./namePlace.js";
|
|
|
31
31
|
import { plinthGroup } from "./plinth.js";
|
|
32
32
|
import { PartPlumes, PlumeKit } from "./plumes.js";
|
|
33
33
|
import { ThrusterLayer } from "./thrusterLayer.js";
|
|
34
|
+
import { glProbe } from "./probe.js";
|
|
34
35
|
import { craftJets, endQuads } from "../thrusters.js";
|
|
35
36
|
/** The ships whose lamps turn the sun down in the Lit look. Kit modules stay in daylight. */
|
|
36
37
|
const NIGHT_SHIPS = new Set(["fleet-ship-01", "fleet-kyotori", "fleet-drone", "fleet-ship-03", "fleet-ship-ariake", "fleet-02", "fleet-02-original", "fleet-kaminari", "fleet-emiake", "fleet-romaryu"]);
|
|
@@ -55,15 +56,8 @@ const COMPILE_MS = 4000;
|
|
|
55
56
|
/** A change of craft, section or Fit eases the camera for this long, even in scale. */
|
|
56
57
|
const SIZE_EASE_MS = 800;
|
|
57
58
|
const autoRotateOf = (spin) => (spin.seconds <= 0 ? 0 : (60 / spin.seconds) * (spin.way === "cw" ? -1 : 1));
|
|
58
|
-
/** A real graphics card keeps smooth edges. Software drawing spends the frame on them, and a heavy ship then misses a frame of the wait. */
|
|
59
|
-
const smoothEdges = () =>
|
|
60
|
-
const canvas = document.createElement("canvas");
|
|
61
|
-
const gl = canvas.getContext("webgl2");
|
|
62
|
-
const info = gl?.getExtension("WEBGL_debug_renderer_info");
|
|
63
|
-
const name = info && gl ? String(gl.getParameter(info.UNMASKED_RENDERER_WEBGL)) : "";
|
|
64
|
-
gl?.getExtension("WEBGL_lose_context")?.loseContext();
|
|
65
|
-
return !/swiftshader|llvmpipe|softpipe/i.test(name);
|
|
66
|
-
};
|
|
59
|
+
/** A real graphics card keeps smooth edges. Software drawing spends the frame on them, and a heavy ship then misses a frame of the wait. The page's one WebGL probe says which this is. */
|
|
60
|
+
const smoothEdges = () => !glProbe().software;
|
|
67
61
|
const LINE_VERTEX = /* glsl */ `
|
|
68
62
|
varying vec3 vNormal;
|
|
69
63
|
varying vec2 vUv;
|
package/dist/element.d.ts
CHANGED
|
@@ -65,6 +65,8 @@ export declare class UchusenRocket extends HTMLBase {
|
|
|
65
65
|
private stateNow;
|
|
66
66
|
private shownAt;
|
|
67
67
|
private starting;
|
|
68
|
+
/** Whether this element has asked the page's WebGL answer since it started watching. */
|
|
69
|
+
private glAsked;
|
|
68
70
|
private key;
|
|
69
71
|
private driven;
|
|
70
72
|
private placeFrame;
|
package/dist/element.js
CHANGED
|
@@ -21,6 +21,7 @@ import { ROCKETS, SLOT_NAMES } from "./rockets.js";
|
|
|
21
21
|
import { tiltMode, tiltTurn } from "./tilt.js";
|
|
22
22
|
import { skyQuery } from "./element/solarGroups.js";
|
|
23
23
|
import { Panel } from "./element/panel.js";
|
|
24
|
+
import { glProbe } from "./element/probe.js";
|
|
24
25
|
import { spinOf } from "./spin.js";
|
|
25
26
|
const HTMLBase = typeof HTMLElement === "undefined" ? class {
|
|
26
27
|
} : HTMLElement;
|
|
@@ -94,29 +95,13 @@ function adopt(root, css, holder) {
|
|
|
94
95
|
const rocketSheet = { sheet: null };
|
|
95
96
|
const lineupSheet = { sheet: null };
|
|
96
97
|
const media = (query) => typeof matchMedia === "function" && matchMedia(query).matches;
|
|
97
|
-
|
|
98
|
-
function webglWorks() {
|
|
99
|
-
if (webgl !== null)
|
|
100
|
-
return webgl;
|
|
101
|
-
try {
|
|
102
|
-
const probe = document.createElement("canvas");
|
|
103
|
-
const gl = probe.getContext("webgl2") ?? probe.getContext("webgl");
|
|
104
|
-
webgl = Boolean(gl);
|
|
105
|
-
gl?.getExtension("WEBGL_lose_context")?.loseContext();
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
webgl = false;
|
|
109
|
-
}
|
|
110
|
-
return webgl;
|
|
111
|
-
}
|
|
98
|
+
/** Why the 3D view is not wanted, from what is known without making a WebGL context. Whether WebGL works is asked once, when the first element comes on screen. */
|
|
112
99
|
function whyNot(mode) {
|
|
113
100
|
if (mode === "drawing")
|
|
114
101
|
return "off";
|
|
115
102
|
const connection = navigator.connection;
|
|
116
103
|
if (connection?.saveData || media("(prefers-reduced-data: reduce)"))
|
|
117
104
|
return "reduced-data";
|
|
118
|
-
if (!webglWorks())
|
|
119
|
-
return "no-webgl";
|
|
120
105
|
return "";
|
|
121
106
|
}
|
|
122
107
|
/**
|
|
@@ -154,6 +139,8 @@ export class UchusenRocket extends HTMLBase {
|
|
|
154
139
|
stateNow = "picture";
|
|
155
140
|
shownAt = 0;
|
|
156
141
|
starting = false;
|
|
142
|
+
/** Whether this element has asked the page's WebGL answer since it started watching. */
|
|
143
|
+
glAsked = false;
|
|
157
144
|
key = "";
|
|
158
145
|
driven = null;
|
|
159
146
|
placeFrame = 0;
|
|
@@ -900,7 +887,8 @@ export class UchusenRocket extends HTMLBase {
|
|
|
900
887
|
}
|
|
901
888
|
startWatching() {
|
|
902
889
|
this.watch?.disconnect();
|
|
903
|
-
|
|
890
|
+
let reason = whyNot(this.getAttribute("mode") ?? "auto");
|
|
891
|
+
this.glAsked = false;
|
|
904
892
|
if (reason) {
|
|
905
893
|
this.setState(reason === "off" ? "picture" : "fallback", reason);
|
|
906
894
|
if (reason !== "off")
|
|
@@ -916,6 +904,16 @@ export class UchusenRocket extends HTMLBase {
|
|
|
916
904
|
this.kickFlight();
|
|
917
905
|
if (reason)
|
|
918
906
|
return;
|
|
907
|
+
if (seen.isIntersecting && !this.glAsked) {
|
|
908
|
+
// The probe makes a context, which software drawing takes seconds over: it waits until a view is about to draw, and is made once for the whole page.
|
|
909
|
+
this.glAsked = true;
|
|
910
|
+
if (!glProbe().works) {
|
|
911
|
+
reason = "no-webgl";
|
|
912
|
+
this.setState("fallback", reason);
|
|
913
|
+
this.emit("uchusen-fallback", { reason });
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
919
917
|
if (seen.isIntersecting) {
|
|
920
918
|
this.shownAt ||= performance.now();
|
|
921
919
|
window.clearTimeout(this.releaseTimer);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnmorrisdotca/uchusen-rockets",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.1",
|
|
4
4
|
"description": "Uchusen Rockets 宇宙船: line drawings and 3D views of rockets, as a web component. Every variant with its own sourced sizes, a typed way to assemble a rocket from parts, stack and separate. No logos. three.js is a peer dependency.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rocket",
|