@genome-spy/core 0.71.0 → 0.73.0
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/LICENSE +1 -1
- package/dist/bundle/index.es.js +6842 -5365
- package/dist/bundle/index.js +159 -140
- package/dist/bundle/parquetRead-BnAGCa4_.js +1663 -0
- package/dist/schema.json +281 -17
- package/dist/src/data/formats/bed.d.ts +8 -0
- package/dist/src/data/formats/bed.d.ts.map +1 -0
- package/dist/src/data/formats/bed.js +53 -0
- package/dist/src/data/formats/bedpe.d.ts +8 -0
- package/dist/src/data/formats/bedpe.d.ts.map +1 -0
- package/dist/src/data/formats/bedpe.js +160 -0
- package/dist/src/data/formats/parquet.d.ts +12 -0
- package/dist/src/data/formats/parquet.d.ts.map +1 -0
- package/dist/src/data/formats/parquet.js +29 -0
- package/dist/src/data/formats/parquetRead.d.ts +18 -0
- package/dist/src/data/formats/parquetRead.d.ts.map +1 -0
- package/dist/src/data/formats/parquetRead.js +326 -0
- package/dist/src/data/sources/dataUtils.d.ts +16 -0
- package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
- package/dist/src/data/sources/dataUtils.js +53 -3
- package/dist/src/data/sources/urlSource.d.ts +4 -0
- package/dist/src/data/sources/urlSource.d.ts.map +1 -1
- package/dist/src/data/sources/urlSource.js +141 -17
- package/dist/src/encoder/encoder.d.ts +2 -2
- package/dist/src/fonts/bmFontManager.d.ts +1 -1
- package/dist/src/genome/assemblyPreflight.d.ts +31 -0
- package/dist/src/genome/assemblyPreflight.d.ts.map +1 -0
- package/dist/src/genome/assemblyPreflight.js +99 -0
- package/dist/src/genome/genome.d.ts +2 -2
- package/dist/src/genome/genome.d.ts.map +1 -1
- package/dist/src/genome/genome.js +4 -0
- package/dist/src/genome/genomeStore.d.ts +34 -3
- package/dist/src/genome/genomeStore.d.ts.map +1 -1
- package/dist/src/genome/genomeStore.js +409 -18
- package/dist/src/genome/rootGenomeConfig.d.ts +26 -0
- package/dist/src/genome/rootGenomeConfig.d.ts.map +1 -0
- package/dist/src/genome/rootGenomeConfig.js +94 -0
- package/dist/src/genomeSpy/interactionController.d.ts +5 -1
- package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
- package/dist/src/genomeSpy/interactionController.js +244 -29
- package/dist/src/genomeSpy/renderCoordinator.js +1 -1
- package/dist/src/genomeSpy.d.ts +13 -3
- package/dist/src/genomeSpy.d.ts.map +1 -1
- package/dist/src/genomeSpy.js +83 -7
- package/dist/src/gl/canvasSizeHelper.d.ts +74 -0
- package/dist/src/gl/canvasSizeHelper.d.ts.map +1 -0
- package/dist/src/gl/canvasSizeHelper.js +203 -0
- package/dist/src/gl/hashTable.d.ts +78 -0
- package/dist/src/gl/hashTable.d.ts.map +1 -0
- package/dist/src/gl/hashTable.js +164 -0
- package/dist/src/gl/includes/common.glsl.js +1 -1
- package/dist/src/gl/webGLHelper.d.ts +25 -11
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +71 -39
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -2
- package/dist/src/marks/link.d.ts.map +1 -1
- package/dist/src/marks/link.js +5 -3
- package/dist/src/marks/mark.d.ts +1 -1
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +8 -4
- package/dist/src/scales/domainPlanner.d.ts +34 -3
- package/dist/src/scales/domainPlanner.d.ts.map +1 -1
- package/dist/src/scales/domainPlanner.js +247 -26
- package/dist/src/scales/scaleInstanceManager.d.ts +2 -1
- package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
- package/dist/src/scales/scaleInstanceManager.js +10 -11
- package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
- package/dist/src/scales/scaleInteractionController.js +16 -14
- package/dist/src/scales/scaleResolution.d.ts +16 -0
- package/dist/src/scales/scaleResolution.d.ts.map +1 -1
- package/dist/src/scales/scaleResolution.js +314 -54
- package/dist/src/scales/scaleResolutionTestUtils.d.ts +21 -0
- package/dist/src/scales/scaleResolutionTestUtils.d.ts.map +1 -0
- package/dist/src/scales/scaleResolutionTestUtils.js +33 -0
- package/dist/src/scales/selectionDomainUtils.d.ts +22 -0
- package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -0
- package/dist/src/scales/selectionDomainUtils.js +79 -0
- package/dist/src/scales/zoomDomainUtils.d.ts +18 -0
- package/dist/src/scales/zoomDomainUtils.d.ts.map +1 -0
- package/dist/src/scales/zoomDomainUtils.js +69 -0
- package/dist/src/screenshotHarness.d.ts +16 -0
- package/dist/src/screenshotHarness.d.ts.map +1 -0
- package/dist/src/screenshotHarness.js +242 -0
- package/dist/src/singlePageApp.js +1 -1
- package/dist/src/spec/data.d.ts +23 -3
- package/dist/src/spec/genome.d.ts +22 -2
- package/dist/src/spec/parameter.d.ts +39 -2
- package/dist/src/spec/root.d.ts +20 -1
- package/dist/src/spec/scale.d.ts +41 -5
- package/dist/src/styles/genome-spy.css +8 -0
- package/dist/src/styles/genome-spy.css.d.ts +1 -1
- package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
- package/dist/src/styles/genome-spy.css.js +8 -0
- package/dist/src/tooltip/dataTooltipHandler.js +59 -10
- package/dist/src/types/embedApi.d.ts +19 -0
- package/dist/src/utils/inferSpecBaseUrl.d.ts +14 -0
- package/dist/src/utils/inferSpecBaseUrl.d.ts.map +1 -0
- package/dist/src/utils/inferSpecBaseUrl.js +73 -0
- package/dist/src/utils/interactionEvent.d.ts +53 -3
- package/dist/src/utils/interactionEvent.d.ts.map +1 -1
- package/dist/src/utils/interactionEvent.js +62 -1
- package/dist/src/utils/radixSort.d.ts.map +1 -1
- package/dist/src/utils/radixSort.js +26 -1
- package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
- package/dist/src/view/containerMutationHelper.js +8 -0
- package/dist/src/view/dataReadiness.d.ts +2 -2
- package/dist/src/view/dataReadiness.d.ts.map +1 -1
- package/dist/src/view/dataReadiness.js +63 -58
- package/dist/src/view/facetView.d.ts +1 -1
- package/dist/src/view/facetView.js +1 -1
- package/dist/src/view/gridView/gridChild.d.ts +7 -0
- package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
- package/dist/src/view/gridView/gridChild.js +180 -11
- package/dist/src/view/gridView/gridView.d.ts.map +1 -1
- package/dist/src/view/gridView/gridView.js +60 -17
- package/dist/src/view/unitView.d.ts +1 -1
- package/dist/src/view/zoom.d.ts +14 -2
- package/dist/src/view/zoom.d.ts.map +1 -1
- package/dist/src/view/zoom.js +373 -76
- package/package.json +5 -2
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handles logical/physical canvas size calculations and optional
|
|
3
|
+
* device-pixel-content-box observation.
|
|
4
|
+
*/
|
|
5
|
+
export default class CanvasSizeHelper {
|
|
6
|
+
/**
|
|
7
|
+
* @param {HTMLElement} container
|
|
8
|
+
* @param {HTMLCanvasElement} canvas
|
|
9
|
+
* @param {() => {width: number, height: number}} sizeSource
|
|
10
|
+
* @param {() => void} [onPhysicalSizeChange]
|
|
11
|
+
*/
|
|
12
|
+
constructor(container, canvas, sizeSource, onPhysicalSizeChange) {
|
|
13
|
+
this._container = container;
|
|
14
|
+
this._canvas = canvas;
|
|
15
|
+
this._sizeSource = sizeSource;
|
|
16
|
+
this._onPhysicalSizeChange = onPhysicalSizeChange ?? (() => {});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @type {{ width: number, height: number } | undefined}
|
|
20
|
+
*/
|
|
21
|
+
this._logicalCanvasSize = undefined;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @type {{ width: number, height: number } | undefined}
|
|
25
|
+
*/
|
|
26
|
+
this._devicePixelContentBoxSize = undefined;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @type {ResizeObserver | undefined}
|
|
30
|
+
*/
|
|
31
|
+
this._devicePixelContentBoxObserver = undefined;
|
|
32
|
+
|
|
33
|
+
this._observeDevicePixelContentBox();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
invalidate() {
|
|
37
|
+
this._logicalCanvasSize = undefined;
|
|
38
|
+
this._devicePixelContentBoxSize = undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
finalize() {
|
|
42
|
+
if (this._devicePixelContentBoxObserver) {
|
|
43
|
+
this._devicePixelContentBoxObserver.disconnect();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Returns the canvas size in true display pixels
|
|
49
|
+
*
|
|
50
|
+
* @param {{ width: number, height: number }} [logicalSize]
|
|
51
|
+
*/
|
|
52
|
+
getPhysicalCanvasSize(logicalSize) {
|
|
53
|
+
// devicePixelContentBox gives the actual backing-store pixel size.
|
|
54
|
+
// Prefer it whenever available to avoid fractional DPR drift.
|
|
55
|
+
// https://web.dev/articles/device-pixel-content-box
|
|
56
|
+
if (this._devicePixelContentBoxSize) {
|
|
57
|
+
return this._devicePixelContentBoxSize;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const dpr = window.devicePixelRatio ?? 1;
|
|
61
|
+
logicalSize = logicalSize || this.getLogicalCanvasSize();
|
|
62
|
+
return {
|
|
63
|
+
width: Math.round(logicalSize.width * dpr),
|
|
64
|
+
height: Math.round(logicalSize.height * dpr),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Returns the ratio between true display pixels and logical pixels.
|
|
70
|
+
*
|
|
71
|
+
* @param {{ width: number, height: number }} [logicalSize]
|
|
72
|
+
*/
|
|
73
|
+
getDevicePixelRatio(logicalSize) {
|
|
74
|
+
logicalSize = logicalSize || this.getLogicalCanvasSize();
|
|
75
|
+
const physicalSize = this.getPhysicalCanvasSize(logicalSize);
|
|
76
|
+
const widthRatio =
|
|
77
|
+
logicalSize.width > 0
|
|
78
|
+
? physicalSize.width / logicalSize.width
|
|
79
|
+
: undefined;
|
|
80
|
+
const heightRatio =
|
|
81
|
+
logicalSize.height > 0
|
|
82
|
+
? physicalSize.height / logicalSize.height
|
|
83
|
+
: undefined;
|
|
84
|
+
|
|
85
|
+
if (widthRatio !== undefined && heightRatio !== undefined) {
|
|
86
|
+
// Width and height can differ slightly because backing-store dimensions
|
|
87
|
+
// are integers. Averaging keeps snapping stable in both directions.
|
|
88
|
+
return (widthRatio + heightRatio) / 2;
|
|
89
|
+
} else if (widthRatio !== undefined) {
|
|
90
|
+
// During transient layout states one logical dimension may be zero.
|
|
91
|
+
// Use the non-zero dimension instead of falling back to window DPR.
|
|
92
|
+
return widthRatio;
|
|
93
|
+
} else if (heightRatio !== undefined) {
|
|
94
|
+
return heightRatio;
|
|
95
|
+
} else {
|
|
96
|
+
return window.devicePixelRatio ?? 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns the size of the canvas canvas container size in logical pixels,
|
|
102
|
+
* without devicePixelRatio correction.
|
|
103
|
+
*/
|
|
104
|
+
getLogicalCanvasSize() {
|
|
105
|
+
if (this._logicalCanvasSize) {
|
|
106
|
+
return this._logicalCanvasSize;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// TODO: The size should never be smaller than the minimum content size!
|
|
110
|
+
const contentSize = this._sizeSource();
|
|
111
|
+
|
|
112
|
+
const cs = window.getComputedStyle(this._container, null);
|
|
113
|
+
// clientWidth/clientHeight are integer CSS pixels, which causes subtle
|
|
114
|
+
// blur at fractional DPR. getBoundingClientRect preserves fractions.
|
|
115
|
+
const containerRect = this._container.getBoundingClientRect();
|
|
116
|
+
|
|
117
|
+
const paddingLeft = parseFloat(cs.paddingLeft);
|
|
118
|
+
const paddingRight = parseFloat(cs.paddingRight);
|
|
119
|
+
const paddingTop = parseFloat(cs.paddingTop);
|
|
120
|
+
const paddingBottom = parseFloat(cs.paddingBottom);
|
|
121
|
+
|
|
122
|
+
const borderLeft = parseFloat(cs.borderLeftWidth);
|
|
123
|
+
const borderRight = parseFloat(cs.borderRightWidth);
|
|
124
|
+
const borderTop = parseFloat(cs.borderTopWidth);
|
|
125
|
+
const borderBottom = parseFloat(cs.borderBottomWidth);
|
|
126
|
+
|
|
127
|
+
const width =
|
|
128
|
+
contentSize.width ??
|
|
129
|
+
containerRect.width -
|
|
130
|
+
paddingLeft -
|
|
131
|
+
paddingRight -
|
|
132
|
+
borderLeft -
|
|
133
|
+
borderRight;
|
|
134
|
+
|
|
135
|
+
const height =
|
|
136
|
+
contentSize.height ??
|
|
137
|
+
containerRect.height -
|
|
138
|
+
paddingTop -
|
|
139
|
+
paddingBottom -
|
|
140
|
+
borderTop -
|
|
141
|
+
borderBottom;
|
|
142
|
+
|
|
143
|
+
this._logicalCanvasSize = { width, height };
|
|
144
|
+
return this._logicalCanvasSize;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
_observeDevicePixelContentBox() {
|
|
148
|
+
if (typeof ResizeObserver != "function") {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const observer = new ResizeObserver((entries) => {
|
|
153
|
+
const entry = entries.find(
|
|
154
|
+
(candidate) => candidate.target == this._canvas
|
|
155
|
+
);
|
|
156
|
+
if (!entry) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const boxSize = entry.devicePixelContentBoxSize;
|
|
161
|
+
if (!boxSize) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const contentBoxSize = Array.isArray(boxSize)
|
|
166
|
+
? boxSize[0]
|
|
167
|
+
: boxSize;
|
|
168
|
+
if (!contentBoxSize) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ResizeObserver reports device pixels directly, which is exactly what
|
|
173
|
+
// canvas width/height expect.
|
|
174
|
+
const nextPhysicalSize = {
|
|
175
|
+
width: contentBoxSize.inlineSize,
|
|
176
|
+
height: contentBoxSize.blockSize,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
if (
|
|
180
|
+
this._devicePixelContentBoxSize &&
|
|
181
|
+
this._devicePixelContentBoxSize.width ==
|
|
182
|
+
nextPhysicalSize.width &&
|
|
183
|
+
this._devicePixelContentBoxSize.height ==
|
|
184
|
+
nextPhysicalSize.height
|
|
185
|
+
) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
this._devicePixelContentBoxSize = nextPhysicalSize;
|
|
190
|
+
this._onPhysicalSizeChange();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
// Fails in browsers that do not support device-pixel-content-box.
|
|
195
|
+
observer.observe(this._canvas, {
|
|
196
|
+
box: "device-pixel-content-box",
|
|
197
|
+
});
|
|
198
|
+
this._devicePixelContentBoxObserver = observer;
|
|
199
|
+
} catch {
|
|
200
|
+
observer.disconnect();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time options for sizing the hash table.
|
|
3
|
+
*
|
|
4
|
+
* @typedef {object} HashTableBuildOptions
|
|
5
|
+
* @property {number} [capacity] Power-of-two table size override.
|
|
6
|
+
* @property {number} [maxLoadFactor] Maximum load factor before resizing.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Packed table payload ready for WebGL texture upload.
|
|
10
|
+
*
|
|
11
|
+
* @typedef {object} HashTableBuildResult
|
|
12
|
+
* @property {Uint32Array} table Hash table slots that contain keys.
|
|
13
|
+
* @property {number} capacity Table capacity (number of slots).
|
|
14
|
+
* @property {number} size Number of stored keys.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* 32-bit integer hash for u32 keys. Keep in sync with GLSL hash32.
|
|
18
|
+
*
|
|
19
|
+
* @param {number} value
|
|
20
|
+
* @returns {number}
|
|
21
|
+
*/
|
|
22
|
+
export function hash32(value: number): number;
|
|
23
|
+
/**
|
|
24
|
+
* Build a hash table for set membership checks.
|
|
25
|
+
*
|
|
26
|
+
* @param {Iterable<number>} keys
|
|
27
|
+
* @param {HashTableBuildOptions} [options]
|
|
28
|
+
* @returns {HashTableBuildResult}
|
|
29
|
+
*/
|
|
30
|
+
export function buildHashTableSet(keys: Iterable<number>, options?: HashTableBuildOptions): HashTableBuildResult;
|
|
31
|
+
/**
|
|
32
|
+
* Computes a 2D texture layout for a power-of-two hash table capacity.
|
|
33
|
+
*
|
|
34
|
+
* The returned dimensions satisfy `width * height === capacity`.
|
|
35
|
+
*
|
|
36
|
+
* @param {number} capacity
|
|
37
|
+
* @param {number} maxTextureSize
|
|
38
|
+
* @returns {{ width: number, height: number }}
|
|
39
|
+
*/
|
|
40
|
+
export function computeHashTextureDimensions(capacity: number, maxTextureSize: number): {
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
};
|
|
44
|
+
/** Sentinel key that marks an empty hash slot. */
|
|
45
|
+
export const HASH_EMPTY_KEY: 4294967295;
|
|
46
|
+
/** Default maximum load factor for table sizing. */
|
|
47
|
+
export const DEFAULT_MAX_LOAD_FACTOR: 0.6;
|
|
48
|
+
/**
|
|
49
|
+
* Build-time options for sizing the hash table.
|
|
50
|
+
*/
|
|
51
|
+
export type HashTableBuildOptions = {
|
|
52
|
+
/**
|
|
53
|
+
* Power-of-two table size override.
|
|
54
|
+
*/
|
|
55
|
+
capacity?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Maximum load factor before resizing.
|
|
58
|
+
*/
|
|
59
|
+
maxLoadFactor?: number;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Packed table payload ready for WebGL texture upload.
|
|
63
|
+
*/
|
|
64
|
+
export type HashTableBuildResult = {
|
|
65
|
+
/**
|
|
66
|
+
* Hash table slots that contain keys.
|
|
67
|
+
*/
|
|
68
|
+
table: Uint32Array;
|
|
69
|
+
/**
|
|
70
|
+
* Table capacity (number of slots).
|
|
71
|
+
*/
|
|
72
|
+
capacity: number;
|
|
73
|
+
/**
|
|
74
|
+
* Number of stored keys.
|
|
75
|
+
*/
|
|
76
|
+
size: number;
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=hashTable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hashTable.d.ts","sourceRoot":"","sources":["../../../src/gl/hashTable.js"],"names":[],"mappings":"AAQA;;;;;;GAMG;AAEH;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,8BAHW,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;GAMG;AACH,wCAJW,QAAQ,CAAC,MAAM,CAAC,YAChB,qBAAqB,GACnB,oBAAoB,CAkDhC;AAED;;;;;;;;GAQG;AACH,uDAJW,MAAM,kBACN,MAAM,GACJ;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA+B7C;AAxID,kDAAkD;AAClD,6BAA8B,UAAW,CAAC;AAE1C,oDAAoD;AACpD,sCAAuC,GAAG,CAAC;;;;;;;;eAQ7B,MAAM;;;;oBACN,MAAM;;;;;;;;;WAON,WAAW;;;;cACX,MAAM;;;;UACN,MAAM"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/** Sentinel key that marks an empty hash slot. */
|
|
2
|
+
export const HASH_EMPTY_KEY = 0xffff_ffff;
|
|
3
|
+
|
|
4
|
+
/** Default maximum load factor for table sizing. */
|
|
5
|
+
export const DEFAULT_MAX_LOAD_FACTOR = 0.6;
|
|
6
|
+
|
|
7
|
+
const MAX_U32 = 0xffff_ffff;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Build-time options for sizing the hash table.
|
|
11
|
+
*
|
|
12
|
+
* @typedef {object} HashTableBuildOptions
|
|
13
|
+
* @property {number} [capacity] Power-of-two table size override.
|
|
14
|
+
* @property {number} [maxLoadFactor] Maximum load factor before resizing.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Packed table payload ready for WebGL texture upload.
|
|
19
|
+
*
|
|
20
|
+
* @typedef {object} HashTableBuildResult
|
|
21
|
+
* @property {Uint32Array} table Hash table slots that contain keys.
|
|
22
|
+
* @property {number} capacity Table capacity (number of slots).
|
|
23
|
+
* @property {number} size Number of stored keys.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 32-bit integer hash for u32 keys. Keep in sync with GLSL hash32.
|
|
28
|
+
*
|
|
29
|
+
* @param {number} value
|
|
30
|
+
* @returns {number}
|
|
31
|
+
*/
|
|
32
|
+
export function hash32(value) {
|
|
33
|
+
let v = value >>> 0;
|
|
34
|
+
v ^= v >>> 16;
|
|
35
|
+
v = Math.imul(v, 0x7feb352d);
|
|
36
|
+
v ^= v >>> 15;
|
|
37
|
+
v = Math.imul(v, 0x846ca68b);
|
|
38
|
+
v ^= v >>> 16;
|
|
39
|
+
return v >>> 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Build a hash table for set membership checks.
|
|
44
|
+
*
|
|
45
|
+
* @param {Iterable<number>} keys
|
|
46
|
+
* @param {HashTableBuildOptions} [options]
|
|
47
|
+
* @returns {HashTableBuildResult}
|
|
48
|
+
*/
|
|
49
|
+
export function buildHashTableSet(keys, options = {}) {
|
|
50
|
+
const normalized = Array.from(keys, (key) => normalizeU32(key, "key"));
|
|
51
|
+
const size = normalized.length;
|
|
52
|
+
const maxLoadFactor = options.maxLoadFactor ?? DEFAULT_MAX_LOAD_FACTOR;
|
|
53
|
+
if (!(maxLoadFactor > 0 && maxLoadFactor < 1)) {
|
|
54
|
+
throw new Error("maxLoadFactor must be between 0 and 1.");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const capacity =
|
|
58
|
+
options.capacity ?? nextPow2(Math.ceil(size / maxLoadFactor));
|
|
59
|
+
if (!Number.isSafeInteger(capacity) || capacity < 1) {
|
|
60
|
+
throw new Error("capacity must be a positive power of two.");
|
|
61
|
+
}
|
|
62
|
+
if ((capacity & (capacity - 1)) !== 0) {
|
|
63
|
+
throw new Error("capacity must be a power of two.");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const table = new Uint32Array(capacity);
|
|
67
|
+
table.fill(HASH_EMPTY_KEY);
|
|
68
|
+
|
|
69
|
+
const mask = capacity - 1;
|
|
70
|
+
for (const key of normalized) {
|
|
71
|
+
if (key === HASH_EMPTY_KEY) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
"Hash table keys must not equal the empty sentinel (0xffffffff)."
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
let index = hash32(key) & mask;
|
|
77
|
+
let inserted = false;
|
|
78
|
+
|
|
79
|
+
for (let probe = 0; probe < capacity; probe += 1) {
|
|
80
|
+
const existingKey = table[index];
|
|
81
|
+
if (existingKey === HASH_EMPTY_KEY || existingKey === key) {
|
|
82
|
+
table[index] = key;
|
|
83
|
+
inserted = true;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
index = (index + 1) & mask;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!inserted) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
"Hash table insertion failed. Increase capacity or lower load factor."
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { table, capacity, size };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Computes a 2D texture layout for a power-of-two hash table capacity.
|
|
101
|
+
*
|
|
102
|
+
* The returned dimensions satisfy `width * height === capacity`.
|
|
103
|
+
*
|
|
104
|
+
* @param {number} capacity
|
|
105
|
+
* @param {number} maxTextureSize
|
|
106
|
+
* @returns {{ width: number, height: number }}
|
|
107
|
+
*/
|
|
108
|
+
export function computeHashTextureDimensions(capacity, maxTextureSize) {
|
|
109
|
+
if (!Number.isSafeInteger(capacity) || capacity < 1) {
|
|
110
|
+
throw new Error("capacity must be a positive integer.");
|
|
111
|
+
}
|
|
112
|
+
if ((capacity & (capacity - 1)) !== 0) {
|
|
113
|
+
throw new Error("capacity must be a power of two.");
|
|
114
|
+
}
|
|
115
|
+
if (!Number.isSafeInteger(maxTextureSize) || maxTextureSize < 1) {
|
|
116
|
+
throw new Error("maxTextureSize must be a positive integer.");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const maxSlots = maxTextureSize * maxTextureSize;
|
|
120
|
+
if (capacity > maxSlots) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
"Selection hash table exceeds maximum texture capacity."
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const maxPow2Width = 1 << Math.floor(Math.log2(maxTextureSize));
|
|
127
|
+
const width = Math.min(capacity, maxPow2Width);
|
|
128
|
+
const height = capacity / width;
|
|
129
|
+
|
|
130
|
+
if (height > maxTextureSize) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
"Selection hash table dimensions exceed maximum texture size."
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { width, height };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param {number} value
|
|
141
|
+
* @param {string} label
|
|
142
|
+
* @returns {number}
|
|
143
|
+
*/
|
|
144
|
+
function normalizeU32(value, label) {
|
|
145
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > MAX_U32) {
|
|
146
|
+
throw new Error(label + " must be a non-negative u32.");
|
|
147
|
+
}
|
|
148
|
+
return value >>> 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param {number} value
|
|
153
|
+
* @returns {number}
|
|
154
|
+
*/
|
|
155
|
+
function nextPow2(value) {
|
|
156
|
+
let v = Math.max(1, value);
|
|
157
|
+
v -= 1;
|
|
158
|
+
v |= v >>> 1;
|
|
159
|
+
v |= v >>> 2;
|
|
160
|
+
v |= v >>> 4;
|
|
161
|
+
v |= v >>> 8;
|
|
162
|
+
v |= v >>> 16;
|
|
163
|
+
return v + 1;
|
|
164
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const shader = "#define PI 3.141593\nuniform View{mediump vec2 uViewOffset;mediump vec2 uViewScale;mediump vec2 uViewportSize;lowp float uDevicePixelRatio;lowp float uViewOpacity;bool uPickingEnabled;};/***Maps a coordinate on the unit scale to a normalized device coordinate.*(0,0)is at the bottom left corner.*/vec4 unitToNdc(vec2 coord){return vec4((coord*uViewScale+uViewOffset)*2.0-1.0,0.0,1.0);}vec4 unitToNdc(float x,float y){return unitToNdc(vec2(x,y));}vec4 pixelsToNdc(vec2 coord){return unitToNdc(coord/uViewportSize);}vec4 pixelsToNdc(float x,float y){return pixelsToNdc(vec2(x,y));}float linearstep(float edge0,float edge1,float x){return clamp((x-edge0)/(edge1-edge0),0.0,1.0);}bool
|
|
1
|
+
const shader = "#define PI 3.141593\nuniform View{mediump vec2 uViewOffset;mediump vec2 uViewScale;mediump vec2 uViewportSize;lowp float uDevicePixelRatio;lowp float uViewOpacity;bool uPickingEnabled;};/***Maps a coordinate on the unit scale to a normalized device coordinate.*(0,0)is at the bottom left corner.*/vec4 unitToNdc(vec2 coord){return vec4((coord*uViewScale+uViewOffset)*2.0-1.0,0.0,1.0);}vec4 unitToNdc(float x,float y){return unitToNdc(vec2(x,y));}vec4 pixelsToNdc(vec2 coord){return unitToNdc(coord/uViewportSize);}vec4 pixelsToNdc(float x,float y){return pixelsToNdc(vec2(x,y));}float linearstep(float edge0,float edge1,float x){return clamp((x-edge0)/(edge1-edge0),0.0,1.0);}const highp uint HASH_EMPTY_KEY=0xffffffffu;highp uint hash32(highp uint key){highp uint v=key;v ^=v>>16u;v*=0x7feb352du;v ^=v>>15u;v*=0x846ca68bu;v ^=v>>16u;return v;}bool isEmptyHashTexture(highp usampler2D s){ivec2 texSize=textureSize(s,0);return texSize.x==1&&texSize.y==1&&texelFetch(s,ivec2(0,0),0).r==HASH_EMPTY_KEY;}bool hashContainsTexture(highp usampler2D s,highp uint value){ivec2 texSize=textureSize(s,0);highp uint width=uint(texSize.x);highp uint size=width*uint(texSize.y);highp uint mask=size-1u;highp uint index=hash32(value)&mask;for(highp uint probe=0u;probe<size;probe+=1u){ivec2 coord=ivec2(int(index % width),int(index/width));highp uint entry=texelFetch(s,coord,0).r;if(entry==value){return true;}if(entry==HASH_EMPTY_KEY){return false;}index=(index+1u)&mask;}return false;}/***Calculates a gamma for antialiasing opacity based on the color.*/float getGammaForColor(vec3 rgb){return mix(1.25,0.75,smoothstep(0.0,1.0,dot(rgb,vec3(0.299,0.587,0.114))));}/***Specialized linearstep for doing antialiasing*/float distanceToRatio(float d){return clamp(d*uDevicePixelRatio+0.5,0.0,1.0);}vec4 distanceToColor(float d,vec4 fill,vec4 stroke,vec4 background,float halfStrokeWidth){if(halfStrokeWidth>0.0){float sd=abs(d)-halfStrokeWidth;return mix(stroke,d<=0.0 ? fill : background,distanceToRatio(sd));}else{return mix(background,fill,distanceToRatio(-d));}}";
|
|
2
2
|
export default shader;
|
|
@@ -45,11 +45,19 @@ export default class WebGLHelper {
|
|
|
45
45
|
width: number;
|
|
46
46
|
height: number;
|
|
47
47
|
}, webglContextAttributes?: WebGLContextAttributes);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
/**
|
|
49
|
+
* @type {CanvasSizeHelper}
|
|
50
|
+
*/
|
|
51
|
+
_canvasSizeHelper: CanvasSizeHelper;
|
|
52
|
+
/**
|
|
53
|
+
* @type {{ logicalWidth: number, logicalHeight: number, physicalWidth: number, physicalHeight: number } | undefined}
|
|
54
|
+
*/
|
|
55
|
+
_appliedCanvasSize: {
|
|
56
|
+
logicalWidth: number;
|
|
57
|
+
logicalHeight: number;
|
|
58
|
+
physicalWidth: number;
|
|
59
|
+
physicalHeight: number;
|
|
60
|
+
} | undefined;
|
|
53
61
|
/** @type {Map<string, WebGLShader>} */
|
|
54
62
|
_shaderCache: Map<string, WebGLShader>;
|
|
55
63
|
/** @type {WeakMap<import("../types/encoder.js").VegaScale, WebGLTexture>} */
|
|
@@ -64,10 +72,6 @@ export default class WebGLHelper {
|
|
|
64
72
|
_pickingAttachmentOptions: import("twgl.js").AttachmentOptions[];
|
|
65
73
|
_pickingBufferInfo: import("twgl.js").FramebufferInfo;
|
|
66
74
|
invalidateSize(): void;
|
|
67
|
-
_logicalCanvasSize: {
|
|
68
|
-
width: any;
|
|
69
|
-
height: any;
|
|
70
|
-
};
|
|
71
75
|
/**
|
|
72
76
|
* Compiles and caches a shader. The shader source is used as a cache key.
|
|
73
77
|
*
|
|
@@ -89,13 +93,22 @@ export default class WebGLHelper {
|
|
|
89
93
|
width: number;
|
|
90
94
|
height: number;
|
|
91
95
|
};
|
|
96
|
+
/**
|
|
97
|
+
* Returns the ratio between true display pixels and logical pixels.
|
|
98
|
+
*
|
|
99
|
+
* @param {{ width: number, height: number }} [logicalSize]
|
|
100
|
+
*/
|
|
101
|
+
getDevicePixelRatio(logicalSize?: {
|
|
102
|
+
width: number;
|
|
103
|
+
height: number;
|
|
104
|
+
}): number;
|
|
92
105
|
/**
|
|
93
106
|
* Returns the size of the canvas canvas container size in logical pixels,
|
|
94
107
|
* without devicePixelRatio correction.
|
|
95
108
|
*/
|
|
96
109
|
getLogicalCanvasSize(): {
|
|
97
|
-
width:
|
|
98
|
-
height:
|
|
110
|
+
width: number;
|
|
111
|
+
height: number;
|
|
99
112
|
};
|
|
100
113
|
/**
|
|
101
114
|
* Creates textures for color schemes and discrete/discretizing ranges.
|
|
@@ -113,4 +126,5 @@ export default class WebGLHelper {
|
|
|
113
126
|
*/
|
|
114
127
|
createSelectionTexture(selection: import("../types/selectionTypes.js").MultiPointSelection, update?: boolean): void;
|
|
115
128
|
}
|
|
129
|
+
import CanvasSizeHelper from "./canvasSizeHelper.js";
|
|
116
130
|
//# sourceMappingURL=webGLHelper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webGLHelper.d.ts","sourceRoot":"","sources":["../../../src/gl/webGLHelper.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"webGLHelper.d.ts","sourceRoot":"","sources":["../../../src/gl/webGLHelper.js"],"names":[],"mappings":"AAybA;;;;GAIG;AACH,kCAJW,sBAAsB,gBACtB,WAAW,kBACX,WAAW;;;;;;EA8CrB;AAED;;;;;GAKG;AACH,0CALW,qBAAqB,WACrB,IAAI,CAAC,OAAO,SAAS,EAAE,cAAc,EAAE,KAAK,CAAC,OAC7C,MAAM,EAAE,GAAG,eAAe,YAC1B,YAAY,gBAYtB;AAED;;;;;;GAMG;AACH,qCALW,sBAAsB,mBACtB,OAAO,SAAS,EAAE,eAAe,KACjC,MAAM,KACN,MAAM,2BAWhB;AAED;;;;;GAKG;AACH,yCAJW,sBAAsB,mBACtB,OAAO,SAAS,EAAE,eAAe,SACjC,MAAM,UA4BhB;AA7gBD;IACI;;;;;;;OAOG;IACH,uBANW,WAAW,eACX,MAAM;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,2BAGrC,sBAAsB,EAsGhC;IA5FG;;OAEG;IACH,mBAFU,gBAAgB,CAEQ;IAElC;;OAEG;IACH,oBAFU;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAEjF;IAEnC,uCAAuC;IACvC,cADW,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CACN;IAE7B,6EAA6E;IAC7E,eADW,OAAO,CAAC,OAAO,qBAAqB,EAAE,SAAS,EAAE,YAAY,CAAC,CACvC;IAElC;;OAEG;IACH,mBAFU,OAAO,CAAC,OAAO,4BAA4B,EAAE,mBAAmB,EAAE,YAAY,CAAC,CAEnD;IA8CtC,0BAAoB;IACpB,2BAAY;IAGZ,oDAAoD;IACpD,2BADW,OAAO,SAAS,EAAE,iBAAiB,EAAE,CAQ/C;IACD,sDAGC;IAaL,uBAGC;IAED;;;;;OAKG;IACH,oBAHW,MAAM,QACN,MAAM,GAAG,MAAM,EAAE,eA2B3B;IAED,iBAgCC;IAED,iBAGC;IAED;;;;OAIG;IACH,oCAFW;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;;;MAI3C;IAED;;;;OAIG;IACH,kCAFW;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,UAI3C;IAED;;;OAGG;IACH;;;MAEC;IAED;;;;;;;;;OASG;IACH,+BAHW,OAAO,8BAA8B,EAAE,OAAO,WAC9C,OAAO,QA2GjB;IAED;;OAEG;IACH,kCAFW,OAAO,4BAA4B,EAAE,mBAAmB,0BAwClE;CACJ;6BAvX4B,uBAAuB"}
|