@deck.gl/extensions 8.9.0-alpha.5 → 8.9.0-beta.2

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.
Files changed (201) hide show
  1. package/dist/dist.dev.js +3688 -14538
  2. package/dist/es5/brushing/brushing.js +0 -8
  3. package/dist/es5/brushing/brushing.js.map +1 -1
  4. package/dist/es5/clip/clip.js +5 -5
  5. package/dist/es5/clip/clip.js.map +1 -1
  6. package/dist/es5/collide/collide-effect.js +286 -0
  7. package/dist/es5/collide/collide-effect.js.map +1 -0
  8. package/dist/es5/collide/collide-pass.js +80 -0
  9. package/dist/es5/collide/collide-pass.js.map +1 -0
  10. package/dist/es5/collide/collide.js +116 -0
  11. package/dist/es5/collide/collide.js.map +1 -0
  12. package/dist/es5/collide/shader-module.js +39 -0
  13. package/dist/es5/collide/shader-module.js.map +1 -0
  14. package/dist/es5/common/projection-utils.js +164 -0
  15. package/dist/es5/common/projection-utils.js.map +1 -0
  16. package/dist/es5/data-filter/shader-module.js +1 -0
  17. package/dist/es5/data-filter/shader-module.js.map +1 -1
  18. package/dist/es5/fill-style/fill-style.js +16 -125
  19. package/dist/es5/fill-style/fill-style.js.map +1 -1
  20. package/dist/es5/fill-style/shaders.glsl.js +6 -8
  21. package/dist/es5/fill-style/shaders.glsl.js.map +1 -1
  22. package/dist/es5/fp64/project64.glsl.js +1 -1
  23. package/dist/es5/index.js +16 -0
  24. package/dist/es5/index.js.map +1 -1
  25. package/dist/es5/mask/mask-effect.js +280 -0
  26. package/dist/es5/mask/mask-effect.js.map +1 -0
  27. package/dist/es5/mask/mask-pass.js +106 -0
  28. package/dist/es5/mask/mask-pass.js.map +1 -0
  29. package/dist/es5/mask/mask.js +17 -4
  30. package/dist/es5/mask/mask.js.map +1 -1
  31. package/dist/es5/mask/shader-module.js +1 -1
  32. package/dist/es5/mask/shader-module.js.map +1 -1
  33. package/dist/es5/mask/utils.js +136 -0
  34. package/dist/es5/mask/utils.js.map +1 -0
  35. package/dist/es5/path-style/shaders.glsl.js +1 -1
  36. package/dist/es5/terrain/height-map-builder.js +116 -0
  37. package/dist/es5/terrain/height-map-builder.js.map +1 -0
  38. package/dist/es5/terrain/shader-module.js +84 -0
  39. package/dist/es5/terrain/shader-module.js.map +1 -0
  40. package/dist/es5/terrain/terrain-cover.js +259 -0
  41. package/dist/es5/terrain/terrain-cover.js.map +1 -0
  42. package/dist/es5/terrain/terrain-effect.js +337 -0
  43. package/dist/es5/terrain/terrain-effect.js.map +1 -0
  44. package/dist/es5/terrain/terrain-pass.js +122 -0
  45. package/dist/es5/terrain/terrain-pass.js.map +1 -0
  46. package/dist/es5/terrain/terrain-picking-pass.js +130 -0
  47. package/dist/es5/terrain/terrain-picking-pass.js.map +1 -0
  48. package/dist/es5/terrain/terrain.js +101 -0
  49. package/dist/es5/terrain/terrain.js.map +1 -0
  50. package/dist/es5/terrain/utils.js +32 -0
  51. package/dist/es5/terrain/utils.js.map +1 -0
  52. package/dist/es5/utils/debug.js +71 -0
  53. package/dist/es5/utils/debug.js.map +1 -0
  54. package/dist/esm/brushing/brushing.js +0 -7
  55. package/dist/esm/brushing/brushing.js.map +1 -1
  56. package/dist/esm/clip/clip.js +5 -4
  57. package/dist/esm/clip/clip.js.map +1 -1
  58. package/dist/esm/collide/collide-effect.js +251 -0
  59. package/dist/esm/collide/collide-effect.js.map +1 -0
  60. package/dist/esm/collide/collide-pass.js +30 -0
  61. package/dist/esm/collide/collide-pass.js.map +1 -0
  62. package/dist/esm/collide/collide.js +78 -0
  63. package/dist/esm/collide/collide.js.map +1 -0
  64. package/dist/esm/collide/shader-module.js +32 -0
  65. package/dist/esm/collide/shader-module.js.map +1 -0
  66. package/dist/esm/common/projection-utils.js +130 -0
  67. package/dist/esm/common/projection-utils.js.map +1 -0
  68. package/dist/esm/data-filter/shader-module.js +1 -0
  69. package/dist/esm/data-filter/shader-module.js.map +1 -1
  70. package/dist/esm/fill-style/fill-style.js +20 -72
  71. package/dist/esm/fill-style/fill-style.js.map +1 -1
  72. package/dist/esm/fill-style/shaders.glsl.js +5 -7
  73. package/dist/esm/fill-style/shaders.glsl.js.map +1 -1
  74. package/dist/esm/fp64/project64.glsl.js +1 -1
  75. package/dist/esm/index.js +2 -0
  76. package/dist/esm/index.js.map +1 -1
  77. package/dist/esm/mask/mask-effect.js +233 -0
  78. package/dist/esm/mask/mask-effect.js.map +1 -0
  79. package/dist/esm/mask/mask-pass.js +62 -0
  80. package/dist/esm/mask/mask-pass.js.map +1 -0
  81. package/dist/esm/mask/mask.js +14 -3
  82. package/dist/esm/mask/mask.js.map +1 -1
  83. package/dist/esm/mask/shader-module.js +1 -1
  84. package/dist/esm/mask/shader-module.js.map +1 -1
  85. package/dist/esm/mask/utils.js +109 -0
  86. package/dist/esm/mask/utils.js.map +1 -0
  87. package/dist/esm/path-style/shaders.glsl.js +1 -1
  88. package/dist/esm/terrain/height-map-builder.js +92 -0
  89. package/dist/esm/terrain/height-map-builder.js.map +1 -0
  90. package/dist/esm/terrain/shader-module.js +73 -0
  91. package/dist/esm/terrain/shader-module.js.map +1 -0
  92. package/dist/esm/terrain/terrain-cover.js +219 -0
  93. package/dist/esm/terrain/terrain-cover.js.map +1 -0
  94. package/dist/esm/terrain/terrain-effect.js +230 -0
  95. package/dist/esm/terrain/terrain-effect.js.map +1 -0
  96. package/dist/esm/terrain/terrain-pass.js +69 -0
  97. package/dist/esm/terrain/terrain-pass.js.map +1 -0
  98. package/dist/esm/terrain/terrain-picking-pass.js +75 -0
  99. package/dist/esm/terrain/terrain-picking-pass.js.map +1 -0
  100. package/dist/esm/terrain/terrain.js +62 -0
  101. package/dist/esm/terrain/terrain.js.map +1 -0
  102. package/dist/esm/terrain/utils.js +21 -0
  103. package/dist/esm/terrain/utils.js.map +1 -0
  104. package/dist/esm/utils/debug.js +61 -0
  105. package/dist/esm/utils/debug.js.map +1 -0
  106. package/dist.min.js +1359 -1
  107. package/package.json +6 -4
  108. package/src/brushing/brushing.ts +0 -11
  109. package/src/clip/clip.ts +7 -6
  110. package/src/collide/collide-effect.ts +258 -0
  111. package/src/collide/collide-pass.ts +35 -0
  112. package/src/collide/collide.ts +79 -0
  113. package/src/collide/shader-module.ts +113 -0
  114. package/src/common/projection-utils.ts +187 -0
  115. package/src/data-filter/shader-module.ts +7 -1
  116. package/src/fill-style/fill-style.ts +15 -53
  117. package/src/fill-style/shaders.glsl.ts +6 -7
  118. package/src/index.ts +4 -0
  119. package/src/mask/mask-effect.ts +255 -0
  120. package/src/mask/mask-pass.ts +67 -0
  121. package/src/mask/mask.ts +17 -7
  122. package/src/mask/shader-module.ts +7 -1
  123. package/src/mask/utils.ts +148 -0
  124. package/src/terrain/height-map-builder.ts +120 -0
  125. package/src/terrain/shader-module.ts +161 -0
  126. package/src/terrain/terrain-cover.ts +225 -0
  127. package/src/terrain/terrain-effect.ts +222 -0
  128. package/src/terrain/terrain-pass.ts +86 -0
  129. package/src/terrain/terrain-picking-pass.ts +87 -0
  130. package/src/terrain/terrain.ts +74 -0
  131. package/src/terrain/utils.ts +29 -0
  132. package/src/utils/debug.ts +59 -0
  133. package/typed/brushing/brushing.d.ts +0 -1
  134. package/typed/brushing/brushing.d.ts.map +1 -1
  135. package/typed/brushing/brushing.js +83 -0
  136. package/typed/brushing/shader-module.js +116 -0
  137. package/typed/clip/clip.d.ts +5 -2
  138. package/typed/clip/clip.d.ts.map +1 -1
  139. package/typed/clip/clip.js +114 -0
  140. package/typed/collide/collide-effect.d.ts +28 -0
  141. package/typed/collide/collide-effect.d.ts.map +1 -0
  142. package/typed/collide/collide-pass.d.ts +14 -0
  143. package/typed/collide/collide-pass.d.ts.map +1 -0
  144. package/typed/collide/collide.d.ts +41 -0
  145. package/typed/collide/collide.d.ts.map +1 -0
  146. package/typed/collide/shader-module.d.ts +4 -0
  147. package/typed/collide/shader-module.d.ts.map +1 -0
  148. package/typed/common/projection-utils.d.ts +27 -0
  149. package/typed/common/projection-utils.d.ts.map +1 -0
  150. package/typed/data-filter/aggregator.js +98 -0
  151. package/typed/data-filter/data-filter.js +159 -0
  152. package/typed/data-filter/shader-module.d.ts.map +1 -1
  153. package/typed/data-filter/shader-module.js +152 -0
  154. package/typed/fill-style/fill-style.d.ts +14 -5
  155. package/typed/fill-style/fill-style.d.ts.map +1 -1
  156. package/typed/fill-style/fill-style.js +152 -0
  157. package/typed/fill-style/shaders.glsl.d.ts.map +1 -1
  158. package/typed/fill-style/shaders.glsl.js +95 -0
  159. package/typed/fp64/fp64.js +34 -0
  160. package/typed/fp64/project64.glsl.js +100 -0
  161. package/typed/fp64/project64.js +50 -0
  162. package/typed/index.d.ts +4 -0
  163. package/typed/index.d.ts.map +1 -1
  164. package/typed/index.js +9 -0
  165. package/typed/mask/mask-effect.d.ts +42 -0
  166. package/typed/mask/mask-effect.d.ts.map +1 -0
  167. package/typed/mask/mask-pass.d.ts +19 -0
  168. package/typed/mask/mask-pass.d.ts.map +1 -0
  169. package/typed/mask/mask.d.ts +9 -3
  170. package/typed/mask/mask.d.ts.map +1 -1
  171. package/typed/mask/mask.js +48 -0
  172. package/typed/mask/shader-module.d.ts.map +1 -1
  173. package/typed/mask/shader-module.js +73 -0
  174. package/typed/mask/utils.d.ts +13 -0
  175. package/typed/mask/utils.d.ts.map +1 -0
  176. package/typed/path-style/path-style.js +107 -0
  177. package/typed/path-style/shaders.glsl.js +93 -0
  178. package/typed/terrain/height-map-builder.d.ts +38 -0
  179. package/typed/terrain/height-map-builder.d.ts.map +1 -0
  180. package/typed/terrain/height-map.d.ts +27 -0
  181. package/typed/terrain/height-map.d.ts.map +1 -0
  182. package/typed/terrain/shader-module-offset.d.ts +4 -0
  183. package/typed/terrain/shader-module-offset.d.ts.map +1 -0
  184. package/typed/terrain/shader-module-source.d.ts +4 -0
  185. package/typed/terrain/shader-module-source.d.ts.map +1 -0
  186. package/typed/terrain/shader-module.d.ts +31 -0
  187. package/typed/terrain/shader-module.d.ts.map +1 -0
  188. package/typed/terrain/terrain-cover.d.ts +46 -0
  189. package/typed/terrain/terrain-cover.d.ts.map +1 -0
  190. package/typed/terrain/terrain-effect.d.ts +29 -0
  191. package/typed/terrain/terrain-effect.d.ts.map +1 -0
  192. package/typed/terrain/terrain-pass.d.ts +11 -0
  193. package/typed/terrain/terrain-pass.d.ts.map +1 -0
  194. package/typed/terrain/terrain-picking-pass.d.ts +19 -0
  195. package/typed/terrain/terrain-picking-pass.d.ts.map +1 -0
  196. package/typed/terrain/terrain.d.ts +21 -0
  197. package/typed/terrain/terrain.d.ts.map +1 -0
  198. package/typed/terrain/utils.d.ts +5 -0
  199. package/typed/terrain/utils.d.ts.map +1 -0
  200. package/typed/utils/debug.d.ts +6 -0
  201. package/typed/utils/debug.d.ts.map +1 -0
