@deck.gl/extensions 9.0.0-beta.3 → 9.0.0-beta.4

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,6 +1,6 @@
1
1
  import type { ShaderModule } from '@luma.gl/shadertools';
2
2
  import type { Viewport } from '@deck.gl/core';
3
- import type { BrushingExtensionProps } from './brushing-extension';
3
+ import type { BrushingExtensionProps } from "./brushing-extension.js";
4
4
  type BrushingModuleSettings = {
5
5
  viewport: Viewport;
6
6
  mousePosition?: {
@@ -1 +1 @@
1
- {"version":3,"file":"shader-module.d.ts","sourceRoot":"","sources":["../../src/brushing/shader-module.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,sBAAsB,CAAC;AAEjE,KAAK,sBAAsB,GAAG;IAE5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,CAAC,EAAE;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC;CACxC,GAAG,sBAAsB,CAAC;;AAkF3B,wBA4B0C"}
1
+ {"version":3,"file":"shader-module.d.ts","sourceRoot":"","sources":["../../src/brushing/shader-module.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAC,sBAAsB,EAAC,gCAA6B;AAEjE,KAAK,sBAAsB,GAAG;IAE5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,CAAC,EAAE;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC;CACxC,GAAG,sBAAsB,CAAC;;AAkF3B,wBA4B0C"}
@@ -1,11 +1,17 @@
1
1
  import type { Layer, LayerContext, Accessor, UpdateParameters } from '@deck.gl/core';
2
2
  import { LayerExtension } from '@deck.gl/core';
3
+ type FilterCategory = number | string;
3
4
  export type DataFilterExtensionProps<DataT = any> = {
4
5
  /**
5
6
  * Accessor to retrieve the value for each object that it will be filtered by.
6
7
  * Returns either a number (if `filterSize: 1`) or an array of numbers.
7
8
  */
8
9
  getFilterValue?: Accessor<DataT, number | number[]>;
10
+ /**
11
+ * Accessor to retrieve the category (`number | string`) for each object that it will be filtered by.
12
+ * Returns either a single category (if `filterSize: 1`) or an array of categories.
13
+ */
14
+ getFilterCategory?: Accessor<DataT, FilterCategory | FilterCategory[]>;
9
15
  /**
10
16
  * Enable/disable the data filter. If the data filter is disabled, all objects are rendered.
11
17
  * @default true
@@ -33,6 +39,11 @@ export type DataFilterExtensionProps<DataT = any> = {
33
39
  * @default true
34
40
  */
35
41
  filterTransformColor?: boolean;
42
+ /**
43
+ * The categories which define whether an object should be rendered.
44
+ * @default []
45
+ */
46
+ filterCategories: FilterCategory[] | FilterCategory[][];
36
47
  /**
37
48
  * Only called if the `countItems` option is enabled.
38
49
  */
@@ -44,21 +55,26 @@ export type DataFilterExtensionProps<DataT = any> = {
44
55
  }) => void;
45
56
  };
46
57
  type DataFilterExtensionOptions = {
58
+ /**
59
+ * The size of the category filter (number of columns to filter by). The category filter can show/hide data based on 1-4 properties of each object.
60
+ * @default 1
61
+ */
62
+ categorySize?: 1 | 2 | 3 | 4;
47
63
  /**
48
64
  * The size of the filter (number of columns to filter by). The data filter can show/hide data based on 1-4 numeric properties of each object.
49
65
  * @default 1
50
66
  */
51
- filterSize: number;
67
+ filterSize?: 1 | 2 | 3 | 4;
52
68
  /**
53
69
  * Use 64-bit precision instead of 32-bit.
54
70
  * @default false
55
71
  */
56
- fp64: boolean;
72
+ fp64?: boolean;
57
73
  /**
58
74
  * If `true`, reports the number of filtered objects with the `onFilteredItemsChange` callback.
59
75
  * @default `false`.
60
76
  */
61
- countItems: boolean;
77
+ countItems?: boolean;
62
78
  };
63
79
  /** Adds GPU-based data filtering functionalities to layers. It allows the layer to show/hide objects based on user-defined properties. */
64
80
  export default class DataFilterExtension extends LayerExtension<DataFilterExtensionOptions> {
@@ -67,6 +83,10 @@ export default class DataFilterExtension extends LayerExtension<DataFilterExtens
67
83
  type: string;
68
84
  value: number;
69
85
  };
86
+ getFilterCategory: {
87
+ type: string;
88
+ value: number;
89
+ };
70
90
  onFilteredItemsChange: {
71
91
  type: string;
72
92
  value: null;
@@ -75,16 +95,27 @@ export default class DataFilterExtension extends LayerExtension<DataFilterExtens
75
95
  filterEnabled: boolean;
76
96
  filterRange: number[];
77
97
  filterSoftRange: null;
98
+ filterCategories: number[];
78
99
  filterTransformSize: boolean;
79
100
  filterTransformColor: boolean;
80
101
  };
81
102
  static extensionName: string;
82
- constructor({ filterSize, fp64, countItems }?: Partial<DataFilterExtensionOptions>);
83
103
  getShaders(this: Layer<DataFilterExtensionProps>, extension: this): any;
84
104
  initializeState(this: Layer<DataFilterExtensionProps>, context: LayerContext, extension: this): void;
85
- updateState(this: Layer<DataFilterExtensionProps>, { props, oldProps }: UpdateParameters<Layer<DataFilterExtensionProps>>): void;
105
+ updateState(this: Layer<DataFilterExtensionProps>, { props, oldProps, changeFlags }: UpdateParameters<Layer<DataFilterExtensionProps>>, extension: this): void;
86
106
  draw(this: Layer<DataFilterExtensionProps>, params: any, extension: this): void;
87
107
  finalizeState(this: Layer<DataFilterExtensionProps>): void;
108
+ /**
109
+ * Updates the bitmask used on the GPU to perform the filter based on the
110
+ * `filterCategories` prop. The mapping between categories and bit in the bitmask
111
+ * is performed by `_getCategoryKey()`
112
+ */
113
+ _updateCategoryBitMask(this: Layer<DataFilterExtensionProps>, params: any, extension: this): void;
114
+ /**
115
+ * Returns an index of bit in the bitmask for a given category. If the category has
116
+ * not yet been assigned a bit, a new one is assigned.
117
+ */
118
+ _getCategoryKey(this: Layer<DataFilterExtensionProps>, category: FilterCategory, channel: number): number;
88
119
  }
89
120
  export {};
90
121
  //# sourceMappingURL=data-filter-extension.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-filter-extension.d.ts","sourceRoot":"","sources":["../../src/data-filter/data-filter-extension.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAC,MAAM,eAAe,CAAC;AACnF,OAAO,EAAC,cAAc,EAAC,MAAM,eAAe,CAAC;AAe7C,MAAM,MAAM,wBAAwB,CAAC,KAAK,GAAG,GAAG,IAAI;IAClD;;;OAGG;IACH,cAAc,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IACpD;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IACpD;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC;IAC/D;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;OAEG;IACH,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE;QAC5B,kCAAkC;QAClC,EAAE,EAAE,MAAM,CAAC;QACX,uDAAuD;QACvD,KAAK,EAAE,MAAM,CAAC;KACf,KAAK,IAAI,CAAC;CACZ,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,IAAI,EAAE,OAAO,CAAC;IACd;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AASF,0IAA0I;AAC1I,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,cAAc,CAAC,0BAA0B,CAAC;IACzF,MAAM,CAAC,YAAY;;;;;;;;;;;;;;;MAAgB;IACnC,MAAM,CAAC,aAAa,SAAyB;gBAEjC,EACV,UAAc,EACd,IAAY,EACZ,UAAkB,EACnB,GAAE,OAAO,CAAC,0BAA0B,CAAM;IAQ3C,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,wBAAwB,CAAC,EAAE,SAAS,EAAE,IAAI,GAAG,GAAG;IAYvE,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,wBAAwB,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI;IAyD7F,WAAW,CACT,IAAI,EAAE,KAAK,CAAC,wBAAwB,CAAC,EACrC,EAAC,KAAK,EAAE,QAAQ,EAAC,EAAE,gBAAgB,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAgBtE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,wBAAwB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI;IA0CxE,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,wBAAwB,CAAC;CAQpD"}
1
+ {"version":3,"file":"data-filter-extension.d.ts","sourceRoot":"","sources":["../../src/data-filter/data-filter-extension.ts"],"names":[],"mappings":"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"}
@@ -18,15 +18,17 @@
18
18
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  // THE SOFTWARE.
20
20
  import { GL } from '@luma.gl/constants';
21
- import { LayerExtension } from '@deck.gl/core';
21
+ import { _deepEqual as deepEqual, LayerExtension, log } from '@deck.gl/core';
22
22
  import { shaderModule, shaderModule64 } from "./shader-module.js";
23
23
  import * as aggregator from "./aggregator.js";
24
24
  const defaultProps = {
25
25
  getFilterValue: { type: 'accessor', value: 0 },
26
+ getFilterCategory: { type: 'accessor', value: 0 },
26
27
  onFilteredItemsChange: { type: 'function', value: null, optional: true },
27
28
  filterEnabled: true,
28
29
  filterRange: [-1, 1],
29
30
  filterSoftRange: null,
31
+ filterCategories: [0],
30
32
  filterTransformSize: true,
31
33
  filterTransformColor: true
32
34
  };
@@ -40,29 +42,26 @@ const DATA_TYPE_FROM_SIZE = {
40
42
  export default class DataFilterExtension extends LayerExtension {
41
43
  static { this.defaultProps = defaultProps; }
42
44
  static { this.extensionName = 'DataFilterExtension'; }
43
- constructor({ filterSize = 1, fp64 = false, countItems = false } = {}) {
44
- if (!DATA_TYPE_FROM_SIZE[filterSize]) {
45
- throw new Error('filterSize out of range');
46
- }
47
- super({ filterSize, fp64, countItems });
48
- }
49
45
  getShaders(extension) {
50
- const { filterSize, fp64 } = extension.opts;
46
+ const { categorySize, filterSize, fp64 } = extension.opts;
51
47
  return {
52
48
  modules: [fp64 ? shaderModule64 : shaderModule],
53
49
  defines: {
54
- DATAFILTER_TYPE: DATA_TYPE_FROM_SIZE[filterSize],
50
+ DATACATEGORY_TYPE: DATA_TYPE_FROM_SIZE[categorySize || 1],
51
+ DATACATEGORY_CHANNELS: categorySize,
52
+ DATAFILTER_TYPE: DATA_TYPE_FROM_SIZE[filterSize || 1],
55
53
  DATAFILTER_DOUBLE: Boolean(fp64)
56
54
  }
57
55
  };
58
56
  }
59
57
  initializeState(context, extension) {
60
58
  const attributeManager = this.getAttributeManager();
59
+ const { categorySize, filterSize, fp64 } = extension.opts;
61
60
  if (attributeManager) {
62
61
  attributeManager.add({
63
62
  filterValues: {
64
- size: extension.opts.filterSize,
65
- type: extension.opts.fp64 ? GL.DOUBLE : GL.FLOAT,
63
+ size: filterSize,
64
+ type: fp64 ? GL.DOUBLE : GL.FLOAT,
66
65
  accessor: 'getFilterValue',
67
66
  shaderAttributes: {
68
67
  filterValues: {
@@ -72,6 +71,22 @@ export default class DataFilterExtension extends LayerExtension {
72
71
  divisor: 1
73
72
  }
74
73
  }
74
+ },
75
+ filterCategoryValues: {
76
+ size: categorySize,
77
+ type: GL.FLOAT,
78
+ accessor: 'getFilterCategory',
79
+ transform: categorySize === 1
80
+ ? d => extension._getCategoryKey.call(this, d, 0)
81
+ : d => d.map((x, i) => extension._getCategoryKey.call(this, x, i)),
82
+ shaderAttributes: {
83
+ filterCategoryValues: {
84
+ divisor: 0
85
+ },
86
+ instanceFilterCategoryValues: {
87
+ divisor: 1
88
+ }
89
+ }
75
90
  }
76
91
  });
77
92
  }
@@ -106,35 +121,62 @@ export default class DataFilterExtension extends LayerExtension {
106
121
  this.setState({ filterFBO, filterModel });
107
122
  }
108
123
  }
109
- updateState({ props, oldProps }) {
124
+ updateState({ props, oldProps, changeFlags }, extension) {
125
+ const attributeManager = this.getAttributeManager();
126
+ const { categorySize } = extension.opts;
110
127
  if (this.state.filterModel) {
111
- const attributeManager = this.getAttributeManager();
112
128
  const filterNeedsUpdate =
113
129
  // attributeManager must be defined for filterModel to be set
114
130
  attributeManager.attributes.filterValues.needsUpdate() ||
131
+ attributeManager.attributes.filterCategoryValues?.needsUpdate() ||
115
132
  props.filterEnabled !== oldProps.filterEnabled ||
116
133
  props.filterRange !== oldProps.filterRange ||
117
- props.filterSoftRange !== oldProps.filterSoftRange;
134
+ props.filterSoftRange !== oldProps.filterSoftRange ||
135
+ props.filterCategories !== oldProps.filterCategories;
118
136
  if (filterNeedsUpdate) {
119
137
  this.setState({ filterNeedsUpdate });
120
138
  }
121
139
  }
140
+ if (attributeManager?.attributes.filterCategoryValues) {
141
+ // Update bitmask if accessor or selected categories has changed
142
+ const categoryBitMaskNeedsUpdate = attributeManager.attributes.filterCategoryValues.needsUpdate() ||
143
+ !deepEqual(props.filterCategories, oldProps.filterCategories, 2);
144
+ if (categoryBitMaskNeedsUpdate) {
145
+ this.setState({ categoryBitMaskNeedsUpdate });
146
+ }
147
+ // Need to recreate category map if categorySize has changed
148
+ const resetCategories = changeFlags.dataChanged;
149
+ if (resetCategories) {
150
+ this.setState({
151
+ categoryMap: Array(categorySize)
152
+ .fill(0)
153
+ .map(() => ({}))
154
+ });
155
+ attributeManager.attributes.filterCategoryValues.setNeedsUpdate('categoryMap');
156
+ }
157
+ }
122
158
  }
123
159
  draw(params, extension) {
124
160
  const filterFBO = this.state.filterFBO;
125
161
  const filterModel = this.state.filterModel;
126
162
  const filterNeedsUpdate = this.state.filterNeedsUpdate;
163
+ const categoryBitMaskNeedsUpdate = this.state.categoryBitMaskNeedsUpdate;
127
164
  const { onFilteredItemsChange } = this.props;
165
+ if (categoryBitMaskNeedsUpdate) {
166
+ extension._updateCategoryBitMask.call(this, params, extension);
167
+ }
128
168
  if (filterNeedsUpdate && onFilteredItemsChange && filterModel) {
129
- const { attributes: { filterValues, filterIndices } } = this.getAttributeManager();
169
+ const { attributes: { filterValues, filterCategoryValues, filterIndices } } = this.getAttributeManager();
130
170
  filterModel.setVertexCount(this.getNumInstances());
131
171
  this.context.device.clearWebGL({ framebuffer: filterFBO, color: [0, 0, 0, 0] });
132
172
  filterModel.updateModuleSettings(params.moduleParameters);
133
173
  // @ts-expect-error filterValue and filterIndices should always have buffer value
134
174
  filterModel.setAttributes({
135
175
  ...filterValues.getValue(),
176
+ ...filterCategoryValues?.getValue(),
136
177
  ...filterIndices?.getValue()
137
178
  });
179
+ filterModel.setUniforms(params.uniforms);
138
180
  filterModel.device.withParametersWebGL({
139
181
  framebuffer: filterFBO,
140
182
  // ts-ignore 'readonly' cannot be assigned to the mutable type '[GLBlendEquation, GLBlendEquation]'
@@ -159,4 +201,43 @@ export default class DataFilterExtension extends LayerExtension {
159
201
  filterFBO?.destroy();
160
202
  filterModel?.destroy();
161
203
  }
204
+ /**
205
+ * Updates the bitmask used on the GPU to perform the filter based on the
206
+ * `filterCategories` prop. The mapping between categories and bit in the bitmask
207
+ * is performed by `_getCategoryKey()`
208
+ */
209
+ _updateCategoryBitMask(params, extension) {
210
+ const { categorySize } = extension.opts;
211
+ const { filterCategories } = this.props;
212
+ const categoryBitMask = new Uint32Array([0, 0, 0, 0]);
213
+ const categoryFilters = (categorySize === 1 ? [filterCategories] : filterCategories);
214
+ const maxCategories = categorySize === 1 ? 128 : categorySize === 2 ? 64 : 32;
215
+ for (let c = 0; c < categoryFilters.length; c++) {
216
+ const categoryFilter = categoryFilters[c];
217
+ for (const category of categoryFilter) {
218
+ const key = extension._getCategoryKey.call(this, category, c);
219
+ if (key < maxCategories) {
220
+ const channel = c * (maxCategories / 32) + Math.floor(key / 32);
221
+ categoryBitMask[channel] += Math.pow(2, key % 32); // 1 << key fails for key > 30
222
+ }
223
+ else {
224
+ log.warn(`Exceeded maximum number of categories (${maxCategories})`)();
225
+ }
226
+ }
227
+ }
228
+ /* eslint-disable-next-line camelcase */
229
+ params.uniforms.filter_categoryBitMask = categoryBitMask;
230
+ this.state.categoryBitMaskNeedsUpdate = false;
231
+ }
232
+ /**
233
+ * Returns an index of bit in the bitmask for a given category. If the category has
234
+ * not yet been assigned a bit, a new one is assigned.
235
+ */
236
+ _getCategoryKey(category, channel) {
237
+ const categoryMap = this.state.categoryMap[channel];
238
+ if (!(category in categoryMap)) {
239
+ categoryMap[category] = Object.keys(categoryMap).length;
240
+ }
241
+ return categoryMap[category];
242
+ }
162
243
  }
@@ -1,5 +1,5 @@
1
1
  import type { ShaderModule } from '@luma.gl/shadertools';
2
- import type { DataFilterExtensionProps } from './data-filter-extension';
2
+ import type { DataFilterExtensionProps } from "./data-filter-extension.js";
3
3
  type DataFilterModuleSettings = {
4
4
  extensions: any[];
5
5
  } & DataFilterExtensionProps;
@@ -1 +1 @@
1
- {"version":3,"file":"shader-module.d.ts","sourceRoot":"","sources":["../../src/data-filter/shader-module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,yBAAyB,CAAC;AAmEtE,KAAK,wBAAwB,GAAG;IAC9B,UAAU,EAAE,GAAG,EAAE,CAAC;CACnB,GAAG,wBAAwB,CAAC;AAiG7B,eAAO,MAAM,YAAY,EAAE,YAAY,CAAC,wBAAwB,CAM/D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,YAAY,CAAC,wBAAwB,CAMjE,CAAC"}
1
+ {"version":3,"file":"shader-module.d.ts","sourceRoot":"","sources":["../../src/data-filter/shader-module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAC,wBAAwB,EAAC,mCAAgC;AA8FtE,KAAK,wBAAwB,GAAG;IAC9B,UAAU,EAAE,GAAG,EAAE,CAAC;CACnB,GAAG,wBAAwB,CAAC;AAkG7B,eAAO,MAAM,YAAY,EAAE,YAAY,CAAC,wBAAwB,CAM/D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,YAAY,CAAC,wBAAwB,CAMjE,CAAC"}
@@ -9,13 +9,16 @@ uniform DATAFILTER_TYPE filter_max;
9
9
  uniform bool filter_useSoftMargin;
10
10
  uniform bool filter_enabled;
11
11
  uniform bool filter_transformSize;
12
+ uniform ivec4 filter_categoryBitMask;
12
13
 
13
14
  #ifdef NON_INSTANCED_MODEL
14
15
  #define DATAFILTER_ATTRIB filterValues
15
16
  #define DATAFILTER_ATTRIB_64LOW filterValues64Low
17
+ #define DATACATEGORY_ATTRIB filterCategoryValues
16
18
  #else
17
19
  #define DATAFILTER_ATTRIB instanceFilterValues
18
20
  #define DATAFILTER_ATTRIB_64LOW instanceFilterValues64Low
21
+ #define DATACATEGORY_ATTRIB instanceFilterCategoryValues
19
22
  #endif
20
23
 
21
24
  in DATAFILTER_TYPE DATAFILTER_ATTRIB;
@@ -25,6 +28,7 @@ in DATAFILTER_TYPE DATAFILTER_ATTRIB;
25
28
  uniform DATAFILTER_TYPE filter_min64High;
26
29
  uniform DATAFILTER_TYPE filter_max64High;
27
30
  #endif
31
+ in DATACATEGORY_TYPE DATACATEGORY_ATTRIB;
28
32
 
29
33
  out float dataFilter_value;
30
34
 
@@ -40,7 +44,7 @@ float dataFilter_reduceValue(vec3 value) {
40
44
  float dataFilter_reduceValue(vec4 value) {
41
45
  return min(min(value.x, value.y), min(value.z, value.w));
42
46
  }
43
- void dataFilter_setValue(DATAFILTER_TYPE valueFromMin, DATAFILTER_TYPE valueFromMax) {
47
+ void dataFilter_setValue(DATAFILTER_TYPE valueFromMin, DATAFILTER_TYPE valueFromMax, DATACATEGORY_TYPE category) {
44
48
  if (filter_enabled) {
45
49
  if (filter_useSoftMargin) {
46
50
  dataFilter_value = dataFilter_reduceValue(
@@ -52,6 +56,29 @@ void dataFilter_setValue(DATAFILTER_TYPE valueFromMin, DATAFILTER_TYPE valueFrom
52
56
  step(filter_min, valueFromMin) * step(valueFromMax, filter_max)
53
57
  );
54
58
  }
59
+
60
+ #if DATACATEGORY_CHANNELS == 1 // One 128-bit mask
61
+ int dataFilter_masks = filter_categoryBitMask[int(category / 32.0)];
62
+ #elif DATACATEGORY_CHANNELS == 2 // Two 64-bit masks
63
+ ivec2 dataFilter_masks = ivec2(
64
+ filter_categoryBitMask[int(category.x / 32.0)],
65
+ filter_categoryBitMask[int(category.y / 32.0) + 2]
66
+ );
67
+ #elif DATACATEGORY_CHANNELS == 3 // Three 32-bit masks
68
+ ivec3 dataFilter_masks = filter_categoryBitMask.xyz;
69
+ #else // Four 32-bit masks
70
+ ivec4 dataFilter_masks = filter_categoryBitMask;
71
+ #endif
72
+
73
+ // Shift mask and extract relevant bits
74
+ DATACATEGORY_TYPE dataFilter_bits = DATACATEGORY_TYPE(dataFilter_masks) / pow(DATACATEGORY_TYPE(2.0), mod(category, 32.0));
75
+ dataFilter_bits = mod(floor(dataFilter_bits), 2.0);
76
+
77
+ #if DATACATEGORY_CHANNELS == 1
78
+ if(dataFilter_bits == 0.0) dataFilter_value = 0.0;
79
+ #else
80
+ if(any(equal(dataFilter_bits, DATACATEGORY_TYPE(0.0)))) dataFilter_value = 0.0;
81
+ #endif
55
82
  } else {
56
83
  dataFilter_value = 1.0;
57
84
  }
@@ -120,10 +147,11 @@ const inject = {
120
147
  #ifdef DATAFILTER_DOUBLE
121
148
  dataFilter_setValue(
122
149
  DATAFILTER_ATTRIB - filter_min64High + DATAFILTER_ATTRIB_64LOW,
123
- DATAFILTER_ATTRIB - filter_max64High + DATAFILTER_ATTRIB_64LOW
150
+ DATAFILTER_ATTRIB - filter_max64High + DATAFILTER_ATTRIB_64LOW,
151
+ DATACATEGORY_ATTRIB
124
152
  );
125
153
  #else
126
- dataFilter_setValue(DATAFILTER_ATTRIB, DATAFILTER_ATTRIB);
154
+ dataFilter_setValue(DATAFILTER_ATTRIB, DATAFILTER_ATTRIB, DATACATEGORY_ATTRIB);
127
155
  #endif
128
156
  `,
129
157
  'vs:#main-end': `
package/dist/dist.dev.js CHANGED
@@ -916,13 +916,16 @@ uniform DATAFILTER_TYPE filter_max;
916
916
  uniform bool filter_useSoftMargin;
917
917
  uniform bool filter_enabled;
918
918
  uniform bool filter_transformSize;
919
+ uniform ivec4 filter_categoryBitMask;
919
920
 
920
921
  #ifdef NON_INSTANCED_MODEL
921
922
  #define DATAFILTER_ATTRIB filterValues
922
923
  #define DATAFILTER_ATTRIB_64LOW filterValues64Low
924
+ #define DATACATEGORY_ATTRIB filterCategoryValues
923
925
  #else
924
926
  #define DATAFILTER_ATTRIB instanceFilterValues
925
927
  #define DATAFILTER_ATTRIB_64LOW instanceFilterValues64Low
928
+ #define DATACATEGORY_ATTRIB instanceFilterCategoryValues
926
929
  #endif
927
930
 
928
931
  in DATAFILTER_TYPE DATAFILTER_ATTRIB;
@@ -932,6 +935,7 @@ in DATAFILTER_TYPE DATAFILTER_ATTRIB;
932
935
  uniform DATAFILTER_TYPE filter_min64High;
933
936
  uniform DATAFILTER_TYPE filter_max64High;
934
937
  #endif
938
+ in DATACATEGORY_TYPE DATACATEGORY_ATTRIB;
935
939
 
936
940
  out float dataFilter_value;
937
941
 
@@ -947,7 +951,7 @@ float dataFilter_reduceValue(vec3 value) {
947
951
  float dataFilter_reduceValue(vec4 value) {
948
952
  return min(min(value.x, value.y), min(value.z, value.w));
949
953
  }
950
- void dataFilter_setValue(DATAFILTER_TYPE valueFromMin, DATAFILTER_TYPE valueFromMax) {
954
+ void dataFilter_setValue(DATAFILTER_TYPE valueFromMin, DATAFILTER_TYPE valueFromMax, DATACATEGORY_TYPE category) {
951
955
  if (filter_enabled) {
952
956
  if (filter_useSoftMargin) {
953
957
  dataFilter_value = dataFilter_reduceValue(
@@ -959,6 +963,29 @@ void dataFilter_setValue(DATAFILTER_TYPE valueFromMin, DATAFILTER_TYPE valueFrom
959
963
  step(filter_min, valueFromMin) * step(valueFromMax, filter_max)
960
964
  );
961
965
  }
966
+
967
+ #if DATACATEGORY_CHANNELS == 1 // One 128-bit mask
968
+ int dataFilter_masks = filter_categoryBitMask[int(category / 32.0)];
969
+ #elif DATACATEGORY_CHANNELS == 2 // Two 64-bit masks
970
+ ivec2 dataFilter_masks = ivec2(
971
+ filter_categoryBitMask[int(category.x / 32.0)],
972
+ filter_categoryBitMask[int(category.y / 32.0) + 2]
973
+ );
974
+ #elif DATACATEGORY_CHANNELS == 3 // Three 32-bit masks
975
+ ivec3 dataFilter_masks = filter_categoryBitMask.xyz;
976
+ #else // Four 32-bit masks
977
+ ivec4 dataFilter_masks = filter_categoryBitMask;
978
+ #endif
979
+
980
+ // Shift mask and extract relevant bits
981
+ DATACATEGORY_TYPE dataFilter_bits = DATACATEGORY_TYPE(dataFilter_masks) / pow(DATACATEGORY_TYPE(2.0), mod(category, 32.0));
982
+ dataFilter_bits = mod(floor(dataFilter_bits), 2.0);
983
+
984
+ #if DATACATEGORY_CHANNELS == 1
985
+ if(dataFilter_bits == 0.0) dataFilter_value = 0.0;
986
+ #else
987
+ if(any(equal(dataFilter_bits, DATACATEGORY_TYPE(0.0)))) dataFilter_value = 0.0;
988
+ #endif
962
989
  } else {
963
990
  dataFilter_value = 1.0;
964
991
  }
@@ -1028,10 +1055,11 @@ in float dataFilter_value;
1028
1055
  #ifdef DATAFILTER_DOUBLE
1029
1056
  dataFilter_setValue(
1030
1057
  DATAFILTER_ATTRIB - filter_min64High + DATAFILTER_ATTRIB_64LOW,
1031
- DATAFILTER_ATTRIB - filter_max64High + DATAFILTER_ATTRIB_64LOW
1058
+ DATAFILTER_ATTRIB - filter_max64High + DATAFILTER_ATTRIB_64LOW,
1059
+ DATACATEGORY_ATTRIB
1032
1060
  );
1033
1061
  #else
1034
- dataFilter_setValue(DATAFILTER_ATTRIB, DATAFILTER_ATTRIB);
1062
+ dataFilter_setValue(DATAFILTER_ATTRIB, DATAFILTER_ATTRIB, DATACATEGORY_ATTRIB);
1035
1063
  #endif
1036
1064
  `,
1037
1065
  "vs:#main-end": `
@@ -1166,10 +1194,12 @@ void main() {
1166
1194
  // src/data-filter/data-filter-extension.ts
1167
1195
  var defaultProps2 = {
1168
1196
  getFilterValue: { type: "accessor", value: 0 },
1197
+ getFilterCategory: { type: "accessor", value: 0 },
1169
1198
  onFilteredItemsChange: { type: "function", value: null, optional: true },
1170
1199
  filterEnabled: true,
1171
1200
  filterRange: [-1, 1],
1172
1201
  filterSoftRange: null,
1202
+ filterCategories: [0],
1173
1203
  filterTransformSize: true,
1174
1204
  filterTransformColor: true
1175
1205
  };
@@ -1180,33 +1210,26 @@ void main() {
1180
1210
  4: "vec4"
1181
1211
  };
1182
1212
  var DataFilterExtension = class extends import_core4.LayerExtension {
1183
- constructor({
1184
- filterSize = 1,
1185
- fp64: fp642 = false,
1186
- countItems = false
1187
- } = {}) {
1188
- if (!DATA_TYPE_FROM_SIZE[filterSize]) {
1189
- throw new Error("filterSize out of range");
1190
- }
1191
- super({ filterSize, fp64: fp642, countItems });
1192
- }
1193
1213
  getShaders(extension) {
1194
- const { filterSize, fp64: fp642 } = extension.opts;
1214
+ const { categorySize, filterSize, fp64: fp642 } = extension.opts;
1195
1215
  return {
1196
1216
  modules: [fp642 ? shaderModule64 : shaderModule],
1197
1217
  defines: {
1198
- DATAFILTER_TYPE: DATA_TYPE_FROM_SIZE[filterSize],
1218
+ DATACATEGORY_TYPE: DATA_TYPE_FROM_SIZE[categorySize || 1],
1219
+ DATACATEGORY_CHANNELS: categorySize,
1220
+ DATAFILTER_TYPE: DATA_TYPE_FROM_SIZE[filterSize || 1],
1199
1221
  DATAFILTER_DOUBLE: Boolean(fp642)
1200
1222
  }
1201
1223
  };
1202
1224
  }
1203
1225
  initializeState(context, extension) {
1204
1226
  const attributeManager = this.getAttributeManager();
1227
+ const { categorySize, filterSize, fp64: fp642 } = extension.opts;
1205
1228
  if (attributeManager) {
1206
1229
  attributeManager.add({
1207
1230
  filterValues: {
1208
- size: extension.opts.filterSize,
1209
- type: extension.opts.fp64 ? GLEnum.DOUBLE : GLEnum.FLOAT,
1231
+ size: filterSize,
1232
+ type: fp642 ? GLEnum.DOUBLE : GLEnum.FLOAT,
1210
1233
  accessor: "getFilterValue",
1211
1234
  shaderAttributes: {
1212
1235
  filterValues: {
@@ -1216,6 +1239,20 @@ void main() {
1216
1239
  divisor: 1
1217
1240
  }
1218
1241
  }
1242
+ },
1243
+ filterCategoryValues: {
1244
+ size: categorySize,
1245
+ type: GLEnum.FLOAT,
1246
+ accessor: "getFilterCategory",
1247
+ transform: categorySize === 1 ? (d) => extension._getCategoryKey.call(this, d, 0) : (d) => d.map((x, i) => extension._getCategoryKey.call(this, x, i)),
1248
+ shaderAttributes: {
1249
+ filterCategoryValues: {
1250
+ divisor: 0
1251
+ },
1252
+ instanceFilterCategoryValues: {
1253
+ divisor: 1
1254
+ }
1255
+ }
1219
1256
  }
1220
1257
  });
1221
1258
  }
@@ -1251,31 +1288,51 @@ void main() {
1251
1288
  this.setState({ filterFBO, filterModel });
1252
1289
  }
1253
1290
  }
1254
- updateState({ props, oldProps }) {
1291
+ updateState({ props, oldProps, changeFlags }, extension) {
1292
+ const attributeManager = this.getAttributeManager();
1293
+ const { categorySize } = extension.opts;
1255
1294
  if (this.state.filterModel) {
1256
- const attributeManager = this.getAttributeManager();
1257
- const filterNeedsUpdate = attributeManager.attributes.filterValues.needsUpdate() || props.filterEnabled !== oldProps.filterEnabled || props.filterRange !== oldProps.filterRange || props.filterSoftRange !== oldProps.filterSoftRange;
1295
+ const filterNeedsUpdate = attributeManager.attributes.filterValues.needsUpdate() || attributeManager.attributes.filterCategoryValues?.needsUpdate() || props.filterEnabled !== oldProps.filterEnabled || props.filterRange !== oldProps.filterRange || props.filterSoftRange !== oldProps.filterSoftRange || props.filterCategories !== oldProps.filterCategories;
1258
1296
  if (filterNeedsUpdate) {
1259
1297
  this.setState({ filterNeedsUpdate });
1260
1298
  }
1261
1299
  }
1300
+ if (attributeManager?.attributes.filterCategoryValues) {
1301
+ const categoryBitMaskNeedsUpdate = attributeManager.attributes.filterCategoryValues.needsUpdate() || !(0, import_core4._deepEqual)(props.filterCategories, oldProps.filterCategories, 2);
1302
+ if (categoryBitMaskNeedsUpdate) {
1303
+ this.setState({ categoryBitMaskNeedsUpdate });
1304
+ }
1305
+ const resetCategories = changeFlags.dataChanged;
1306
+ if (resetCategories) {
1307
+ this.setState({
1308
+ categoryMap: Array(categorySize).fill(0).map(() => ({}))
1309
+ });
1310
+ attributeManager.attributes.filterCategoryValues.setNeedsUpdate("categoryMap");
1311
+ }
1312
+ }
1262
1313
  }
1263
1314
  draw(params, extension) {
1264
1315
  const filterFBO = this.state.filterFBO;
1265
1316
  const filterModel = this.state.filterModel;
1266
1317
  const filterNeedsUpdate = this.state.filterNeedsUpdate;
1318
+ const categoryBitMaskNeedsUpdate = this.state.categoryBitMaskNeedsUpdate;
1267
1319
  const { onFilteredItemsChange } = this.props;
1320
+ if (categoryBitMaskNeedsUpdate) {
1321
+ extension._updateCategoryBitMask.call(this, params, extension);
1322
+ }
1268
1323
  if (filterNeedsUpdate && onFilteredItemsChange && filterModel) {
1269
1324
  const {
1270
- attributes: { filterValues, filterIndices }
1325
+ attributes: { filterValues, filterCategoryValues, filterIndices }
1271
1326
  } = this.getAttributeManager();
1272
1327
  filterModel.setVertexCount(this.getNumInstances());
1273
1328
  this.context.device.clearWebGL({ framebuffer: filterFBO, color: [0, 0, 0, 0] });
1274
1329
  filterModel.updateModuleSettings(params.moduleParameters);
1275
1330
  filterModel.setAttributes({
1276
1331
  ...filterValues.getValue(),
1332
+ ...filterCategoryValues?.getValue(),
1277
1333
  ...filterIndices?.getValue()
1278
1334
  });
1335
+ filterModel.setUniforms(params.uniforms);
1279
1336
  filterModel.device.withParametersWebGL(
1280
1337
  {
1281
1338
  framebuffer: filterFBO,
@@ -1301,6 +1358,34 @@ void main() {
1301
1358
  filterFBO?.destroy();
1302
1359
  filterModel?.destroy();
1303
1360
  }
1361
+ _updateCategoryBitMask(params, extension) {
1362
+ const { categorySize } = extension.opts;
1363
+ const { filterCategories } = this.props;
1364
+ const categoryBitMask = new Uint32Array([0, 0, 0, 0]);
1365
+ const categoryFilters = categorySize === 1 ? [filterCategories] : filterCategories;
1366
+ const maxCategories = categorySize === 1 ? 128 : categorySize === 2 ? 64 : 32;
1367
+ for (let c = 0; c < categoryFilters.length; c++) {
1368
+ const categoryFilter = categoryFilters[c];
1369
+ for (const category of categoryFilter) {
1370
+ const key = extension._getCategoryKey.call(this, category, c);
1371
+ if (key < maxCategories) {
1372
+ const channel = c * (maxCategories / 32) + Math.floor(key / 32);
1373
+ categoryBitMask[channel] += Math.pow(2, key % 32);
1374
+ } else {
1375
+ import_core4.log.warn(`Exceeded maximum number of categories (${maxCategories})`)();
1376
+ }
1377
+ }
1378
+ }
1379
+ params.uniforms.filter_categoryBitMask = categoryBitMask;
1380
+ this.state.categoryBitMaskNeedsUpdate = false;
1381
+ }
1382
+ _getCategoryKey(category, channel) {
1383
+ const categoryMap = this.state.categoryMap[channel];
1384
+ if (!(category in categoryMap)) {
1385
+ categoryMap[category] = Object.keys(categoryMap).length;
1386
+ }
1387
+ return categoryMap[category];
1388
+ }
1304
1389
  };
1305
1390
  DataFilterExtension.defaultProps = defaultProps2;
1306
1391
  DataFilterExtension.extensionName = "DataFilterExtension";