@tellegen/svelte 0.4.5 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import 'maplibre-gl/dist/maplibre-gl.css';
3
3
  import { tick, untrack } from 'svelte';
4
- import type { Layer, PickingInfo } from '@deck.gl/core';
4
+ import type { Layer, LayerExtension, PickingInfo } from '@deck.gl/core';
5
5
  import type { IconLayer, IconLayerProps, PathLayer, ScatterplotLayer } from '@deck.gl/layers';
6
6
  import type { MapLibreOverlay } from '@deck.gl/maplibre';
7
7
  import type { LngLatBoundsLike, Map as MapLibreMap } from 'maplibre-gl';
@@ -54,6 +54,7 @@
54
54
  transformerIconAtlas,
55
55
  transformerIconMapping
56
56
  } from './transformer-icon.js';
57
+ import { createGlobeHorizon, withGlobeHorizon } from './globe-horizon.js';
57
58
  import { foldMapBounds } from './map-bounds.js';
58
59
  import { getAppState, getController } from './context.svelte.js';
59
60
  import { displayFmt, fmt, rgbaCss } from './format.js';
@@ -239,6 +240,8 @@
239
240
  isHovering: boolean;
240
241
  };
241
242
  let layerCtors = $state.raw<LayerCtors | null>(null);
243
+ // Hides the far side of the globe; built with the deck modules.
244
+ let globeHorizon: LayerExtension | null = null;
242
245
 
243
246
  interface CaseDisplay {
244
247
  prices: Map<number, number>;
@@ -818,9 +821,10 @@
818
821
  }
819
822
 
