mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
Fix Navara model display tint, overlay ordering, follow gestures and resource cleanup. Reduce repeated aircraft work and correct fixture enrichment responses. Strengthen dev/preview QA, preserve failure history and document verified W1 results. Validation: 7171 ordinary tests pass, one Windows-only skip; 15 allocation probes; 37 W1 acceptance steps; contracts, shell, four performance gates; 18 identical Cesium baseline PNGs. Known pre-existing CCTV and live-source limits are recorded.
129 lines
4.4 KiB
JavaScript
129 lines
4.4 KiB
JavaScript
// Navara ports for the existing heading scenarios. No app viewer is installed.
|
|
// Samples come from the renderer handles through the read-only layer probe.
|
|
export async function installNavaraHeadingProbe(page) {
|
|
await page.evaluate(async () => {
|
|
const app = window.__godsEyeView;
|
|
if (app.engine.kind !== 'navara') return;
|
|
const geo = await import('/src/geo/index.js');
|
|
const engine = app.engine;
|
|
const drawn = (id) =>
|
|
['flights', 'military']
|
|
.flatMap((layer) => app.layers.drawn(layer) ?? [])
|
|
.filter((row) => row.id === id);
|
|
window.__findTrackedModel = (id) => {
|
|
const rows = drawn(id);
|
|
const row =
|
|
rows.find((r) => r.trackedModel && r.model?.ready && r.model.show) ??
|
|
rows.find((r) => r.model?.ready && r.model.show);
|
|
return row
|
|
? {
|
|
id,
|
|
ready: row.model.ready,
|
|
show: row.model.show,
|
|
modelMatrix: row.model.matrix,
|
|
}
|
|
: null;
|
|
};
|
|
window.__courseFromModelMatrix = (mm, offset) => {
|
|
if (!mm) return null;
|
|
const c = geo.cartographic.fromCartesian({
|
|
x: mm[12],
|
|
y: mm[13],
|
|
z: mm[14],
|
|
});
|
|
const ex = -Math.sin(c.longitude),
|
|
ey = Math.cos(c.longitude);
|
|
const nx = -Math.sin(c.latitude) * Math.cos(c.longitude);
|
|
const ny = -Math.sin(c.latitude) * Math.sin(c.longitude),
|
|
nz = Math.cos(c.latitude);
|
|
const h =
|
|
(Math.atan2(
|
|
-(mm[0] * nx + mm[1] * ny + mm[2] * nz),
|
|
mm[0] * ex + mm[1] * ey,
|
|
) *
|
|
180) /
|
|
Math.PI;
|
|
return (((h - offset) % 360) + 360) % 360;
|
|
};
|
|
const turn = (angle = 0, nearer = 0) => {
|
|
const handle = engine.camera.following();
|
|
const target = handle?.target();
|
|
if (!target)
|
|
throw new Error('Heading screenshot needs a followed target');
|
|
const frame = geo.transforms.eastNorthUpToFixedFrame(target);
|
|
const inverse = geo.matrix4.inverseTransformation(
|
|
frame,
|
|
geo.matrix4.create(),
|
|
);
|
|
const p = geo.matrix4.multiplyByPoint(
|
|
inverse,
|
|
engine.camera.pose().position,
|
|
geo.cartesian3.create(),
|
|
);
|
|
const x = p.x * Math.cos(angle) - p.y * Math.sin(angle);
|
|
const y = p.x * Math.sin(angle) + p.y * Math.cos(angle);
|
|
const range = Math.hypot(x, y, p.z);
|
|
const scale = Math.max(150, range - nearer) / range;
|
|
handle.retarget({
|
|
viewFrom: { x: x * scale, y: y * scale, z: p.z * scale },
|
|
});
|
|
};
|
|
window.__headingView = {
|
|
clock: { currentTime: undefined },
|
|
get trackedEntity() {
|
|
const handle = engine.camera.following();
|
|
if (!handle) return null;
|
|
const label = handle.label;
|
|
const id = label?.id?.split(':').at(-1);
|
|
return {
|
|
gevTrackedId: label?.id,
|
|
gevLabelModel:
|
|
typeof label?.model === 'function' ? label.model() : label?.model,
|
|
gevDisplayPosition: handle.target,
|
|
billboard: {
|
|
rotation: {
|
|
getValue: () => drawn(id).find((r) => r.tracked)?.trackedRotation,
|
|
},
|
|
},
|
|
};
|
|
},
|
|
camera: {
|
|
rotateRight: (a) => turn(a),
|
|
rotateLeft: (a) => turn(-a),
|
|
zoomIn: (m) => turn(0, m),
|
|
},
|
|
scene: {
|
|
preRender: engine.frames.preRender,
|
|
postRender: engine.frames.postRender,
|
|
requestRender: () => engine.render.request('heading-probe'),
|
|
},
|
|
};
|
|
// Observe real Navara frames; unlike Cesium this port does not own a
|
|
// manual viewer loop. The 600 ms case samples at that cadence while
|
|
// Navara continues rendering; the dt-clamp branch is covered by unit QA.
|
|
window.__sampleModelCourse = (id, offset, windowMs, stepMs = 100) =>
|
|
new Promise((resolve) => {
|
|
const samples = [];
|
|
let last = -Infinity;
|
|
const remove = engine.frames.preRender.addEventListener(() => {
|
|
const now = performance.now();
|
|
if (now - last < stepMs) return;
|
|
last = now;
|
|
const model = window.__findTrackedModel(id);
|
|
samples.push({
|
|
tMs: now,
|
|
epochMs: Date.now(),
|
|
course: model
|
|
? window.__courseFromModelMatrix(model.modelMatrix, offset)
|
|
: null,
|
|
});
|
|
});
|
|
setTimeout(() => {
|
|
remove();
|
|
resolve(samples);
|
|
}, windowMs);
|
|
engine.render.request('heading-probe');
|
|
});
|
|
});
|
|
}
|