@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.
- package/dist/brushing/shader-module.d.ts +1 -1
- package/dist/brushing/shader-module.d.ts.map +1 -1
- package/dist/data-filter/data-filter-extension.d.ts +36 -5
- package/dist/data-filter/data-filter-extension.d.ts.map +1 -1
- package/dist/data-filter/data-filter-extension.js +96 -15
- package/dist/data-filter/shader-module.d.ts +1 -1
- package/dist/data-filter/shader-module.d.ts.map +1 -1
- package/dist/data-filter/shader-module.js +31 -3
- package/dist/dist.dev.js +106 -21
- package/dist/index.cjs +107 -17
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +18 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/mask/mask-effect.d.ts +1 -1
- package/dist/mask/mask-effect.d.ts.map +1 -1
- package/dist/terrain/height-map-builder.d.ts +1 -1
- package/dist/terrain/height-map-builder.d.ts.map +1 -1
- package/dist/terrain/shader-module.d.ts +2 -2
- package/dist/terrain/shader-module.d.ts.map +1 -1
- package/dist/terrain/terrain-cover.d.ts +1 -1
- package/dist/terrain/terrain-cover.d.ts.map +1 -1
- package/dist/terrain/terrain-effect.d.ts +1 -1
- package/dist/terrain/terrain-effect.d.ts.map +1 -1
- package/dist/terrain/terrain-pass.d.ts +2 -2
- package/dist/terrain/terrain-pass.d.ts.map +1 -1
- package/dist/terrain/terrain-picking-pass.d.ts +1 -1
- package/dist/terrain/terrain-picking-pass.d.ts.map +1 -1
- package/dist.min.js +47 -19
- package/package.json +2 -2
- package/src/data-filter/data-filter-extension.ts +133 -24
- package/src/data-filter/shader-module.ts +31 -3
|
@@ -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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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:
|
|
65
|
-
type:
|
|
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
|
|
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,
|
|
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
|
-
|
|
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:
|
|
1209
|
-
type:
|
|
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
|
|
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";
|