@@ -0,0 +1,187 @@
1
+ import {WebMercatorViewport, OrthographicViewport} from '@deck.gl/core';
2
+ import type {Layer, Viewport} from '@deck.gl/core';
3
+
4
+ /** Bounds in CARTESIAN coordinates */
5
+ export type Bounds = [minX: number, minY: number, maxX: number, maxY: number];
6
+
7
+ /*
8
+ * Compute the union of bounds from multiple layers
9
+ * Returns bounds in CARTESIAN coordinates
10
+ */
11
+ export function joinLayerBounds(
12
+ /** The layers to combine */
13
+ layers: Layer[],
14
+ /** A Viewport instance that is used to determine the type of the view */
15
+ viewport: Viewport
16
+ ): Bounds | null {
17
+ // Join the bounds of layer data
18
+ const bounds: Bounds = [Infinity, Infinity, -Infinity, -Infinity];
19
+ for (const layer of layers) {
20
+ const layerBounds = layer.getBounds();
21
+ if (layerBounds) {
22
+ const bottomLeftCommon = layer.projectPosition(layerBounds[0], {viewport, autoOffset: false});
23
+ const topRightCommon = layer.projectPosition(layerBounds[1], {viewport, autoOffset: false});
24
+
25
+ bounds[0] = Math.min(bounds[0], bottomLeftCommon[0]);
26
+ bounds[1] = Math.min(bounds[1], bottomLeftCommon[1]);
27
+ bounds[2] = Math.max(bounds[2], topRightCommon[0]);
28
+ bounds[3] = Math.max(bounds[3], topRightCommon[1]);
29
+ }
30
+ }
31
+
32
+ if (Number.isFinite(bounds[0])) {
33
+ return bounds;
34
+ }
35
+ return null;
36
+ }
37
+
38
+ const MAX_VIEWPORT_SIZE = 2048;
39
+
40
+ /** Construct a viewport that just covers the target bounds. Used for rendering to common space indexed texture. */
41
+ export function makeViewport(opts: {
42
+ /** The cartesian bounds of layers that will render into this texture */
43
+ bounds: Bounds;
44
+ /** Target width. If not specified, will be deduced from zoom */
45
+ width?: number;
46
+ /** Target height. If not specified, will be deduced from zoom */
47
+ height?: number;
48
+ /** Target zoom. If not specified, will be deduced from width and height */
49
+ zoom?: number;
50
+ /** Border around the viewport in pixels */
51
+ border?: number;
52
+ /** A viewport used to determine the output type */
53
+ viewport: Viewport;
54
+ }): Viewport | null {
55
+ const {bounds, viewport, border = 0} = opts;
56
+ const {isGeospatial} = viewport;
57
+
58
+ if (bounds[2] <= bounds[0] || bounds[3] <= bounds[1]) {
59
+ return null;
60
+ }
61
+
62
+ const centerWorld = viewport.unprojectPosition([
63
+ (bounds[0] + bounds[2]) / 2,
64
+ (bounds[1] + bounds[3]) / 2,
65
+ 0
66
+ ]);
67
+
68
+ const maxSize = MAX_VIEWPORT_SIZE - border * 2;
69
+ let {width, height, zoom} = opts;
70
+ if (zoom === undefined) {
71
+ // Use width and height to determine zoom
72
+ const scale = Math.min(width! / (bounds[2] - bounds[0]), height! / (bounds[3] - bounds[1]));
73
+ zoom = Math.min(Math.log2(scale), 20);
74
+ } else if (!width || !height) {
75
+ // Use zoom to determine width and height
76
+ const scale = 2 ** zoom;
77
+ width = Math.round(Math.abs(bounds[2] - bounds[0]) * scale);
78
+ height = Math.round(Math.abs(bounds[3] - bounds[1]) * scale);
79
+ if (width > maxSize || height > maxSize) {
80
+ const r = maxSize / Math.max(width, height);
81
+ width = Math.round(width * r);
82
+ height = Math.round(height * r);
83
+ zoom += Math.log2(r);
84
+ }
85
+ }
86
+
87
+ // TODO - find a more generic way to construct this viewport
88
+ // Geospatial viewports may not be web-mercator
89
+ return isGeospatial
90
+ ? new WebMercatorViewport({
91
+ x: border,
92
+ y: border,
93
+ width,
94
+ height,
95
+ longitude: centerWorld[0],
96
+ latitude: centerWorld[1],
97
+ zoom,
98
+ orthographic: true
99
+ })
100
+ : new OrthographicViewport({
101
+ x: border,
102
+ y: border,
103
+ width,
104
+ height,
105
+ target: centerWorld,
106
+ zoom,
107
+ flipY: false
108
+ });
109
+ }
110
+
111
+ /** Returns viewport bounds in CARTESIAN coordinates */
112
+ export function getViewportBounds(viewport: Viewport, zRange?: [number, number]): Bounds {
113
+ // Viewport bounds in world coordinates
114
+ let viewportBoundsWorld: Bounds;
115
+ if (zRange && zRange.length === 2) {
116
+ const [minZ, maxZ] = zRange;
117
+ const bounds0 = viewport.getBounds({z: minZ});
118
+ const bounds1 = viewport.getBounds({z: maxZ});
119
+ viewportBoundsWorld = [
120
+ Math.min(bounds0[0], bounds1[0]),
121
+ Math.min(bounds0[1], bounds1[1]),
122
+ Math.max(bounds0[2], bounds1[2]),
123
+ Math.max(bounds0[3], bounds1[3])
124
+ ];
125
+ } else {
126
+ viewportBoundsWorld = viewport.getBounds();
127
+ }
128
+
129
+ // Viewport bounds in cartesian coordinates
130
+ const viewportBottomLeftCommon = viewport.projectPosition(viewportBoundsWorld.slice(0, 2));
131
+ const viewportTopRightCommon = viewport.projectPosition(viewportBoundsWorld.slice(2, 4));
132
+ return [
133
+ viewportBottomLeftCommon[0],
134
+ viewportBottomLeftCommon[1],
135
+ viewportTopRightCommon[0],
136
+ viewportTopRightCommon[1]
137
+ ];
138
+ }
139
+
140
+ /*
141
+ * Determine the common space bounds that best cover the given data for the given viewport
142
+ * Returns bounds in CARTESIAN coordinates
143
+ */
144
+ export function getRenderBounds(
145
+ layerBounds: Bounds,
146
+ viewport: Viewport,
147
+ zRange?: [number, number]
148
+ ): Bounds {
149
+ if (!layerBounds) {
150
+ return [0, 0, 1, 1];
151
+ }
152
+
153
+ const viewportBounds = getViewportBounds(viewport, zRange);
154
+ // Expand viewport bounds by 2X. Heurestically chosen to avoid masking
155
+ // errors when mask is partially out of view
156
+ const paddedBounds = doubleBounds(viewportBounds);
157
+
158
+ // When bounds of the layers are smaller than the viewport bounds simply use
159
+ // mask bounds, so as to maximize resolution & avoid rerenders
160
+ if (
161
+ layerBounds[2] - layerBounds[0] <= paddedBounds[2] - paddedBounds[0] &&
162
+ layerBounds[3] - layerBounds[1] <= paddedBounds[3] - paddedBounds[1]
163
+ ) {
164
+ return layerBounds;
165
+ }
166
+
167
+ // As viewport shrinks, to avoid pixelation along mask edges
168
+ // we need to reduce the bounds and only render the visible portion
169
+ // of the mask.
170
+ // We pad the viewport bounds to capture the section
171
+ // of the mask just outside the viewport to correctly maskByInstance.
172
+ // Intersect mask & padded viewport bounds
173
+ return [
174
+ Math.max(layerBounds[0], paddedBounds[0]),
175
+ Math.max(layerBounds[1], paddedBounds[1]),
176
+ Math.min(layerBounds[2], paddedBounds[2]),
177
+ Math.min(layerBounds[3], paddedBounds[3])
178
+ ];
179
+ }
180
+
181
+ function doubleBounds(bounds: Bounds): Bounds {
182
+ const dx = bounds[2] - bounds[0];
183
+ const dy = bounds[3] - bounds[1];
184
+ const centerX = (bounds[0] + bounds[2]) / 2;
185
+ const centerY = (bounds[1] + bounds[3]) / 2;
186
+ return [centerX - dx, centerY - dy, centerX + dx, centerY + dy];
187
+ }
@@ -1,4 +1,4 @@
1
- import {_ShaderModule as ShaderModule} from '@deck.gl/core/typed';
1
+ import {_ShaderModule as ShaderModule} from '@deck.gl/core';
2
2
 
