Commit Graph
2791 Commits
Author SHA1 Message Date
Sameh Khamis aa7943a07b Move the changelog entry into changelog.d
Main now takes release notes as one fragment per pull request (#787).
2026-10-16 14:16:34 -07:00
Sameh Khamis 6bc473ac29 Merge branch 'main' into fix/overpass-failure-phrases-in-data
# Conflicts:
#	CHANGELOG.md
2026-10-16 14:16:33 -07:00
Sameh Khamis 1d62cdd805 Move the changelog entry into changelog.d
Main now takes release notes as one fragment per pull request (#787).
2026-10-16 14:16:32 -07:00
Sameh Khamis 8667993b7f Merge branch 'main' into fix/military-sticky-track
# Conflicts:
#	CHANGELOG.md
2026-10-16 14:16:32 -07:00
German Khokhlov d81ebf1268 build: the panel build leaves out the initial-chunk guard
The MCP panel build inlines every dynamic import into its one script, the
Navara mode included, so the guard that keeps Navara out of the app's
initial chunks failed it (npm run build:panel was red since d483e589). The
panel config filters the guard out, as phase 4 does; a test pins it.
build:panel is green and joins the gates. Phase-4 merge review item 2.
2026-10-17 01:09:58 +04:00
German Khokhlov 2ce1c55f69 build: the QA probe stays out of production builds
src/app/qaProbe.js was statically imported by standalone/application.js
and shipped in the main chunk (+639 B gzip). It now loads with import()
behind import.meta.env.DEV at the import site: production builds drop it
(main chunk 2,890,982 -> 2,889,432 B), the dev server still installs
window.__godsEyeView.layers (checked headless). Phase-4 merge review item 1.
2026-10-17 01:09:58 +04:00
German Khokhlov 7f13ef4a87 refactor(flights): Cesium rendering behind a renderer interface
Task 10a. The flights logic no longer imports Cesium: every Cesium object it
built moved one for one into the shared aircraft renderer
(src/layers/aircraft/render/cesium.js; contract in render/contract.js), with
the flights' parameters in src/layers/flights/render/cesium.js, registered
for 'cesium' in the renderer registry and created at init(viewer, {engine}).

- Fleet: BillboardCollection + sprite-order slot; sprites stay Billboard
  handles (field vocabulary documented for the Navara renderer).
- Models: fleet/tracked/preload glTF loads (asynchronous:false, MIX tint, IR
  boost as `unlit` → the UNLIT CustomShader), the model collection, setPose
  (same HPR frame with airplane.glb's 180° offset).
- Tracked: the ENU billboard Entity with its position/colour/rotation
  callbacks, viewFrom, followed through viewer.trackedEntity + the one exact
  tracked frame (kept as is; engine.camera.follow on Cesium would also end
  the follow on any camera command, a behaviour change).
- Trail: the entity trail (data/trailRenderer, unchanged: its Primitive
  predecessor failed below the mesh) and the live head segment.
- View: camera/scene reads, frame number, frustum and horizon tests,
  projection, render requests, frame events, evidence render-loop hold.
- Clicks and Escape go through the engine's selection dispatcher (the
  ScreenSpaceEventHandler and keydown listener are gone); the cross-layer
  untrack watches the renderer's follow.
- Colours are engine-neutral Rgba (src/layers/shared/color.js), bit-equal to
  the Cesium.Color constants they replace; NearFarScalar → nearFarScale.
- Cesium math in the logic → src/geo.
- check-import-directions: cesium only in render/cesium*.js and three/Navara
  only in render/navara*.js for flights, aircraft and shared; a layer's
  Navara renderer only through navaraMode's import().

Source-text tests updated with the moved code, same meaning:
- readLayerSource reads a layer's render/*.js and, for the aircraft layers,
  aircraft/render/*.js (GLB-load, tracked-entity, CallbackProperty checks).
- modelScale.test: _normalBillboardScaleByDistance reads nearFarScale(...).
- spriteOrder.test: flights' enable calls fleet.restoreOrderOnEnable(); the
  renderer wires it to restoreSpriteOrderOnEnable('flights', viewer).
- trackingClickGesture.test: the flights gesture rules are the dispatcher's
  (selection gesture before select, click classifier before the empty-space
  clear); flights answers the empty click with a user-origin clear.
- militaryAwareness.test: flights' hasContact guard reads fleet.visible().
- ownership.test: the services fixture names selection and renderer.

New tests: color, the Cesium aircraft renderer (contract, collections, pose,
loads, frustum/horizon, tracked entity, follow, trail head, adopted
collections), the flights logic over a plain-data renderer and a fake engine
(fleet, click-select, no-op on the tracked visual, empty-click release), the
boundary rule.

W1 replay vs 7c7422e2: counts, known objects, screen points (0 px), tracking
poses (0 m), requests and shots (same sha256) identical. Main chunk
2,890,982 B (+6.2 KB, +0.21 %: the renderer indirection).
2026-10-17 01:04:25 +04:00
Said Turksever 20c4c4a97f Merge pull request #787 from daikaginza/feat/changelog-fragments
feat(tooling): one changelog file per pull request
2026-10-16 21:48:37 +01:00
German Khokhlov 8e702f0c3b feat(data): the selection dispatcher treats an unowned pick as empty space
Task 10a moves flights onto the dispatcher; its old handler deselected on a
pick nobody owns (an unclaimed entity), and only another layer's pick was not
empty space. The dispatcher now does the same (after drill-through owners had
their chance), and selectionDispatcherFor(engine) gives the application one
dispatcher per engine. Tests for both.
2026-10-17 00:20:18 +04:00
German Khokhlov 9f1a0611fa fix(ui): the "kept in the link" notice is readable on Navara
On Navara the engine's persistent notice came up right after the share
restore's NOT ON THIS ENGINE notice and replaced it at once, so the names
of the linked layers this engine lacks were never seen (and a later finite
notice would in turn have wiped the engine notice for good).

- ShellFeedback keeps a persistent notice that a finite one covers and
  brings it back when the finite one ends; a persistent notice that arrives
  under a live finite one waits for it.
- Notices take a dwellMs; "Kept in the link: …" stays 15 s.
- scripts/qa-engine-notice.mjs (headless): a Cesium share link with a layer
  Navara lacks, opened with ?engine=navara: notice shown with the layer
  name, still up 14 s later, then the engine notice is back. 4/4 (before
  the fix the notice never appeared).

T8-T9 review item 2.
2026-10-16 23:27:47 +04:00
Rudy CelekliandClaude Sonnet 5.5 0d8caaafd8 fix(launches): keep null pad and landing coordinates unknown instead of 0
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Signed-off-by: Rudy Celekli <[email protected]>
2026-10-16 15:24:08 -04:00
German Khokhlov c493a8303b build: the QA fixture sources stay out of production builds
The ?qaFeed import is now gated by import.meta.env.DEV at its import site,
so Rollup drops the qaSources chunk (4.7 KB) from production builds instead
of shipping it unreachable. Dev behaviour is unchanged. (T8-T9 review item 3)
2026-10-16 23:18:33 +04:00
German Khokhlov bf46eb6d97 fix(voice): Navara refusals name the feature and the way back to Cesium
Review of T8-T9:
- The UNAVAILABLE_ON_ENGINE refusal now names the feature (UNAVAILABLE_FEATURES)
  and suggests the Cesium engine (?engine=cesium): "The cockpit is not
  available on the Navara engine yet. Switch to the Cesium engine
  (?engine=cesium) to use it." New fields: feature, suggestion.
- set_visual_style and set_post_processing refuse without a viewer
  (CESIUM_LOOK_ACTIONS): Navara has no visual styles, so they used to answer
  ok, change nothing and record a style get_current_view_state then reported.
- set_layer_visibility on a known layer the engine's catalog left out refuses
  with the same structured result instead of throwing "Unknown data layer".
  A name no engine knows still throws.
- Removed the dead summarizeEntity and the two helpers only it used.

Cesium (viewer present) is unchanged; tests cover each refusal and that the
style still applies with a viewer.
2026-10-16 23:17:36 +04:00
quanticsoul4772andClaude Opus 5.5 60444cca53 style: format the AIS type 0 regression test
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 12:06:24 -07:00
quanticsoul4772 1d418fd5af Merge remote-tracking branch 'origin/main' into fix-1056-ais0
# Conflicts:
#	CHANGELOG.md
2026-10-16 12:06:22 -07:00
quanticsoul4772andClaude Opus 5.5 52863a9f42 fix(tools): count AIS ship type 0 as unknown in vessel breakdowns
AIS type 0 means "not available" (ITU-R M.1371), but the source keeps it
as '0', so group_by: 'type' listed "0" as a type and understated
unknown_count. vessels_in_area rows now report it as null, and a
regression fixture mixes type 0 with Cargo and Tanker.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 12:06:10 -07:00
German Khokhlov 907cb2092d fix(globe): Navara redraws after a map change while its tiles arrive
Phase-4 QA: after a map-stack switch at the whole-globe view Navara
sometimes showed the new map only seconds later. Navara 0.2.1 reports idle
and stops drawing before a raster tile it fetched arrives, so the last
frame stays stale until something else asks for one. Every engine.maps
change (base imagery, terrain, photoreal tiles, their release) now asks for
a frame and keeps asking every 250 ms for 6 s, then the engine idles again.

scripts/qa-map-switch.mjs switches the stack (photoreal / Esri / OSM) at
18 000 km on each engine, waits for the tiles, hashes the picture, forces
frames and hashes again: a change means a stale picture. Before: Navara 3/8
switches drew the new picture by themselves; after: Navara 8/8, Cesium 8/8.
2026-10-16 23:00:15 +04:00
German Khokhlov ba039f7974 fix(app): the Navara notice no longer calls voice unavailable
Voice runs on Navara since 18b5475b (the viewer-only actions refuse with
UNAVAILABLE_ON_ENGINE); the notice still listed it as unavailable.
2026-10-16 22:53:35 +04:00
German Khokhlov 18b5475b8a feat(app): voice on the Navara engine
createViewerlessTools creates the voice commands with viewer: null on the
engine (tools.js passes it the voice options and place search), so
window.__gevVoiceCommands exists in ?engine=navara: the W1 tools drive the
engine and the viewer-only tools answer UNAVAILABLE_ON_ENGINE. The Cesium
path hands its engine to initGevVoiceCommands explicitly.

viewerlessTools.test.mjs: the Navara tools now return voice commands
(created without DOM through injected control and session stand-ins),
whose runner answers get_current_view_state on the engine and refuses
control_scene; the debug handle carries them and teardown removes them.
2026-10-16 22:52:21 +04:00
German Khokhlov bc281ab246 refactor(voice): W1 actions on the engine
The voice runner drives the globe engine (an `engine` option, by default
the one registered for the viewer) instead of the Cesium viewer:
track_entity, stop_tracking (camera.following().stop()), frame_overhead
(camera.flyToSphere with the pose heading), fly_to_location, zoom_to_globe
(follow released through camera.following()), adjust_camera_zoom,
next_iss_pass / next_satellite_pass, select_nearest_aircraft and the
analyst view context, get_current_view_state, get_entity_context
(projector.worldToWindow), the view target (picker.pickPosition, then
camera.pickEllipsoid, then picker.pickGlobe) and its moveEnd prewarm, and
the viewport samples. getBasemapLabelContext takes the engine or a viewer
with a registered engine (the HUD).

Without a viewer the tools that still need one (VIEWER_ONLY_ACTIONS:
control_scene, annotate_map, clear_annotations, fly_route, move_camera,
control_cockpit, set_context_mode, control_radio, control_cctv) answer a
structured refusal (code UNAVAILABLE_ON_ENGINE) instead of throwing.

Point-and-ask picks with engine.picker and keys the camera pose from
engine.camera.pose(); the viewport capture reads engine.host.canvas and
waits for the engine's postRender (both engines keep their drawing
buffer: Cesium's contextOptions, Navara's own WebGL2 canvas, S4).

Tests: gevActionsEngine.test.mjs runs each moved action on the fake
engine. gevActions.test.mjs keeps its expectations; its runner helper now
registers an engine for the test viewer (the Cesium engine over it, or a
fake engine at its positionCartographic) and the two harness cameras gain
roll and frustum for camera.pose().
2026-10-16 22:52:21 +04:00
German Khokhlov ec7dcc944e test(qa): an engine-neutral layer probe
- src/app/qaProbe.js → window.__godsEyeView.layers: list() (with
  `available`), unavailable(), available(id), enable/disable(id) (a user
  toggle through the layer manager), objects(id) → [{ id, label, position,
  screen }], screenOf(id, objectId), selected(id), following(), movers(),
  framesDrawn() - all through the engine and the catalog's availability, the
  same on Cesium and Navara. Installed after the tools.
- src/app/layers/qaSources.js: `?qaFeed=flights:synthetic:10000,
  vessels:fixture` (dev server only, allowQaRegistration) replaces those
  sources with seeded synthetic answers served through the live sources'
  own parsers (same snapshot shape; loaded with import() only then). The
  pure generators moved from src/tooling/w1Feeds.js to src/bench/w1Feeds.js
  (the browser graph may not reach src/tooling); the W1 recordings
  re-export them.
- scripts/qa-w1-layers.mjs: the parity checklist's frame, --layer
  --engine=cesium|navara with the fixture feeds, JSON per run and
  --compare (objects on screen per pose, 5 known objects within 2 px).
  Filled: 1 view, 8 idle, 9 errors/no leftover mover sets after 10
  on/off; 2-7 report TODO for the layer tasks.
- qa-perf, qa-labels, qa-overlay-baseline and qa-label-readability take
  --engine= and use the engine (camera, frames, render requests, tiles) and
  the probe (layers, following, movers) instead of the viewer; checks whose
  layers an engine lacks are SKIPPED with the reason (Navara today), as are
  qa-perf's animated-style checks there (Cesium post stages).

Verdicts, Cesium, before → after: qa-perf 24/24 → 24/24, qa-labels 14/14 →
14/14, qa-overlay-baseline (datacenters, firms, detection-50) 2 measured +
1 skipped → the same. Navara: qa-perf 14 passed, 3 skipped; qa-labels 3/3,
1 skipped; qa-overlay-baseline 3 skipped; qa-w1-layers flights: Cesium 5/5
(10 000 synthetic aircraft), Navara skipped (no flights renderer yet).
2026-10-16 22:52:18 +04:00
Said Turksever 4ae6d154f9 Merge branch 'main' into feat/changelog-fragments 2026-10-16 19:33:08 +01:00
Arianna Method 8d37661219 fix(perimeters): handle antimeridian queries and incident card anchors
Rebase #1142 onto current main, keeping both sides of the changelog and runtime documentation conflicts. Retain unwrapped containment and circular query coordinates.

Address review feedback on perimeterAnchorDegrees: use circular longitude for the selected outer ring, excluding its closing duplicate. Compare multipart areas along short longitude edges so a small dateline part cannot steal the card anchor from a larger polygon.

Three new card regressions fail before the fix and pass afterward. Coverage includes reversed/rotated rings, vertices at +/-180 and both multipart selection directions. Local validation: 43 focused tests; full suite 7,636 passed, one skipped, zero failures; production build, formatting, module boundaries and whitespace checks pass on Node 24.19.0. Browser tracking validation follows on the QA branch.
2026-10-16 21:33:01 +03:00
German Khokhlov 834d34296f test(overlays): an allocation workload through the Navara projector
Review of Task 7: every allocation workload ran the Cesium engine on a mock
viewer with identity matrices and the horizon test off, so the per-label
horizon test and the Navara projector were never in the gate. The new
`navara-projector` profile gives the host the Navara engine's projector
(cameraMath worldToWindow, viewProjection, horizonVisible, as
createNavaraEngine wires it) over a real WGS84 camera 2 000 km up, with 250
moving labels on a lon/lat grid under it and the horizon test on.

Node 24.14 (the calibrated runtime; CI's 24.14.0 matrix leg runs the gate
with GEV_REQUIRE_ALLOCATION_GATE=1): 10 905 B/frame median (generic 250
labels: 9 681), 43.6 B per candidate; budget 13 000 B/frame as the generic
saturated workload. All 14 workloads pass on 24.14.
2026-10-16 22:26:04 +04:00
German Khokhlov d6fe0f3805 fix(globe): the floor takes no tile coarser than 1 km
Review of Task 6: from 15 km with cold tiles Navara answers from 3.5-4 km
tiles (13.9 km at AUS), below the 4 km floor limit, 6-16 m off; the mesh
floor samples up to 25 km, so those would latch. MAX_FLOOR_TILE_SIZE_M is
now 1 km: from 800 m the answering tiles are 112-314 m. The 1.8 km tile
that read the Austin ground right under a 600 m camera is now a miss too,
which only leaves the DEM prior in place.

qa-surface-heights runs the 15 km pass first in a fresh page (cold), then
the 800 m pass in another, and keeps every answer the floor rule would
take while the tiles stream. Measured: 800 m 19/20 within 2 m of Cesium;
15 km cold 7/20 (the plan's gate of 15 assumed warm tiles - lod 'finest'
can only use what Navara has loaded, and at 15 km it loads 3.5-14 km
tiles), and the floor rule takes 0/20 of those; while streaming it took
answers more than 2 m off at 3 places: the Empire State spire (39 m, also
the settled answer) and two street points 2.2-2.3 m off.
2026-10-16 22:19:57 +04:00
German Khokhlov f0ce1b04c7 fix(globe): the Navara follow turns and dollies as Cesium's tracked camera
Review of Tasks 6-7: a 240 px drag right turned Navara's follow by -67.5°
where Cesium turns +119.5°, and a right drag did nothing (Cesium dollies,
1.95 Mm → 0.91 Mm). followGestures.js now follows Cesium's
ScreenSpaceCameraController for a tracked camera:
- left drag (rotate3D): heading += 1.77 × dx / width × 2π (clockwise for a
  drag right; 1.77 is Cesium's maximum rotate rate, which a tracked camera
  beyond a few metres always reaches) and the camera rises by
  1.77 × -dy / height × π; each move counts at most a tenth of the canvas
  (maximumMovementRatio);
- right drag (zoom3D): follow.js dollyOffset moves 5 × dy / height of the
  distance above the floor (closing) or of the range (opening), never
  inside it; no context menu while following.

The contract follow check turns the camera with a 240 px drag right and
asserts the heading turn against Cesium's rate (±15 %; the harness holds the
pointer before releasing it so Cesium adds no inertia), and dollies with a
right drag down (range below 80 %, never under 149 m). Cesium 73/73, Navara
79/79.
2026-10-16 22:14:02 +04:00
German Khokhlov b0cbed081b feat(app): layer catalog per engine
- src/app/layers/renderers.js: the layer renderer registry
  (registerLayerRenderers(engineKind, { [layerId]: factory }),
  rendererFor(layerId, engine), layerAvailableOn(layerId, engineKind)).
  Every application layer is registered for Cesium as CESIUM_BUILTIN (it
  still draws itself until its render/cesium.js); Navara factories come with
  the layer tasks. On it, the stable availability API phase 4 injects into
  its layer list: createLayerAvailability(engineKind) →
  { engine, isAvailable(id), available(), unavailable() } with { id, name }
  rows (src/app/layers/directory.js holds the ids and names in catalog
  order; a test keeps it equal to the constructed catalog).
- createApplicationCatalog({ engineKind, availability }) constructs only the
  layers the engine can show, in the same order (shared instances made
  once, on demand); the catalog carries `availability` and the others'
  metadata (`unavailableMetadata`). Cesium builds the same 30 layers in the
  same order as before; Navara builds none yet (no Navara renderers) - the
  standalone app now asks the catalog for it instead of making an empty one.
- catalogControlServices: a role whose layer the engine leaves out gets an
  empty stand-in (absentLayerControlServices folded in); any other missing
  layer is still a wiring error.
- LayerLifecycle(viewer, { engine }): every module call (init, enable,
  update, disable, destroy) gets { signal, engine } as its second argument;
  viewer is null on Navara.
- Links: a link that turns on layers this engine leaves out keeps them in
  the durable state (re-saved links carry them; back on Cesium they turn
  on) and activates nothing; a notice names them ("NOT ON THIS ENGINE · Kept
  in the link: …"). The Navara notice lists the engine's data layers from
  the registry.
- The dev launcher's CCTV wiring check matches the new catalog form.
2026-10-16 22:07:55 +04:00
German Khokhlov e8b3a10a07 Merge pull request #602 from meirm/i18n-spanish
i18n: interface localization with Spanish and Russian (English-text keys)
2026-10-16 21:43:26 +04:00
German Khokhlov 69f2089636 Merge remote-tracking branch 'meirm/i18n-spanish' into i18n-spanish 2026-10-16 21:30:15 +04:00
German KhokhlovandClaude Opus 5.5 b0e28f97d9 i18n: translate the SDR messages from the latest main
Seek stopped, the decoder overload stops and the stalled USB transfer
now follow the UI language.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 21:29:28 +04:00
German Khokhlov 178a57c16a Merge branch 'main' into i18n-spanish 2026-10-16 21:26:06 +04:00
German Khokhlov e48841072d Merge remote-tracking branch 'origin/main' into i18n-spanish 2026-10-16 21:25:39 +04:00
German Khokhlov e1be2eac19 feat(app): detection and readout on Navara
The shell creates detection and the tracked readout on the engine, so the
Navara mode has them too (before: not created without a viewer). Detection
reads the camera height from the engine. The tracked readout follows
engine.camera.following(): a follow's plain-data label
(FollowOptions.label: { id, model, position }, the readout card's model
and anchor) or, on Cesium, the followed Entity and its gevLabelModel as
before (Cesium layers keep publishing the same). refreshTrackedReadout
takes a follow handle too. A bare viewer without a registered engine
(tests) is still read for its trackedEntity.

In the app on Navara: "[Detection] Initialized", "[TrackedReadout]
Initialized", and a follow with a label paints its card.
2026-10-16 21:14:59 +04:00
German Khokhlov 7d5a484796 refactor(overlays): layer labels through the engine
The world overlay host already projects through engine.projector and tests
the horizon with projector.horizonVisible; the last Cesium value on its
path, the snapshot of an entry's cullPosition, is now a geo Cartesian3.

Navara's horizonVisible is Cesium's EllipsoidalOccluder.isPointVisible:
tested against Cesium's own on 8 000 eye/point pairs (600 m to 20 000 km,
points under, on and over the ellipsoid; no disagreement) and at the limb
(a label 40 m under the ellipsoid just past the horizon is hidden, with its
cullPosition lifted 100 m it is shown). It ran per label per frame with
three new vectors each call; it now uses scratch vectors. Micro-measure
(500 labels per frame, no scavenge during the run): 7 941 B per frame
before, 51 after.

The W1 sources that projected with Cesium's SceneTransforms themselves -
the icon orientation's exact projection and the launch replay overlay - go
through the engine's projector when their scene or viewer has a registered
engine. Satellites, vessel labels and aircraft recession project nothing
themselves (the host does).

scripts/qa-overlay-engines.mjs publishes the same labels on both engines at
a city, an orbital and a limb pose and compares the painted rectangles:
worst 0 / 2 / 0 px; on both, the far-side label is not painted, the lifted
limb label is and the buried one is not.
2026-10-16 21:14:52 +04:00
VG31OP b48f44f6e8 Merge remote-tracking branch 'origin/main' into fix/tle-catalog-matching 2026-10-16 22:44:46 +05:30
Mazeyar Moeini Feizabadi ecaab6a946 Merge pull request #1183 from bilawalsidhu/codex/sdr-bounded-playback
fix(sdr): stabilize local playback and integrate seek-wrap fix
2026-10-16 19:12:27 +02:00
German Khokhlov 311722e6de test(globe): the mover shader test expects decoded sRGB colours
68ff4e1c decodes instance colours in the fragment shader; its shader
test still matched the undecoded line.
2026-10-16 21:04:22 +04:00
German Khokhlov c71ce9cf9b Merge remote-tracking branch 'meirm/i18n-spanish' into i18n-spanish 2026-10-16 21:00:43 +04:00
German KhokhlovandClaude Opus 5.5 20f4cf3763 i18n: fix the LGTM review findings and tidy the branch texts
- i18n scanner: lastError sink, no false sinks on defaults and ternaries
- shared src/i18n/marker.js for modules that only mark strings
- wind field messages move to wind/fields.js; CITY_POIS imports from cityPois.js
- drop review IDs, change history and stale references from comments and docs

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 21:00:28 +04:00
Mazeyar Moeini Feizabadi e021159938 Merge remote-tracking branch 'origin/main' into codex/sdr-bounded-playback 2026-10-16 19:00:04 +02:00
German Khokhlov d7193e3b9e fix(globe): the CPU pick radius follows scaleByDistance
Review of Task 4: the CPU picks (Navara hover, drill, the stale-answer
check) took a sprite or point at its full sizePx however far it was, so a
set shrunk with scaleByDistance was picked well off its drawn edge. The
radius now scales by the set's scaleByDistance at the instance's camera
distance, linear between near and far as the Navara shader draws it.
2026-10-16 21:00:01 +04:00
Meir Michanie 5bfa4a290c Merge branch 'main' into i18n-spanish 2026-10-16 18:59:54 +02:00
German Khokhlov a4e753d550 fix(globe): Cesium picks fall through filtered results; no picks behind the horizon
Review of Task 4:
- a Cesium pick whose top result is filtered (a pickable: false set, the
  follow camera's proxy entity) took nothing; it now drills and answers the
  first result under it with an id (an object), else the first kept one
  (map tiles);
- billboards and points drawn without a depth test reach Cesium's GPU pick
  through the globe: mover instances behind the horizon (projector
  horizonVisible on the instance position) are left out of picks and
  drills, as Navara's CPU drill already did;
- the follow proxy entity (1 px point, cesium/follow.js) carries
  gevFollowProxy and is never a pick (review item 6).
2026-10-16 20:59:10 +04:00
manjunath 8abc99aa0a fix(voice): preserve request ownership across callbacks
keep typed and tool-follow-up responses attached to their original requests
reject duplicate events before they affect newer work
preserve newer input and Radio handoffs during old callback cleanup
integrate the tested main snapshot without dropping existing history
2026-10-16 22:29:00 +05:30
German Khokhlov 4e195dac6b fix(globe): the Navara follow orbits on drag and zooms to the floor
Review of Task 5: while following on Navara a left drag panned the globe
about the ground point under the cursor (range 2,154 m → 54,136 m) and the
wheel zoomed toward that point (462 m after 40 notches, Cesium stops at
the 150 m floor). While a follow runs, Navara's spin, zoom and tilt are
now suspended (input.suspendCameraInputs, which gives the app's own
setCameraInputs choice back after the release) and followGestures.js turns
a drag into heading and pitch of the ENU offset (a canvas width is one
turn, its height half a turn; elevation kept within 2°-89°) and a wheel
notch into 20 % of its range, never inside the minimum range, as Cesium's
EntityView orbits the target. The offset is no longer read back from a
camera the follow alone moves.

Also (review item 7): a follow requests a render only when the target, the
offset or a gesture changed, so a still target lets Navara idle.

The contract follow check drags (range within ±5 %, target within 3 px)
and turns the wheel 40 notches (never below 149 m, ends within 5 % of
150 m) on both engines with real input (qa-globe-contract's pointer gains
drag and wheel); after stop it waits for Cesium's gesture inertia to end
before checking that the camera is free. Contract: Cesium 73/73, Navara
79/79.
2026-10-16 20:57:10 +04:00
Mazeyar Moeini Feizabadi 365b2e7913 test(radio): fixture unavailable optional Google tokens 2026-10-16 18:56:17 +02:00
Said Turksever 6a4960762d Merge branch 'main' into feat/changelog-fragments 2026-10-16 17:52:11 +01:00
Said Turksever 3a57a04c3b Merge pull request #1159 from asturksever/fix/street-level-gate-pill-race
test(street-level): settle coverage before reading the ON/OFF pill
2026-10-16 17:52:04 +01:00
German Khokhlov 68ff4e1c25 fix(globe): Navara overlay colours are sRGB
Review of Tasks 4-5: the overlay pass encodes linear to sRGB, and the
instance and line colours went in as if linear: grey 0.5 drew 187 (Cesium
128) and [1, 0.6, 0.2] drew 255/203/124 (Cesium 255/153/52). The sprite,
point and line shaders now decode them (sRGBTransferEOTF), as the sRGB
atlas texture already was.

A contract check draws mid-tone sprites, points and lines (grey 0.5 and
[1, 0.6, 0.2]) and reads their centres: within 3 per channel on every
engine. Navara 79/79.
2026-10-16 20:51:36 +04:00
German Khokhlov 645f042450 feat(globe): surface height quality
engine.surface.sampleHeightDetailed(place, { exclude, lod }) answers
{ height, source: 'tiles' | 'terrain', tileSizeM? } (src/globe/surfaceDetail.js).
On Navara tileSizeM is the extent of the tile that answered and
lod 'finest' takes the finest loaded tile, drawn or not; Cesium answers
tiles without a size, else the globe's terrain. acceptedSurfaceHeight()
turns an answer into a height or a miss: tiles larger than
MAX_FLOOR_TILE_SIZE_M (4 km) are too coarse, terrain is not the mesh.

The mesh floor sampler and the ground snap are engine-only now (a viewer
or scene reaches its engine through engineFromViewer, which also registers
the scene) and treat a coarse answer as a miss, as they treat undefined.
Measured on Navara at Austin: while the tiles stream, a 27.9 km tile reads
143.2 m where the ground is 122.5 m (a 1.8 km tile).

`exclude` takes the engine's own mover sets (MoverSet.id): Cesium hides a
set's primitive for the synchronous sample (Cesium matches excluded objects
per billboard, not per collection); Navara's ray only meets tiles.

"sampleHeight undefined in the app on Navara" did not reproduce: the
startup Austin sample is 121.9 m (tile 889 m) on Navara and 122.4 m on
Cesium; one ModelMesh class is patched.

S2 QA (scripts/qa-surface-heights.mjs, 20 places at JFK, SFO, AUS, roofs
and streets): Navara within 2 m of Cesium at 19/20 from 800 m and 18/20
from 15 km with lod 'finest' (gates 18 and 15); offsets per region
-0.21 / +0.4 / +0.1 m; the Empire State roof differs by 39 m (spire
geometry). Navara tile sizes at 800 m: 112-314 m.
2026-10-16 20:51:29 +04:00