820
823
  async function loadMapModules() {
821
- const [maplibre, deckMap, layers, workerUrl] = await Promise.all([
824
+ const [maplibre, deckMap, deckCore, layers, workerUrl] = await Promise.all([
822
825
  import('maplibre-gl'),
823
826
  import('@deck.gl/maplibre'),
827
+ import('@deck.gl/core'),
824
828
  import('@deck.gl/layers'),
825
829
  // A package export, not a relative import: its `development`
826
830
  // condition keeps the worker's `?worker&url` import out of the dev
@@ -832,6 +836,7 @@
832
836
  return {
833
837
  maplibregl: maplibre,
834
838
  MapLibreOverlay: deckMap.MapLibreOverlay,
839
+ horizon: createGlobeHorizon(deckCore.LayerExtension),
835
840
  PathLayer: layers.PathLayer,
836
841
  ScatterplotLayer: layers.ScatterplotLayer,
837
842
  IconLayer: layers.IconLayer
@@ -844,8 +849,10 @@
844
849
  // maplibre's own WebGL2 context instead of a second canvas, so the page
845
850
  // holds one GL context rather than two. Safari evicts WebGL contexts under
846
851
  // memory and tab pressure, and one context survives that far better; the
847
- // deck layers also inherit maplibre's 4096 canvas clamp. The flat positron
848
- // basemap writes no depth, so the overlay always draws on top.
852
+ // deck layers also inherit maplibre's 4096 canvas clamp. The basemap
853
+ // writes no depth we can trust (on the globe, deck's sphere and maplibre's
854
+ // disagree near the surface), so the overlay always draws on top, and on
855
+ // the globe the horizon extension hides what lies on the far side.
849
856
  function buildOverlay(MapLibreOverlay: MapModules['MapLibreOverlay']): MapLibreOverlay {
850
857
  const touch = coarsePointer();
851
858
  return new MapLibreOverlay({
@@ -885,9 +892,10 @@
885
892
  let cleanup = () => {};
886
893
  let cancelled = false;
887
894
  void loadMapModules()
888
- .then(({ maplibregl, MapLibreOverlay, PathLayer, ScatterplotLayer, IconLayer }) => {
895
+ .then(({ maplibregl, MapLibreOverlay, horizon, PathLayer, ScatterplotLayer, IconLayer }) => {
889
896
  if (cancelled) return;
890
897
  layerCtors = { PathLayer, ScatterplotLayer, IconLayer };
898
+ globeHorizon = horizon;
891
899
  const m = new maplibregl.Map({
892
900
  container,
893
901
  style: spec.primary,
@@ -1145,7 +1153,7 @@
1145
1153
  updateTriggers: { getFillColor: [study, mode, theme], getRadius: [app.busSizing] }
1146
1154
  })
1147
1155
  );
1148
- overlay.setProps({ layers: [...layers, ...extraLayers] });
1156
+ overlay.setProps({ layers: withGlobeHorizon([...layers, ...extraLayers], globeHorizon) });
1149
1157
  return;
1150
1158
  }
1151
1159
  for (const c of app.cases) {
@@ -1418,7 +1426,7 @@
1418
1426
  })
1419
1427
  );
1420
1428
  }
1421
- overlay.setProps({ layers: [...layers, ...extraLayers] });
1429
+ overlay.setProps({ layers: withGlobeHorizon([...layers, ...extraLayers], globeHorizon) });
1422
1430
  });
1423
1431
 
1424
1432
  $effect(() => {
@@ -0,0 +1,6 @@
1
+ import type { Layer, LayerExtension } from '@deck.gl/core';
2
+ /** A deck.gl extension that hides geometry on the far side of the globe. It
3
+ * takes deck's `LayerExtension` class, which the map loads lazily. */
4
+ export declare function createGlobeHorizon(Base: typeof LayerExtension): LayerExtension;
5
+ /** The layers with the extension added, once each. */
6
+ export declare function withGlobeHorizon(layers: Layer[], horizon: LayerExtension | null): Layer[];
@@ -0,0 +1,41 @@
1
+ // On the globe, geometry past the horizon is on the far side of the earth and
2
+ // must not show through it. A depth test against maplibre's globe cannot do
3
+ // this: deck's globe and maplibre's write different depth near the surface,
4
+ // so the test also cut away parts of a network facing the camera. Instead
5
+ // each vertex measures itself against the horizon in deck's own common space,
6
+ // where the globe is a sphere at the origin and `project.cameraPosition` is
7
+ // the eye: a surface point p faces the eye when (eye − p)·p > 0. Fragments
8
+ // past it are discarded; on the flat map nothing changes.
9
+ const INJECT = {
10
+ 'vs:#decl': 'out float tgHorizon;',
11
+ 'vs:DECKGL_FILTER_GL_POSITION': `
12
+ vec3 tgPoint = geometry.position.xyz;
13
+ tgHorizon = project.projectionMode == PROJECTION_MODE_GLOBE
14
+ ? dot(tgPoint, project.cameraPosition - tgPoint) / max(dot(tgPoint, tgPoint), 1.0) + 1e-4
15
+ : 1.0;
16
+ `,
17
+ 'fs:#decl': 'in float tgHorizon;',
18
+ 'fs:DECKGL_FILTER_COLOR': 'if (tgHorizon < 0.0) discard;'
19
+ };
20
+ /** A deck.gl extension that hides geometry on the far side of the globe. It
21
+ * takes deck's `LayerExtension` class, which the map loads lazily. */
22
+ export function createGlobeHorizon(Base) {
23
+ class GlobeHorizonExtension extends Base {
24
+ static extensionName = 'GlobeHorizonExtension';
25
+ getShaders() {
26
+ return { inject: INJECT };
27
+ }
28
+ }
29
+ return new GlobeHorizonExtension();
30
+ }
31
+ /** The layers with the extension added, once each. */
32
+ export function withGlobeHorizon(layers, horizon) {
33
+ if (!horizon)
34
+ return layers;
35
+ return layers.map((layer) => {
36
+ const extensions = layer.props.extensions ?? [];
37
+ return extensions.includes(horizon)
38
+ ? layer
39
+ : layer.clone({ extensions: [...extensions, horizon] });
40
+ });
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellegen/svelte",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "description": "Svelte components for tellegen power system maps, local case files, and browser solves.",
5
5
  "keywords": [
6
6
  "svelte",