3
3
  import type {DataFilterExtensionProps} from './data-filter';
4
4
 
@@ -146,6 +146,12 @@ const inject = {
146
146
  #endif
147
147
  `,
148
148
 
149
+ 'vs:#main-end': `
150
+ if (dataFilter_value == 0.0) {
151
+ gl_Position = vec4(0.);
152
+ }
153
+ `,
154
+
149
155
  'vs:DECKGL_FILTER_SIZE': `
150
156
  if (filter_transformSize) {
151
157
  size = size * dataFilter_value;
@@ -15,8 +15,15 @@ import type {
15
15
 
16
16
  const defaultProps = {
17
17
  fillPatternEnabled: true,
18
- fillPatternAtlas: null,
19
- fillPatternMapping: null,
18
+ fillPatternAtlas: {
19
+ type: 'image',
20
+ value: null,
21
+ async: true,
22
+ parameters: {
23
+ [GL.TEXTURE_MIN_FILTER]: GL.LINEAR
24
+ }
25
+ },
26
+ fillPatternMapping: {type: 'object', value: {}, async: true},
20
27
  fillPatternMask: true,
21
28
  getFillPattern: {type: 'accessor', value: d => d.pattern},
22
29
  getFillPatternScale: {type: 'accessor', value: 1},
@@ -29,7 +36,7 @@ export type FillStyleExtensionProps<DataT = any> = {
29
36
  */
30
37
  fillPatternEnabled?: boolean;
31
38
  /** Sprite image url or texture that packs all your patterns into one layout. */
32
- fillPatternAtlas?: Texture;
39
+ fillPatternAtlas?: string | Texture;
33
40
  /** Pattern names mapped to pattern definitions, or a url that points to a JSON file. */
34
41
  fillPatternMapping?:
35
42
  | string
@@ -71,15 +78,6 @@ type FillStyleExtensionOptions = {
71
78
  pattern: boolean;
72
79
  };
73
80
 
74
- const DEFAULT_TEXTURE_PARAMETERS = {
75
- [GL.TEXTURE_MIN_FILTER]: GL.LINEAR,
76
- // GL.LINEAR is the default value but explicitly set it here
77
- [GL.TEXTURE_MAG_FILTER]: GL.LINEAR,
78
- // for texture boundary artifact
79
- [GL.TEXTURE_WRAP_S]: GL.CLAMP_TO_EDGE,
80
- [GL.TEXTURE_WRAP_T]: GL.CLAMP_TO_EDGE
81
- };
82
-
83
81
  /** Adds selected features to layers that render a "fill", such as the `PolygonLayer` and `ScatterplotLayer`. */
84
82
  export default class FillStyleExtension extends LayerExtension<FillStyleExtensionOptions> {
85
83
  static defaultProps = defaultProps;
@@ -170,11 +168,8 @@ export default class FillStyleExtension extends LayerExtension<FillStyleExtensio
170
168
  return;
171
169
  }
172
170
 
173
- if (props.fillPatternAtlas && props.fillPatternAtlas !== oldProps.fillPatternAtlas) {
174
- extension.loadPatternAtlas.call(this);
175
- }
176
171
  if (props.fillPatternMapping && props.fillPatternMapping !== oldProps.fillPatternMapping) {
177
- extension.loadPatternMapping.call(this);
172
+ this.getAttributeManager()!.invalidate('getFillPattern');
178
173
  }
179
174
  }
180
175
 
@@ -183,9 +178,9 @@ export default class FillStyleExtension extends LayerExtension<FillStyleExtensio
183
178
  return;
184
179
  }
185
180
 
186
- const {patternTexture} = this.state;
181
+ const {fillPatternAtlas} = this.props;
187
182
  this.setModuleParameters({
188
- fillPatternTexture: patternTexture || this.state.emptyTexture
183
+ fillPatternTexture: fillPatternAtlas || this.state.emptyTexture
189
184
  });
190
185
  }
191
186
 
@@ -195,42 +190,9 @@ export default class FillStyleExtension extends LayerExtension<FillStyleExtensio
195
190
  emptyTexture?.delete();
196
191
  }
197
192
 
198
- async loadPatternAtlas(this: Layer<FillStyleExtensionProps>) {
199
- const {fillPatternAtlas, fetch} = this.props;
200
- this.state.patternTexture?.delete();
201
- this.setState({patternTexture: null});
202
- let image = fillPatternAtlas;
203
- if (typeof image === 'string') {
204
- image = await fetch(image, {propName: 'fillPatternAtlas', layer: this});
205
- }
206
- const patternTexture =
207
- image instanceof Texture2D
208
- ? image
209
- : new Texture2D(this.context.gl, {
210
- data: image,
211
- parameters: DEFAULT_TEXTURE_PARAMETERS
212
- });
213
- this.setState({patternTexture});
214
- }
215
-
216
- async loadPatternMapping(this: Layer<FillStyleExtensionProps>) {
217
- const {fillPatternMapping, fetch} = this.props;
218
- this.setState({patternMapping: null});
219
- let patternMapping = fillPatternMapping;
220
- if (typeof patternMapping === 'string') {
221
- patternMapping = await fetch(patternMapping, {
222
- propName: 'fillPatternMapping',
223
- layer: this
224
- });
225
- }
226
- this.setState({patternMapping});
227
- this.getAttributeManager()!.invalidate('getFillPattern');
228
- this.setNeedsUpdate();
229
- }
230
-
231
193
  getPatternFrame(this: Layer<FillStyleExtensionProps>, name: string) {
232
- const {patternMapping} = this.state;
233
- const def = patternMapping && patternMapping[name];
194
+ const {fillPatternMapping} = this.getCurrentLayer()!.props;
195
+ const def = fillPatternMapping && fillPatternMapping[name];
234
196
  return def ? [def.x, def.y, def.width, def.height] : [0, 0, 0, 0];
235
197
  }
236
198
  }
