85 lines
4.6 KiB
Markdown
85 lines
4.6 KiB
Markdown
# Intelligent Planet Interactable Clustering
|
|
|
|
Earth interactable icons are managed by `createInteractableLayer`. Rendering still uses Three.js `Points`, and picking, hover, locked state, tooltips, and cruise focus still depend on marker `userData`; the clustering strategy only decides which markers are represented by a cluster dot.
|
|
|
|
## Strategies
|
|
|
|
`cluster.strategy` supports three modes:
|
|
|
|
- `stable-spherical`: stable spherical clustering. It clusters by local 3D positions on the globe and caches topology by zoom band. Rotation and small zoom changes within the same band only update projection and size. Use it for semi-static layers such as BGP, compute centers, and Earth interactables.
|
|
- `dynamic-screen`: dynamic screen-space clustering. This keeps the previous projection-based behavior and evaluates visible relationships per frame. Use it for high-frequency realtime layers such as vessels, or as a fallback.
|
|
- `none`: no clustering. Every marker is shown independently. Use it for low-count layers or precision-first views.
|
|
|
|
`cluster: false` is equivalent to `strategy: "none"`. If a layer enables clustering without declaring a strategy, it keeps the compatible `dynamic-screen` behavior.
|
|
|
|
## Stable Spherical
|
|
|
|
`stable-spherical` moves cluster identity from screen distance to globe distance:
|
|
|
|
- Each marker uses `icon_base_position` as its geographic anchor.
|
|
- By default, zoom levels above `2.5` force clustering off and show every marker as its original icon.
|
|
- The current zoom maps to a discrete band; rotation and small zoom changes inside the same band do not recompute topology.
|
|
- Clusters are recomputed only when the band, data revision, visibility, or filter state changes.
|
|
- A cluster centroid is computed from member 3D positions and normalized back to the globe shell, so the cluster dot stays rigidly aligned to its geographic center.
|
|
- Cluster dots do not participate in 2D avoidance, so screen-space repulsion cannot push them away from their real geographic projection.
|
|
- Band changes use a small hysteresis margin so zooming at a boundary does not repeatedly bounce between two bands.
|
|
- Newly created marker and cluster dots run a short scale + opacity ease. This is only a rendering transition; it does not change marker coordinates, picking objects, or locked state.
|
|
|
|
The stable strategy uses spherical bucket/hash neighbor lookup and must not use an all-pairs loop. Most frames only pay projection and material-size cost; topology cost is paid only on band or data changes.
|
|
|
|
## Configuration
|
|
|
|
```js
|
|
const computeCenterIconLayer = createInteractableLayer({
|
|
id: "computeCenters",
|
|
// ...
|
|
avoidance: SURFACE_AVOIDANCE_PROFILES.city,
|
|
cluster: {
|
|
strategy: "stable-spherical",
|
|
minCount: 2,
|
|
maxMarkersPerDot: 14,
|
|
transitionMs: 220,
|
|
bandHysteresis: 0.08,
|
|
disableAboveZoom: 2.5,
|
|
bands: [
|
|
{ key: "far", maxZoom: 1.7, distance: 15 },
|
|
{ key: "mid", maxZoom: 2.6, distance: 8 },
|
|
{ key: "near", maxZoom: 3.5, distance: 4 },
|
|
{ key: "detail", maxZoom: Infinity, distance: 0 },
|
|
],
|
|
},
|
|
});
|
|
```
|
|
|
|
Realtime layers can stay dynamic:
|
|
|
|
```js
|
|
const vesselIconLayer = createInteractableLayer({
|
|
id: "vessels",
|
|
// ...
|
|
cluster: {
|
|
strategy: "dynamic-screen",
|
|
enabled: true,
|
|
maxMarkersPerDot: 10,
|
|
},
|
|
});
|
|
```
|
|
|
|
## Tuning
|
|
|
|
- `distance` is the 3D globe-distance threshold and uses the same unit as `CONFIG.earthRadius`. Larger values cluster more aggressively.
|
|
- The farthest band usually uses a larger `distance`; the nearest detail band usually uses `0` to split clusters into original icons.
|
|
- `bandHysteresis` controls band-boundary stickiness. Too little can flicker near thresholds; too much can make band changes feel late.
|
|
- `transitionMs` controls cluster split/merge easing. Keep it around 160-260ms; longer durations can make realtime layers feel sluggish.
|
|
- `disableAboveZoom` controls the precision-view threshold. It defaults to `2.5`; above that zoom, no cluster dots are generated. Set it to `false` to disable the hard threshold.
|
|
- High-frequency realtime layers should prefer `dynamic-screen` so frequent data changes do not trigger stable topology recomputation.
|
|
- If a layer behaves poorly, switch it back to `dynamic-screen` or use `cluster: false`.
|
|
|
|
## Acceptance Checks
|
|
|
|
- Rotating the globe inside one zoom band should not cause clusters to flicker or regroup.
|
|
- Cluster dots should stay aligned with the globe-surface centroid and should not be pushed by avoidance.
|
|
- Zooming into the detail band should restore the layer's original icon textures and click behavior.
|
|
- Zoom levels above 250% should not show cluster dots.
|
|
- Realtime layers such as vessels should reflect incoming updates immediately.
|