diff --git a/src/layers/wind/edgeCases.test.mjs b/src/layers/wind/edgeCases.test.mjs index 1b7a0e2ec..2ae1512ae 100644 --- a/src/layers/wind/edgeCases.test.mjs +++ b/src/layers/wind/edgeCases.test.mjs @@ -23,13 +23,13 @@ const options = { validTime: '2026-09-14 18:00 UTC', status: 'Model forecast', }; -/** The Cesium path picks the centre itself; Navara hands in the position. */ -const viaViewer = (snapshot, point) => +/** Inspect either the current map centre or a captured position. */ +const viaEngine = (snapshot, point) => inspectWindAtCenter( snapshot, { camera: { pickEllipsoid: () => point }, - scene: { canvas: { clientWidth: 800, clientHeight: 600 } }, + host: { canvas: { clientWidth: 800, clientHeight: 600 } }, }, options, ); @@ -42,7 +42,7 @@ test('edge:wind: a fixed grid gives the same direction, speed and legend colour // 5 m/s blowing toward the west: the wind comes FROM the east. const snapshot = field(-5, 0); for (const point of [at(10, 20), at(180, 0), at(-180, 0), at(-45.5, -60)]) { - const cesiumReading = viaViewer(snapshot, point); + const cesiumReading = viaEngine(snapshot, point); const navaraReading = viaPosition(snapshot, point); for (const reading of [cesiumReading, navaraReading]) { assert.equal(reading.from, 'E'); @@ -76,7 +76,7 @@ test('edge:wind: an empty or unavailable field is not reported as calm, a real 0 ]; for (const snapshot of empty) { for (const reading of [ - viaViewer(snapshot, point), + viaEngine(snapshot, point), viaPosition(snapshot, point), ]) { assert.equal(reading.speed, undefined, 'no speed is invented'); diff --git a/src/layers/wind/index.js b/src/layers/wind/index.js index 928573057..3d7b659fd 100644 --- a/src/layers/wind/index.js +++ b/src/layers/wind/index.js @@ -82,14 +82,7 @@ export function createWindLayer({ inspectionMarker?.clear(); }; const sample = (position) => { - if (!position && engine && !viewer) { - const canvas = engine.host.canvas; - position = engine.camera.pickEllipsoid({ - x: canvas.clientWidth / 2, - y: canvas.clientHeight / 2, - }); - } - reading = inspectWindAtCenter(manifest, viewer, { + reading = inspectWindAtCenter(manifest, engine, { position, units, overlay, @@ -130,7 +123,6 @@ export function createWindLayer({ rendering?.setOptions?.({ overlay, paused }); inspectionMarker = createWindInspectionMarker({ container: engine.host.container, - viewer, engine, }); if (enabled && manifest && !manifest.unavailable) { @@ -254,7 +246,7 @@ export function createWindLayer({ loading = enabled && needsField; if (enabled && needsField) queueMicrotask(() => { - if (enabled && generation === current) void layer.update(viewer); + if (enabled && generation === current) void layer.update(); }); else if (enabled && manifest && !manifest.unavailable) rendering?.start(); diff --git a/src/layers/wind/index.test.mjs b/src/layers/wind/index.test.mjs index 30ef80423..5ef39e521 100644 --- a/src/layers/wind/index.test.mjs +++ b/src/layers/wind/index.test.mjs @@ -365,8 +365,10 @@ test('sample stays fixed across model, field and unit changes; dismissal and dis feed: { getSnapshot: async ({ model }) => complete(model) }, createNativeRendering: () => rendering, }); - // A Cesium page: the viewer samples, the engine projects the marker. - layer.init(viewer, { engine: createFakeEngine({ container }) }); + const engine = createFakeEngine({ container, canvas: viewer.scene.canvas }); + engine.camera.pickEllipsoid = (point) => viewer.camera.pickEllipsoid(point); + engine.frames.postRender = viewer.scene.postRender; + layer.init(viewer, { engine }); layer.enable(); await layer.update(); layer.setParams({ inspect: true }); diff --git a/src/layers/wind/inspection.js b/src/layers/wind/inspection.js index 3459a1df2..8299ddfe1 100644 --- a/src/layers/wind/inspection.js +++ b/src/layers/wind/inspection.js @@ -45,20 +45,17 @@ export function formatWindSpeed(speed, units = 'km/h') { /** A deliberate snapshot at the map center; does not own pointer input. */ export function inspectWindAtCenter( snapshot, - viewer, + engine, { units, overlay, model, validTime, status, position } = {}, ) { - const scene = viewer?.scene; - const canvas = scene?.canvas; - // The globe's own ellipsoid, else WGS84 (geo's default). - const ellipsoid = scene?.globe?.ellipsoid; + const canvas = engine?.host.canvas; const point = position || (canvas && - viewer.camera?.pickEllipsoid?.( - geo.cartesian2.create(canvas.clientWidth / 2, canvas.clientHeight / 2), - ellipsoid, - )); + engine.camera.pickEllipsoid({ + x: canvas.clientWidth / 2, + y: canvas.clientHeight / 2, + })); const noReading = () => ({ coordinates: msg('No surface reading'), wind: msg('Aim the map center at Earth'), @@ -70,7 +67,7 @@ export function inspectWindAtCenter( ), }); if (!point || !snapshot?.u || !snapshot?.v) return noReading(); - const location = geo.cartographic.fromCartesian(point, ellipsoid); + const location = geo.cartographic.fromCartesian(point); const lon = geo.math.toDegrees(location.longitude); const lat = geo.math.toDegrees(location.latitude); const vector = sampleWind( diff --git a/src/layers/wind/inspection.test.mjs b/src/layers/wind/inspection.test.mjs index a19801228..1161c1d76 100644 --- a/src/layers/wind/inspection.test.mjs +++ b/src/layers/wind/inspection.test.mjs @@ -27,11 +27,11 @@ test('center inspection uses a fixed forecast reading without double-converting values: Float32Array.from([20, 20, 20, 20]), }, }; - const viewer = { + const engine = { camera: { pickEllipsoid: () => geo.cartesian3.fromDegrees(0, 0) }, - scene: { canvas: { clientWidth: 800, clientHeight: 600 } }, + host: { canvas: { clientWidth: 800, clientHeight: 600 } }, }; - const result = inspectWindAtCenter(snapshot, viewer, { + const result = inspectWindAtCenter(snapshot, engine, { overlay: 'temperature', units: 'km/h', model: 'GFS', @@ -42,9 +42,9 @@ test('center inspection uses a fixed forecast reading without double-converting assert.equal(result.scalarValue, '20.0 °C'); assert.equal(result.validTime, 'fixed'); assert.equal(result.coordinates, '0.00°N · 0.00°E'); - viewer.camera.pickEllipsoid = () => null; + engine.camera.pickEllipsoid = () => null; assert.equal( - inspectWindAtCenter(snapshot, viewer).coordinates, + inspectWindAtCenter(snapshot, engine).coordinates, 'No surface reading', ); }); diff --git a/src/layers/wind/inspectionMarker.js b/src/layers/wind/inspectionMarker.js index 7291158bb..028bd7ade 100644 --- a/src/layers/wind/inspectionMarker.js +++ b/src/layers/wind/inspectionMarker.js @@ -9,9 +9,7 @@ import { applicationPinia } from '../../ui/vueRuntime.js'; */ export function createWindInspectionMarker({ container, - viewer, engine, - cesium, store = useWindInspectionStore(applicationPinia()), }) { const owner = {}; @@ -28,45 +26,31 @@ export function createWindInspectionMarker({ removeRender = null; if (shown()) store.publish(null); marker = null; - if (engine) engine.render.request('wind-inspection'); - else viewer?.scene?.requestRender?.(); + engine.render.request('wind-inspection'); }; return { show(position) { clear(); - const scene = viewer?.scene; - const canvas = scene?.canvas ?? engine?.host.canvas; - const frames = scene?.postRender ?? engine?.frames.postRender; + const canvas = engine.host.canvas; + const frames = engine.frames.postRender; if ( !position || !container?.ownerDocument || !frames?.addEventListener || - (!engine?.projector && !scene?.cartesianToCanvasCoordinates) + !engine.projector ) return; present({ owner, container, visible: false, left: 0, top: 0 }); - const occluder = - !engine && - new cesium.EllipsoidalOccluder( - scene.globe?.ellipsoid || cesium.Ellipsoid.WGS84, - viewer.camera.positionWC, - ); const render = () => { if (!shown()) return; - if (occluder) occluder.cameraPosition = viewer.camera.positionWC; - const point = engine - ? engine.projector.worldToWindow(position, {}) - : scene.cartesianToCanvasCoordinates(position); + const point = engine.projector.worldToWindow(position, {}); const visible = point && point.x >= 0 && point.y >= 0 && point.x <= canvas.clientWidth && point.y <= canvas.clientHeight && - (engine - ? engine.projector.horizonVisible(position) - : scene.mode !== cesium.SceneMode.SCENE3D || - occluder.isPointVisible(position)); + engine.projector.horizonVisible(position); if (!visible) { // A hidden marker keeps its last place, as the element did. if (marker.visible) present({ visible: false }); @@ -82,8 +66,7 @@ export function createWindInspectionMarker({ }; removeRender = frames.addEventListener(render); render(); - if (engine) engine.render.request('wind-inspection'); - else scene.requestRender?.(); + engine.render.request('wind-inspection'); }, clear, destroy: clear, diff --git a/src/layers/wind/inspectionMarker.test.mjs b/src/layers/wind/inspectionMarker.test.mjs index 49f0a6574..94d84630d 100644 --- a/src/layers/wind/inspectionMarker.test.mjs +++ b/src/layers/wind/inspectionMarker.test.mjs @@ -10,16 +10,7 @@ import { useWindInspectionStore } from '../../ui/stores/windInspection.js'; import WindInspectionMarker from '../../ui/views/panels/WindInspectionMarker.vue'; import { createWindInspectionMarker } from './inspectionMarker.js'; -const cesium = { - SceneMode: { SCENE3D: 3 }, - Ellipsoid: { WGS84: {} }, - EllipsoidalOccluder: class { - isPointVisible() { - return cesium.visible; - } - }, - visible: true, -}; +const visibility = { visible: true }; function fixture(t) { document.body.replaceChildren(); @@ -56,18 +47,26 @@ function fixture(t) { requestRender() {}, }, }; + const engine = { + host: { canvas: viewer.scene.canvas }, + frames: { postRender: viewer.scene.postRender }, + projector: { + worldToWindow: viewer.scene.cartesianToCanvasCoordinates, + horizonVisible: () => visibility.visible, + }, + render: { request() {} }, + }; const store = useWindInspectionStore(pinia); - cesium.visible = true; + visibility.visible = true; const owner = createWindInspectionMarker({ container, - viewer, - cesium, + engine, store, }); const nodes = () => [ ...container.querySelectorAll('.gev-wind-inspection-marker'), ]; - return { owner, viewer, store, frames, projected, nodes, container, cesium }; + return { owner, viewer, store, frames, projected, nodes, container, engine }; } test('the marker follows only its captured point, hides beyond the limb, and releases its listener', async (t) => { @@ -85,13 +84,13 @@ test('the marker follows only its captured point, hides beyond the limb, and rel assert.equal(marker.style.top, '220px'); assert.equal(marker.hidden, false); viewer.camera.positionWC = { moved: true }; - cesium.visible = false; + visibility.visible = false; frames.listener(); await nextTick(); assert.equal(marker.hidden, true); assert.equal(marker.style.left, '110px', 'a hidden marker keeps its place'); assert.ok(projected.every((value) => value === point)); - cesium.visible = true; + visibility.visible = true; frames.listener(); await nextTick(); assert.equal(marker.hidden, false); @@ -111,7 +110,7 @@ test('the marker follows only its captured point, hides beyond the limb, and rel }); test('an unchanged frame publishes nothing, and an older owner leaves a newer marker', (t) => { - const { owner, viewer, store, frames, container } = fixture(t); + const { owner, engine, store, frames, container } = fixture(t); owner.show({ x: 1 }); let publishes = 0; const publish = store.publish; @@ -124,8 +123,7 @@ test('an unchanged frame publishes nothing, and an older owner leaves a newer ma frames.listener = null; const newer = createWindInspectionMarker({ container, - viewer, - cesium, + engine, store, }); newer.show({ x: 2 });