@@ -26,7 +26,7 @@ uniform vec2 fill_patternTextureSize;
26
26
 
27
27
  varying vec2 fill_uv;
28
28
  varying vec4 fill_patternBounds;
29
- varying vec3 fill_patternPlacement;
29
+ varying vec4 fill_patternPlacement;
30
30
  `;
31
31
 
32
32
  const patternFs = `
@@ -37,7 +37,7 @@ uniform vec2 fill_uvCoordinateOrigin;
37
37
  uniform vec2 fill_uvCoordinateOrigin64Low;
38
38
 
39
39
  varying vec4 fill_patternBounds;
40
- varying vec3 fill_patternPlacement;
40
+ varying vec4 fill_patternPlacement;
41
41
  varying vec2 fill_uv;
42
42
 
43
43
  const float FILL_UV_SCALE = 512.0 / 40000000.0;
@@ -52,13 +52,13 @@ const inject = {
52
52
  if (fill_patternEnabled) {
53
53
  fill_patternBounds = FILL_PATTERN_FRAME_ATTRIB / vec4(fill_patternTextureSize, fill_patternTextureSize);
54
54
  fill_patternPlacement.xy = FILL_PATTERN_OFFSET_ATTRIB;
55
- fill_patternPlacement.z = FILL_PATTERN_SCALE_ATTRIB * FILL_PATTERN_FRAME_ATTRIB.w;
55
+ fill_patternPlacement.zw = FILL_PATTERN_SCALE_ATTRIB * FILL_PATTERN_FRAME_ATTRIB.zw;
56
56
  }
57
57
  `,
58
58
 
59
59
  'fs:DECKGL_FILTER_COLOR': `
60
60
  if (fill_patternEnabled) {
61
- float scale = FILL_UV_SCALE * fill_patternPlacement.z;
61
+ vec2 scale = FILL_UV_SCALE * fill_patternPlacement.zw;
62
62
  vec2 patternUV = mod(mod(fill_uvCoordinateOrigin, scale) + fill_uvCoordinateOrigin64Low + fill_uv, scale) / scale;
63
63
  patternUV = mod(fill_patternPlacement.xy + patternUV, 1.0);
64
64
 
@@ -99,10 +99,9 @@ function getPatternUniforms(
99
99
  };
100
100
  }
101
101
  if ('viewport' in opts) {
102
- const {viewport, fillPatternMask = true, fillPatternEnabled = true} = opts;
103
- const {project_uCoordinateOrigin} = uniforms as ProjectUniforms;
102
+ const {fillPatternMask = true, fillPatternEnabled = true} = opts;
103
+ const {project_uCommonOrigin: coordinateOriginCommon} = uniforms as ProjectUniforms;
104
104
 
105
- const coordinateOriginCommon = viewport.projectPosition(project_uCoordinateOrigin);
106
105
  const coordinateOriginCommon64Low = [
107
106
  fp64LowPart(coordinateOriginCommon[0]),
108
107
  fp64LowPart(coordinateOriginCommon[1])
package/src/index.ts CHANGED
@@ -4,7 +4,9 @@ export {default as Fp64Extension} from './fp64/fp64';
4
4
  export {default as PathStyleExtension} from './path-style/path-style';
5
5
  export {default as FillStyleExtension} from './fill-style/fill-style';
6
6
  export {default as ClipExtension} from './clip/clip';
7
+ export {default as CollideExtension} from './collide/collide';
7
8
  export {default as MaskExtension} from './mask/mask';
9
+ export {default as TerrainExtension} from './terrain/terrain';
8
10
 
9
11
  // Shader module
10
12
  export {default as project64} from './fp64/project64';
@@ -15,4 +17,6 @@ export type {DataFilterExtensionProps} from './data-filter/data-filter';
15
17
  export type {PathStyleExtensionProps} from './path-style/path-style';
16
18
  export type {FillStyleExtensionProps} from './fill-style/fill-style';
17
19
  export type {ClipExtensionProps} from './clip/clip';
20
+ export type {CollideExtensionProps} from './collide/collide';
18
21
  export type {MaskExtensionProps} from './mask/mask';
22
+ export type {TerrainExtensionProps} from './terrain/terrain';
@@ -0,0 +1,255 @@
1
+ import {Layer, Viewport, Effect, PreRenderOptions, CoordinateSystem, log} from '@deck.gl/core';
2
+ import {Texture2D} from '@luma.gl/core';
3
+ import {equals} from '@math.gl/core';
4
+ import MaskPass from './mask-pass';
5
+ import {getMaskBounds, getMaskViewport, MaskBounds} from './utils';
6
+ // import {debugFBO} from '../utils/debug';
7
+
8
+ type Mask = {
9
+ /** The channel index */
10
+ index: number;
11
+ bounds: MaskBounds;
12
+ coordinateOrigin: [number, number, number];
13
+ coordinateSystem: CoordinateSystem;
14
+ };
15
+
16
+ type Channel = {
17
+ id: string;
18
+ index: number;
19
+ layers: Layer[];
20
+ bounds: MaskBounds;
21
+ maskBounds: MaskBounds;
22
+ layerBounds: MaskBounds[];
23
+ coordinateOrigin: [number, number, number];
24
+ coordinateSystem: CoordinateSystem;
25
+ };
26
+
27
+ export type MaskPreRenderStats = {
28
+ didRender: boolean;
29
+ };
30
+
31
+ // Class to manage mask effect
32
+ export default class MaskEffect implements Effect {
33
+ id = 'mask-effect';
34
+ props = null;
35
+ useInPicking = true;
36
+ order = 0;
37
+
38
+ private dummyMaskMap?: Texture2D;
39
+ private channels: (Channel | null)[] = [];
40
+ private masks: Record<string, Mask> | null = null;
41
+ private maskPass?: MaskPass;
42
+ private maskMap?: Texture2D;
43
+ private lastViewport?: Viewport;
44
+
45
+ preRender(
46
+ gl: WebGLRenderingContext,
47
+ {layers, layerFilter, viewports, onViewportActive, views, isPicking}: PreRenderOptions
48
+ ): MaskPreRenderStats {
49
+ let didRender = false;
50
+ if (!this.dummyMaskMap) {
51
+ this.dummyMaskMap = new Texture2D(gl, {
52
+ width: 1,
53
+ height: 1
54
+ });
55
+ }
56
+
57
+ if (isPicking) {
58
+ // Do not update on picking pass
59
+ return {didRender};
60
+ }
61
+
62
+ const maskLayers = layers.filter(l => l.props.visible && l.props.operation.includes('mask'));
63
+ if (maskLayers.length === 0) {
64
+ this.masks = null;
65
+ this.channels.length = 0;
66
+ return {didRender};
67
+ }
68
+ this.masks = {};
69
+
70
+ if (!this.maskPass) {
71
+ this.maskPass = new MaskPass(gl, {id: 'default-mask'});
72
+ this.maskMap = this.maskPass.maskMap;
73
+ }
74
+
75
+ // Map layers to channels
76
+ const channelMap = this._sortMaskChannels(maskLayers);
77
+ // TODO - support multiple views
78
+ const viewport = viewports[0];
79
+ const viewportChanged = !this.lastViewport || !this.lastViewport.equals(viewport);
80
+
81
+ for (const maskId in channelMap) {
82
+ const result = this._renderChannel(channelMap[maskId], {
83
+ layerFilter,
84
+ onViewportActive,
85
+ views,
86
+ viewport,
87
+ viewportChanged
88
+ });
89
+ didRender ||= result;
90
+ }
91
+
92
+ // debugFBO(this.maskMap, {opaque: true});
93
+ return {didRender};
94
+ }
95
+
96
+ private _renderChannel(
97
+ channelInfo: Channel,
98
+ {
99
+ layerFilter,
100
+ onViewportActive,
101
+ views,
102
+ viewport,
103
+ viewportChanged
104
+ }: {
105
+ layerFilter: PreRenderOptions['layerFilter'];
106
+ onViewportActive: PreRenderOptions['onViewportActive'];
107
+ views: PreRenderOptions['views'];
108
+ viewport: Viewport;
109
+ viewportChanged: boolean;
110
+ }
111
+ ): boolean {
112
+ let didRender = false;
113
+ const oldChannelInfo = this.channels[channelInfo.index];
114
+ if (!oldChannelInfo) {
115
+ return didRender;
116
+ }
117
+
118
+ const maskChanged =
119
+ // If a channel is new
120
+ channelInfo === oldChannelInfo ||
121
+ // If sublayers have changed
122
+ oldChannelInfo.layers.length !== channelInfo.layers.length ||
123
+ // If a sublayer's positions have been updated, the cached bounds will change shallowly
124
+ channelInfo.layerBounds.some((b, i) => b !== oldChannelInfo.layerBounds[i]);
125
+
126
+ channelInfo.bounds = oldChannelInfo.bounds;
127
+ channelInfo.maskBounds = oldChannelInfo.maskBounds;
128
+ this.channels[channelInfo.index] = channelInfo;
129
+
130
+ if (maskChanged || viewportChanged) {
131
+ // Recalculate mask bounds
132
+ this.lastViewport = viewport;
133
+
134
+ channelInfo.bounds = getMaskBounds({layers: channelInfo.layers, viewport});
135
+
136
+ if (maskChanged || !equals(channelInfo.bounds, oldChannelInfo.bounds)) {
137
+ // Rerender mask FBO
138
+ const {maskPass, maskMap} = this;
139
+
140
+ const maskViewport = getMaskViewport({
141
+ bounds: channelInfo.bounds,
142
+ viewport,
143
+ width: maskMap.width,
144
+ height: maskMap.height
145
+ });
146
+
147
+ channelInfo.maskBounds = maskViewport ? maskViewport.getBounds() : [0, 0, 1, 1];
148
+
149
+ // @ts-ignore (2532) This method is only called from preRender where maskPass is defined
150
+ maskPass.render({
151
+ pass: 'mask',
152
+ channel: channelInfo.index,
153
+ layers: channelInfo.layers,
154
+ layerFilter,
155
+ viewports: maskViewport ? [maskViewport] : [],
156
+ onViewportActive,
157
+ views,
158
+ moduleParameters: {
159
+ devicePixelRatio: 1
160
+ }
161
+ });
162
+
163
+ didRender = true;
164
+ }
165
+ }
166
+
167
+ // @ts-ignore (2532) This method is only called from preRender where masks is defined
168
+ this.masks[channelInfo.id] = {
169
+ index: channelInfo.index,
170
+ bounds: channelInfo.maskBounds,
171
+ coordinateOrigin: channelInfo.coordinateOrigin,
172
+ coordinateSystem: channelInfo.coordinateSystem
173
+ };
174
+
175
+ return didRender;
176
+ }
177
+
178
+ /**
179
+ * Find a channel to render each mask into
180
+ * If a maskId already exists, diff and update the existing channel
181
+ * Otherwise replace a removed mask
182
+ * Otherwise create a new channel
183
+ * Returns a map from mask layer id to channel info
184
+ */
185
+ private _sortMaskChannels(maskLayers: Layer[]): Record<string, Channel> {
186
+ const channelMap = {};
187
+ let channelCount = 0;
188
+ for (const layer of maskLayers) {
189
+ const {id} = layer.root;
190
+ let channelInfo = channelMap[id];
191
+ if (!channelInfo) {
192
+ if (++channelCount > 4) {
193
+ log.warn('Too many mask layers. The max supported is 4')();
194
+ continue; // eslint-disable-line no-continue
195
+ }
196
+ channelInfo = {
197
+ id,
198
+ index: this.channels.findIndex(c => c?.id === id),
199
+ layers: [],
200
+ layerBounds: [],
201
+ coordinateOrigin: layer.root.props.coordinateOrigin,
202
+ coordinateSystem: layer.root.props.coordinateSystem
203
+ };
204
+ channelMap[id] = channelInfo;
205
+ }
206
+ channelInfo.layers.push(layer);
207
+ channelInfo.layerBounds.push(layer.getBounds());
208
+ }
209
+
210
+ for (let i = 0; i < 4; i++) {
211
+ const channelInfo = this.channels[i];
212
+ if (!channelInfo || !(channelInfo.id in channelMap)) {
213
+ // The mask id at this channel no longer exists
214
+ this.channels[i] = null;
215
+ }
216
+ }
217
+
218
+ for (const maskId in channelMap) {
219
+ const channelInfo = channelMap[maskId];
220
+
221
+ if (channelInfo.index < 0) {
222
+ channelInfo.index = this.channels.findIndex(c => !c);
223
+ this.channels[channelInfo.index] = channelInfo;
224
+ }
225
+ }
226
+ return channelMap;
227
+ }
228
+
229
+ getModuleParameters(): {
230
+ maskMap: Texture2D;
231
+ maskChannels: Record<string, Mask> | null;
232
+ } {
233
+ return {
234
+ maskMap: this.masks ? this.maskMap : this.dummyMaskMap,
235
+ maskChannels: this.masks
236
+ };
237
+ }
238
+
239
+ cleanup(): void {
240
+ if (this.dummyMaskMap) {
241
+ this.dummyMaskMap.delete();
242
+ this.dummyMaskMap = undefined;
243
+ }
244
+
245
+ if (this.maskPass) {
246
+ this.maskPass.delete();
247
+ this.maskPass = undefined;
248
+ this.maskMap = undefined;
249
+ }
250
+
251
+ this.lastViewport = undefined;
252
+ this.masks = null;
253
+ this.channels.length = 0;
254
+ }
255
+ }
@@ -0,0 +1,67 @@
1
+ import {Framebuffer, Texture2D, withParameters} from '@luma.gl/core';
2
+ import {_LayersPass as LayersPass, LayersPassRenderOptions} from '@deck.gl/core';
3
+
4
+ type MaskPassRenderOptions = LayersPassRenderOptions & {
5
+ /** The channel to render into, 0:red, 1:green, 2:blue, 3:alpha */
6
+ channel: number;
7
+ };
8
+
9
+ export default class MaskPass extends LayersPass {
10
+ maskMap: Texture2D;
11
+ fbo: Framebuffer;
12
+
13
+ constructor(gl, props: {id: string; mapSize?: number}) {
14
+ super(gl, props);
15
+
16
+ const {mapSize = 2048} = props;
17
+
18
+ this.maskMap = new Texture2D(gl, {
19
+ width: mapSize,
20
+ height: mapSize,
21
+ parameters: {
22
+ [gl.TEXTURE_MIN_FILTER]: gl.LINEAR,
23
+ [gl.TEXTURE_MAG_FILTER]: gl.LINEAR,
24
+ [gl.TEXTURE_WRAP_S]: gl.CLAMP_TO_EDGE,
25
+ [gl.TEXTURE_WRAP_T]: gl.CLAMP_TO_EDGE
26
+ }
27
+ });
28
+
29
+ this.fbo = new Framebuffer(gl, {
30
+ id: 'maskmap',
31
+ width: mapSize,
32
+ height: mapSize,
33
+ attachments: {
34
+ [gl.COLOR_ATTACHMENT0]: this.maskMap
35
+ }
36
+ });
37
+ }
38
+
39
+ render(options: MaskPassRenderOptions) {
40
+ const gl = this.gl;
41
+
42
+ const colorMask = [false, false, false, false];
43
+ colorMask[options.channel] = true;
44
+
45
+ return withParameters(
46
+ gl,
47
+ {
48
+ clearColor: [255, 255, 255, 255],
49
+ blend: true,
50
+ blendFunc: [gl.ZERO, gl.ONE],
51
+ blendEquation: gl.FUNC_SUBTRACT,
52
+ colorMask,
53
+ depthTest: false
54
+ },
55
+ () => super.render({...options, target: this.fbo, pass: 'mask'})
56
+ );
57
+ }
58
+
59
+ shouldDrawLayer(layer) {
60
+ return layer.props.operation.includes('mask');
61
+ }
62
+
63
+ delete() {
64
+ this.fbo.delete();
65
+ this.maskMap.delete();
66
+ }
67
+ }