fix(cockpit): the sensor strip follows the view through a zoom step; the pre-load level test checks a worked number; the boundary gate's dependency-asset exemption is tested

This commit is contained in:
Sameh Khamis committed 2026-11-08 19:08:51 -08:00
1 parent 1453794573
commit 63aa45c520
4 files changed
+84 -14

No files matched your search

+4 -2
View File
@@ -1658,14 +1658,16 @@ the satellite's live altitude and the window's shape is still at least as
wide as that swath (`orbitalMaxZoom` in `src/cockpitOrbital.js`); when the
cap falls below the zoom shown, the view steps back to it. Unconfirmed
swaths add no steps, but are still drawn, read out and offered to SENSOR.
The sensor's swath is drawn to scale as a strip along the ground track with
The sensor's swath is drawn to scale as a strip along the ground track (its
width follows the view through a zoom step, not ahead of it) with
a SENSOR SWATH readout, and a SENSOR button zooms to exactly that width when
it lies between 1× and the cap. No strip is drawn for a geostationary view
(no ground track; its sensors image areas, not strips) or a SAR (it images
to one side of nadir, not under it); the readout stays. Tilted off nadir
the strip stays (view and swath widen alike), the readout scales as the swath
readout does (`SENSOR SWATH ≈ 191 KM`), and SENSOR, a nadir framing, is
hidden; tilted past the Earth's limb neither the sensor nor the swath reads. A swath is confirmed only on the operator's, an agency's (ESA's
hidden; tilted past the Earth's limb neither the sensor nor the swath
reads. A swath is confirmed only on the operator's, an agency's (ESA's
eoPortal included) or an academic page for its platform. From
16×, with flat imagery on the globe (not Google 3D tiles) and the view at
nadir, every 2 s the view asks the base imagery layer for the tiles it will
+46
View File
@@ -126,3 +126,49 @@ test('a lifecycle owner cannot acquire a layer panel even through an unused impo
);
await assert.rejects(checkPackageBoundaries(root), /unowned module.*panel/);
});
test('a declared dependency owns the asset files it ships, and only those', async (t) => {
const asset = (pkg) =>
`import url from './node_modules/${pkg}/flags/aa.svg?url&no-inline'; export const value = url;`;
const root = await fixture(t, asset('flags'));
for (const pkg of ['flags', 'flags-extra', 'stray']) {
await mkdir(path.join(root, 'node_modules', pkg, 'flags'), {
recursive: true,
});
await writeFile(
path.join(root, 'node_modules', pkg, 'flags/aa.svg'),
'<svg xmlns="http://www.w3.org/2000/svg"/>',
);
}
await writeFile(
path.join(root, 'package.json'),
JSON.stringify({
name: 'boundary-fixture',
type: 'module',
exports: { './feature': './entry.js' },
dependencies: { flags: '1.0.0' },
}),
);
await writeFile(
path.join(root, 'scripts/package-boundaries.json'),
JSON.stringify({
feature: {
exports: ['./feature'],
modules: ['entry.js'],
external: ['flags'],
},
}),
);
// The entry and the flag it ships.
assert.deepEqual(await checkPackageBoundaries(root), [
{ name: 'feature', exports: 1, modules: 2 },
]);
// An undeclared package's file, and one whose name only starts the same.
for (const pkg of ['stray', 'flags-extra']) {
await writeFile(path.join(root, 'entry.js'), asset(pkg));
await assert.rejects(
checkPackageBoundaries(root),
new RegExp(`unowned module.*node_modules/${pkg}/`),
);
}
});
+6 -1
View File
@@ -631,6 +631,11 @@ export function publishOrbitalView(info, transition = IDLE_TRANSITION) {
: canvasAspect(this.viewer);
const settledFov = fovForZoom(orbitalBaseFovRad(info.altitudeM), state.zoom);
const footprintKm = footprintWidthKm(info.altitudeM, settledFov, aspect);
// The strip is drawn over the picture, so it follows the view as a step
// zooms (republished every frame then), not the step it is heading for.
const shownKm = Number.isFinite(state.verticalFov)
? footprintWidthKm(info.altitudeM, state.verticalFov, aspect)
: footprintKm;
const maxZoom = orbitalMaxZoomFor.call(this, info);
const swathKm = sensorSwathKm(info);
const trackUp = Number.isFinite(info.groundTrackDeg);
@@ -687,7 +692,7 @@ export function publishOrbitalView(info, transition = IDLE_TRANSITION) {
// along, and a SAR images to one side of nadir, not under it.
stripFraction:
trackUp && info.sensor?.type !== 'SAR'
? sensorStripFraction(swathKm, footprintKm)
? sensorStripFraction(swathKm, shownKm)
: null,
zoom: sensorSwathZoom(info.altitudeM, swathKm, aspect, maxZoom),
},
+28 -11
View File
@@ -26,7 +26,6 @@ import {
} from '../cockpitOrbital.js';
import { vesselCockpitInfo } from '../layers/vessels/cockpitSubject.js';
import { prefetchImageryAhead } from './cockpitOrbitalView.js';
import { imageryLevelForView } from '../cockpitOrbital.js';
import { claimPointer, releasePointer } from '../data/inputOwnership.js';
const ISS = Object.freeze({
@@ -1161,6 +1160,27 @@ function imageryGlobe(viewer, { requestImage } = {}) {
const WORLDVIEW_ON_TRACK = Object.freeze({ ...WORLDVIEW, groundTrackDeg: 52 });
test('the sensor strip follows the view through a zoom step, not ahead of it', async (t) => {
const { cockpit, store, clock } = await orbitAt(t, {
satellite: WORLDVIEW,
reducedMotion: false,
});
const strip = () => store.orbital.sensor.stripFraction;
const atOne = strip();
assert.ok(atOne > 0 && atOne < 1);
press(cockpit, '+');
cockpit.update();
near(strip(), atOne, 'the step starts at 1x, and so does the strip');
clock.advance(ORBITAL_ZOOM_TRANSITION_MS / 2);
cockpit.update();
const midway = strip();
clock.advance(ORBITAL_ZOOM_TRANSITION_MS + 1000);
cockpit.update();
const atTwo = strip();
assert.ok(atTwo > atOne * 1.5, 'twice the zoom, about twice the share');
assert.ok(midway > atOne && midway < atTwo, 'part-way through the step');
});
test('from 16x the imagery ahead of the ground track is pre-loaded', async (t) => {
const { cockpit, viewer, store, clock } = await orbitAt(t, {
satellite: WORLDVIEW_ON_TRACK,
@@ -1444,16 +1464,13 @@ test('the pre-load is throttled, at the level Cesium draws, and waits out a tilt
);
assert.equal(request.type, Cesium.RequestType.IMAGERY);
}
// The level Cesium's own selection takes for this view.
const fov = fovForZoom(orbitalBaseFovRad(WORLDVIEW.altitudeM), 16);
const metresPerPixel =
(WORLDVIEW.altitudeM * 2 * Math.tan(fov / 2)) /
viewer.scene.canvas.clientHeight;
const expected = imageryLevelForView(new Cesium.WebMercatorTilingScheme(), {
metresPerPixel,
latitudeDeg: WORLDVIEW.latitude,
});
assert.equal(Number(requests[0].split('/')[0]), expected);
// The level Cesium's own selection takes for this view, worked by hand:
// at 16x from 617 km the 900 px canvas is ~49.5 m a pixel. The ellipsoid
// terrain's level-zero error is 6378137 · 2π / 4 / 130 ≈ 77,067 m; it first
// meets Cesium's screen-space error of 2 px (≤ 99 m) at level 10, 75.3 m.
// A Web Mercator level-zero texel at 30° is 6378137 · 2π · cos 30° / 256
// ≈ 135,566 m, so the imagery level is round(log2(135,566 / 75.3)) = 11.
assert.equal(Number(requests[0].split('/')[0]), 11);
});
test('the TLE age is measured from the time the satellite is shown at', async (t) => {