@genome-spy/core 0.48.0 → 0.48.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle/index.es.js +3613 -3504
- package/dist/bundle/index.js +85 -85
- package/dist/schema.json +9 -2
- package/dist/src/data/collector.d.ts +10 -8
- package/dist/src/data/collector.d.ts.map +1 -1
- package/dist/src/data/collector.js +131 -33
- package/dist/src/data/collector.test.js +55 -1
- package/dist/src/data/transforms/identifier.d.ts +1 -1
- package/dist/src/data/transforms/identifier.d.ts.map +1 -1
- package/dist/src/data/transforms/identifier.js +2 -2
- package/dist/src/data/transforms/identifier.test.js +23 -14
- package/dist/src/genomeSpy.d.ts.map +1 -1
- package/dist/src/genomeSpy.js +14 -15
- package/dist/src/gl/dataToVertices.d.ts +1 -1
- package/dist/src/gl/dataToVertices.d.ts.map +1 -1
- package/dist/src/gl/dataToVertices.js +8 -5
- package/dist/src/gl/glslScaleGenerator.js +1 -1
- package/dist/src/gl/includes/picking.vertex.glsl.js +1 -1
- package/dist/src/marks/link.common.glsl.js +1 -1
- package/dist/src/marks/link.d.ts.map +1 -1
- package/dist/src/marks/link.js +14 -22
- package/dist/src/marks/link.vertex.glsl.js +1 -1
- package/dist/src/marks/mark.d.ts +9 -0
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +19 -0
- package/dist/src/marks/rect.vertex.glsl.js +1 -1
- package/dist/src/spec/mark.d.ts +1 -1
- package/dist/src/utils/animator.d.ts +5 -4
- package/dist/src/utils/animator.d.ts.map +1 -1
- package/dist/src/utils/animator.js +34 -10
- package/dist/src/utils/inertia.d.ts +3 -1
- package/dist/src/utils/inertia.d.ts.map +1 -1
- package/dist/src/utils/inertia.js +10 -5
- package/dist/src/utils/iterateNestedMaps.d.ts +4 -3
- package/dist/src/utils/iterateNestedMaps.d.ts.map +1 -1
- package/dist/src/utils/iterateNestedMaps.js +3 -2
- package/dist/src/utils/radixSort.d.ts +9 -0
- package/dist/src/utils/radixSort.d.ts.map +1 -0
- package/dist/src/utils/radixSort.js +130 -0
- package/dist/src/utils/radixSort.test.js +51 -0
- package/dist/src/view/gridView.d.ts +3 -1
- package/dist/src/view/gridView.d.ts.map +1 -1
- package/dist/src/view/gridView.js +7 -6
- package/dist/src/view/zoom.d.ts.map +1 -1
- package/dist/src/view/zoom.js +23 -15
- package/package.json +2 -2
package/dist/schema.json
CHANGED
|
@@ -3471,8 +3471,15 @@
|
|
|
3471
3471
|
"description": "The vertical padding, in pixels, when the `y2` channel is used for ranged text.\n\n**Default value:** `0`"
|
|
3472
3472
|
},
|
|
3473
3473
|
"segments": {
|
|
3474
|
-
"
|
|
3475
|
-
|
|
3474
|
+
"anyOf": [
|
|
3475
|
+
{
|
|
3476
|
+
"type": "number"
|
|
3477
|
+
},
|
|
3478
|
+
{
|
|
3479
|
+
"$ref": "#/definitions/ExprRef"
|
|
3480
|
+
}
|
|
3481
|
+
],
|
|
3482
|
+
"description": "The number of segments in the bézier curve. Affects the rendering quality and performance. Use a higher value for a smoother curve.\n\n**Default value:* `101`"
|
|
3476
3483
|
},
|
|
3477
3484
|
"semanticZoomFraction": {
|
|
3478
3485
|
"anyOf": [
|
|
@@ -12,25 +12,27 @@ export default class Collector extends FlowNode {
|
|
|
12
12
|
params: import("../spec/transform.js").CollectParams;
|
|
13
13
|
/** @type {(function(Collector):void)[]} */
|
|
14
14
|
observers: ((arg0: Collector) => void)[];
|
|
15
|
-
/** @type {Map<
|
|
16
|
-
facetBatches: Map<
|
|
17
|
-
_init(): void;
|
|
18
|
-
/** @type {import("./flowNode.js").Data} */
|
|
19
|
-
_data: import("./flowNode.js").Data;
|
|
15
|
+
/** @type {Map<import("../spec/channel.js").Scalar[], Data>} TODO: proper type for key */
|
|
16
|
+
facetBatches: Map<import("../spec/channel.js").Scalar[], import("./flowNode.js").Data>;
|
|
20
17
|
/**
|
|
21
|
-
* @returns {Iterable<
|
|
18
|
+
* @returns {Iterable<Datum>}
|
|
22
19
|
*/
|
|
23
20
|
getData(): Iterable<import("./flowNode.js").Datum>;
|
|
24
21
|
/**
|
|
25
22
|
*
|
|
26
|
-
* @param {(datum:
|
|
23
|
+
* @param {(datum: Datum) => void} visitor
|
|
27
24
|
*/
|
|
28
25
|
visitData(visitor: (datum: import("./flowNode.js").Datum) => void): void;
|
|
29
26
|
/**
|
|
30
27
|
* Returns the total number of data items collected.
|
|
31
28
|
*/
|
|
32
29
|
getItemCount(): number;
|
|
33
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Use an index to find a datum by its unique id.
|
|
32
|
+
*
|
|
33
|
+
* @param {number} uniqueId
|
|
34
|
+
*/
|
|
35
|
+
findDatumByUniqueId(uniqueId: number): import("./flowNode.js").Datum;
|
|
34
36
|
#private;
|
|
35
37
|
}
|
|
36
38
|
import FlowNode from "./flowNode.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../../src/data/collector.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../../src/data/collector.js"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH;IA8BI;;OAEG;IACH,qBAFW,OAAO,sBAAsB,EAAE,aAAa,EAetD;IAVG,qDAA2C;IAE3C,2CAA2C;IAC3C,WADW,QAAU,SAAS,KAAE,IAAI,CAAC,EAAE,CACpB;IAGnB,yFAAyF;IACzF,cADW,IAAI,OAAO,oBAAoB,EAAE,MAAM,EAAE,+BAAO,CACN;IAuHzD;;OAEG;IACH,mDAmBC;IAED;;;OAGG;IACH,6DAF6B,IAAI,QAUhC;IAED;;OAEG;IACH,uBAMC;IA8CD;;;;OAIG;IACH,8BAFW,MAAM,iCA4BhB;;CACJ;qBA3SyD,eAAe"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { InternMap } from "internmap";
|
|
2
|
-
import { group } from "d3-array";
|
|
2
|
+
import { bisector, group } from "d3-array";
|
|
3
3
|
import { compare } from "vega-util";
|
|
4
4
|
import iterateNestedMaps from "../utils/iterateNestedMaps.js";
|
|
5
5
|
import FlowNode, { BEHAVIOR_COLLECTS, isFacetBatch } from "./flowNode.js";
|
|
6
6
|
import { field } from "../utils/field.js";
|
|
7
7
|
import { asArray } from "../utils/arrayUtils.js";
|
|
8
|
+
import { radixSortIntoLookupArray } from "../utils/radixSort.js";
|
|
9
|
+
import { UNIQUE_ID_KEY } from "./transforms/identifier.js";
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Collects (materializes) the data that flows through this node.
|
|
@@ -13,6 +15,31 @@ import { asArray } from "../utils/arrayUtils.js";
|
|
|
13
15
|
* Grouping is primarily intended for handling faceted data.
|
|
14
16
|
*/
|
|
15
17
|
export default class Collector extends FlowNode {
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {import("./flowNode.js").Datum} Datum
|
|
20
|
+
* @typedef {import("./flowNode.js").Data} Data
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Current batch that is being collected.
|
|
25
|
+
* @type {Data}
|
|
26
|
+
*/
|
|
27
|
+
#buffer = [];
|
|
28
|
+
|
|
29
|
+
#uniqueIdAccessor = field(UNIQUE_ID_KEY);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @type {number[]}
|
|
33
|
+
*/
|
|
34
|
+
#uniqueIdIndex = [];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Start and end indices of all facets if they are concatenated into a single array.
|
|
38
|
+
* Used together with the uniqueIdIndex for looking up data items by their unique id.
|
|
39
|
+
* @type {{start: number, stop: number, facetId: import("../spec/channel.js").Scalar[]}[]}
|
|
40
|
+
*/
|
|
41
|
+
#facetIndices;
|
|
42
|
+
|
|
16
43
|
get behavior() {
|
|
17
44
|
return BEHAVIOR_COLLECTS;
|
|
18
45
|
}
|
|
@@ -28,32 +55,30 @@ export default class Collector extends FlowNode {
|
|
|
28
55
|
/** @type {(function(Collector):void)[]} */
|
|
29
56
|
this.observers = [];
|
|
30
57
|
|
|
31
|
-
|
|
32
|
-
|
|
58
|
+
// TODO: Consider nested maps instead of InternMap
|
|
59
|
+
/** @type {Map<import("../spec/channel.js").Scalar[], Data>} TODO: proper type for key */
|
|
60
|
+
this.facetBatches = new InternMap([], JSON.stringify);
|
|
33
61
|
|
|
34
|
-
this
|
|
62
|
+
this.#init();
|
|
35
63
|
}
|
|
36
64
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
this._data = [];
|
|
65
|
+
#init() {
|
|
66
|
+
this.#buffer = [];
|
|
40
67
|
|
|
41
|
-
|
|
42
|
-
this.facetBatches
|
|
43
|
-
this.facetBatches.set(undefined, this._data);
|
|
68
|
+
this.facetBatches.clear();
|
|
69
|
+
this.facetBatches.set(undefined, this.#buffer);
|
|
44
70
|
}
|
|
45
71
|
|
|
46
72
|
reset() {
|
|
47
73
|
super.reset();
|
|
48
|
-
this
|
|
74
|
+
this.#init();
|
|
49
75
|
}
|
|
50
76
|
|
|
51
77
|
/**
|
|
52
|
-
*
|
|
53
|
-
* @param {import("./flowNode.js").Datum} datum
|
|
78
|
+
* @param {Datum} datum
|
|
54
79
|
*/
|
|
55
80
|
handle(datum) {
|
|
56
|
-
this.
|
|
81
|
+
this.#buffer.push(datum);
|
|
57
82
|
}
|
|
58
83
|
|
|
59
84
|
/**
|
|
@@ -61,12 +86,15 @@ export default class Collector extends FlowNode {
|
|
|
61
86
|
*/
|
|
62
87
|
beginBatch(flowBatch) {
|
|
63
88
|
if (isFacetBatch(flowBatch)) {
|
|
64
|
-
this
|
|
65
|
-
this.facetBatches.set(asArray(flowBatch.facetId), this
|
|
89
|
+
this.#buffer = [];
|
|
90
|
+
this.facetBatches.set(asArray(flowBatch.facetId), this.#buffer);
|
|
66
91
|
}
|
|
67
92
|
}
|
|
68
93
|
|
|
69
94
|
complete() {
|
|
95
|
+
// Free some memory
|
|
96
|
+
this.#buffer = [];
|
|
97
|
+
|
|
70
98
|
const sort = this.params?.sort;
|
|
71
99
|
// Vega's "compare" function is incredibly slow (uses megamorphic field accessor)
|
|
72
100
|
// TODO: Implement a replacement for static data types
|
|
@@ -84,6 +112,8 @@ export default class Collector extends FlowNode {
|
|
|
84
112
|
throw new Error("TODO: Support faceted data!");
|
|
85
113
|
}
|
|
86
114
|
|
|
115
|
+
const data = this.facetBatches.get(undefined);
|
|
116
|
+
|
|
87
117
|
const accessors = this.params.groupby.map((fieldName) =>
|
|
88
118
|
field(fieldName)
|
|
89
119
|
);
|
|
@@ -91,10 +121,10 @@ export default class Collector extends FlowNode {
|
|
|
91
121
|
accessors.length > 1
|
|
92
122
|
? // There's something strange in d3-array's typings
|
|
93
123
|
/** @type {Map<any, any>} */ /** @type {any} */ (
|
|
94
|
-
group(
|
|
124
|
+
group(data, ...accessors)
|
|
95
125
|
)
|
|
96
126
|
: // D3's group is SLOW!
|
|
97
|
-
groupBy(
|
|
127
|
+
groupBy(data, accessors[0]);
|
|
98
128
|
|
|
99
129
|
this.facetBatches.clear();
|
|
100
130
|
for (const [key, data] of iterateNestedMaps(groups)) {
|
|
@@ -107,6 +137,7 @@ export default class Collector extends FlowNode {
|
|
|
107
137
|
sortData(data);
|
|
108
138
|
}
|
|
109
139
|
|
|
140
|
+
this.#buildUniqueIdIndex();
|
|
110
141
|
this.#propagateToChildren();
|
|
111
142
|
|
|
112
143
|
super.complete();
|
|
@@ -118,16 +149,16 @@ export default class Collector extends FlowNode {
|
|
|
118
149
|
|
|
119
150
|
#propagateToChildren() {
|
|
120
151
|
if (this.children.length) {
|
|
121
|
-
for (const [
|
|
122
|
-
if (
|
|
152
|
+
for (const [facetId, data] of this.facetBatches.entries()) {
|
|
153
|
+
if (facetId) {
|
|
123
154
|
/** @type {import("../types/flowBatch.js").FacetBatch} */
|
|
124
|
-
const facetBatch = { type: "facet", facetId
|
|
155
|
+
const facetBatch = { type: "facet", facetId };
|
|
125
156
|
for (const child of this.children) {
|
|
126
157
|
child.beginBatch(facetBatch);
|
|
127
158
|
}
|
|
128
159
|
}
|
|
129
|
-
for (
|
|
130
|
-
this._propagate(
|
|
160
|
+
for (let i = 0, n = data.length; i < n; i++) {
|
|
161
|
+
this._propagate(data[i]);
|
|
131
162
|
}
|
|
132
163
|
}
|
|
133
164
|
}
|
|
@@ -146,10 +177,10 @@ export default class Collector extends FlowNode {
|
|
|
146
177
|
}
|
|
147
178
|
|
|
148
179
|
/**
|
|
149
|
-
* @returns {Iterable<
|
|
180
|
+
* @returns {Iterable<Datum>}
|
|
150
181
|
*/
|
|
151
182
|
getData() {
|
|
152
|
-
this
|
|
183
|
+
this.#checkStatus();
|
|
153
184
|
|
|
154
185
|
switch (this.facetBatches.size) {
|
|
155
186
|
case 0:
|
|
@@ -161,9 +192,7 @@ export default class Collector extends FlowNode {
|
|
|
161
192
|
return {
|
|
162
193
|
[Symbol.iterator]: function* generator() {
|
|
163
194
|
for (const data of groups.values()) {
|
|
164
|
-
|
|
165
|
-
yield data[i];
|
|
166
|
-
}
|
|
195
|
+
yield* data;
|
|
167
196
|
}
|
|
168
197
|
},
|
|
169
198
|
};
|
|
@@ -173,10 +202,10 @@ export default class Collector extends FlowNode {
|
|
|
173
202
|
|
|
174
203
|
/**
|
|
175
204
|
*
|
|
176
|
-
* @param {(datum:
|
|
205
|
+
* @param {(datum: Datum) => void} visitor
|
|
177
206
|
*/
|
|
178
207
|
visitData(visitor) {
|
|
179
|
-
this
|
|
208
|
+
this.#checkStatus();
|
|
180
209
|
|
|
181
210
|
for (const data of this.facetBatches.values()) {
|
|
182
211
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -196,21 +225,90 @@ export default class Collector extends FlowNode {
|
|
|
196
225
|
return count;
|
|
197
226
|
}
|
|
198
227
|
|
|
199
|
-
|
|
228
|
+
#checkStatus() {
|
|
200
229
|
if (!this.completed) {
|
|
201
230
|
throw new Error(
|
|
202
231
|
"Data propagation is not completed! No data are available."
|
|
203
232
|
);
|
|
204
233
|
}
|
|
205
234
|
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Builds an index for looking up data items by their unique id.
|
|
238
|
+
* Using a sorted index and binary search for O(log n) complexity.
|
|
239
|
+
*/
|
|
240
|
+
#buildUniqueIdIndex() {
|
|
241
|
+
this.#facetIndices = [];
|
|
242
|
+
|
|
243
|
+
/** @type {Datum} */
|
|
244
|
+
const obj = this.facetBatches.values().next().value?.[0];
|
|
245
|
+
if (obj == null || !(UNIQUE_ID_KEY in obj)) {
|
|
246
|
+
return; // No unique ids in the data
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let cumulativePos = 0;
|
|
250
|
+
|
|
251
|
+
/** @type {number[]} */
|
|
252
|
+
const ids = [];
|
|
253
|
+
|
|
254
|
+
const a = this.#uniqueIdAccessor;
|
|
255
|
+
|
|
256
|
+
for (const [facetId, data] of this.facetBatches) {
|
|
257
|
+
this.#facetIndices.push({
|
|
258
|
+
start: cumulativePos,
|
|
259
|
+
stop: cumulativePos + data.length,
|
|
260
|
+
facetId,
|
|
261
|
+
});
|
|
262
|
+
cumulativePos += data.length;
|
|
263
|
+
|
|
264
|
+
for (let i = 0, n = data.length; i < n; i++) {
|
|
265
|
+
ids.push(a(data[i]));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
this.#uniqueIdIndex = radixSortIntoLookupArray(ids);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Use an index to find a datum by its unique id.
|
|
274
|
+
*
|
|
275
|
+
* @param {number} uniqueId
|
|
276
|
+
*/
|
|
277
|
+
findDatumByUniqueId(uniqueId) {
|
|
278
|
+
if (!this.#uniqueIdIndex.length) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const facetBisector = bisector((f) => f.start).right;
|
|
283
|
+
const a = this.#uniqueIdAccessor;
|
|
284
|
+
const indexBisector = bisector((i) => a(getDatum(i))).left;
|
|
285
|
+
|
|
286
|
+
const getDatum = (/** @type {number} */ i) => {
|
|
287
|
+
const fi = facetBisector(this.#facetIndices, i);
|
|
288
|
+
const facet = this.#facetIndices[fi - 1];
|
|
289
|
+
if (!facet || i >= facet.stop) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const data = this.facetBatches.get(facet.facetId);
|
|
293
|
+
return data[i - facet.start];
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const index = indexBisector(this.#uniqueIdIndex, uniqueId);
|
|
297
|
+
if (index >= 0) {
|
|
298
|
+
const datum = getDatum(this.#uniqueIdIndex[index]);
|
|
299
|
+
if (datum && a(datum) === uniqueId) {
|
|
300
|
+
return datum;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
206
304
|
}
|
|
207
305
|
|
|
208
306
|
/**
|
|
209
307
|
* Like D3's group but without InternMap, which is slow.
|
|
210
308
|
* TODO: Implement multi-level grouping
|
|
211
309
|
*
|
|
212
|
-
* @param {
|
|
213
|
-
* @param {(data:
|
|
310
|
+
* @param {Datum[]} data
|
|
311
|
+
* @param {(data: Datum) => import("../spec/channel.js").Scalar} accessor
|
|
214
312
|
*/
|
|
215
313
|
function groupBy(data, accessor) {
|
|
216
314
|
const groups = new Map();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { expect, test } from "vitest";
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
2
|
|
|
3
3
|
import Collector from "./collector.js";
|
|
4
|
+
import { UNIQUE_ID_KEY } from "./transforms/identifier.js";
|
|
4
5
|
|
|
5
6
|
const data = [1, 5, 2, 4, 3].map((x) => ({ x }));
|
|
6
7
|
|
|
@@ -82,3 +83,56 @@ test("Collector throws on incomplete flow", () => {
|
|
|
82
83
|
|
|
83
84
|
expect(() => collector.getData()).toThrow();
|
|
84
85
|
});
|
|
86
|
+
|
|
87
|
+
describe("Indexing unique ids", () => {
|
|
88
|
+
test("Collector builds a working index when ids are available", () => {
|
|
89
|
+
const collector = new Collector({
|
|
90
|
+
type: "collect",
|
|
91
|
+
groupby: ["a"],
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const data = [
|
|
95
|
+
{ a: 1, x: 1, [UNIQUE_ID_KEY]: 8 },
|
|
96
|
+
{ a: 1, x: 2, [UNIQUE_ID_KEY]: 2 },
|
|
97
|
+
{ a: 1, x: 3, [UNIQUE_ID_KEY]: 4 },
|
|
98
|
+
{ a: 1, x: 4, [UNIQUE_ID_KEY]: 6 },
|
|
99
|
+
{ a: 2, x: 5, [UNIQUE_ID_KEY]: 9 },
|
|
100
|
+
{ a: 2, x: 6, [UNIQUE_ID_KEY]: 7 },
|
|
101
|
+
{ a: 2, x: 7, [UNIQUE_ID_KEY]: 3 },
|
|
102
|
+
{ a: 2, x: 8, [UNIQUE_ID_KEY]: 1 },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
for (const d of data) {
|
|
106
|
+
collector.handle(d);
|
|
107
|
+
}
|
|
108
|
+
collector.complete();
|
|
109
|
+
|
|
110
|
+
expect(collector.findDatumByUniqueId(8)).toEqual(data[0]);
|
|
111
|
+
expect(collector.findDatumByUniqueId(2)).toEqual(data[1]);
|
|
112
|
+
expect(collector.findDatumByUniqueId(4)).toEqual(data[2]);
|
|
113
|
+
expect(collector.findDatumByUniqueId(6)).toEqual(data[3]);
|
|
114
|
+
expect(collector.findDatumByUniqueId(9)).toEqual(data[4]);
|
|
115
|
+
expect(collector.findDatumByUniqueId(7)).toEqual(data[5]);
|
|
116
|
+
expect(collector.findDatumByUniqueId(3)).toEqual(data[6]);
|
|
117
|
+
expect(collector.findDatumByUniqueId(1)).toEqual(data[7]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("Collector returns undefined when ids are not available", () => {
|
|
121
|
+
const collector = new Collector({
|
|
122
|
+
type: "collect",
|
|
123
|
+
groupby: ["a"],
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const data = [
|
|
127
|
+
{ a: 1, x: 1 },
|
|
128
|
+
{ a: 2, x: 5 },
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
for (const d of data) {
|
|
132
|
+
collector.handle(d);
|
|
133
|
+
}
|
|
134
|
+
collector.complete();
|
|
135
|
+
|
|
136
|
+
expect(collector.findDatumByUniqueId(0)).toBeUndefined();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identifier.d.ts","sourceRoot":"","sources":["../../../../src/data/transforms/identifier.js"],"names":[],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"identifier.d.ts","sourceRoot":"","sources":["../../../../src/data/transforms/identifier.js"],"names":[],"mappings":"AAEA,wCAAyC;AAEzC,+BAAgC;AAehC;;;;;;;GAOG;AACH;IAKI;;;OAGG;IACH,oBAFW,OAAO,yBAAyB,EAAE,gBAAgB,EAuB5D;IAnBG,2DAAoB;IAEpB,WAAoC;IAEpC;;;OAGG;IACH,SAFU,MAAM,EAAE,CAED;IAEjB;;OAEG;IACH,oBAAoB;IAEpB;;OAEG;IACH,YAAa;IAuBjB;;OAEG;IACH,WAFa,MAAM,CAOlB;IAED,oBAMC;IAED,wBAOC;CACJ;qBAzG2C,gBAAgB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import FlowNode, { BEHAVIOR_MODIFIES } from "../flowNode.js";
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const UNIQUE_ID_KEY = "_uniqueId";
|
|
4
4
|
|
|
5
5
|
export const BLOCK_SIZE = 10000;
|
|
6
6
|
|
|
@@ -38,7 +38,7 @@ export default class IdentifierTransform extends FlowNode {
|
|
|
38
38
|
super();
|
|
39
39
|
this.params = params;
|
|
40
40
|
|
|
41
|
-
this.as = params.as ??
|
|
41
|
+
this.as = params.as ?? UNIQUE_ID_KEY;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* The block indexes reserved by this transform instance.
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
2
|
import { range } from "d3-array";
|
|
3
3
|
import { processData } from "../flowTestUtils.js";
|
|
4
|
-
import IdentifierTransform, {
|
|
4
|
+
import IdentifierTransform, {
|
|
5
|
+
BLOCK_SIZE,
|
|
6
|
+
UNIQUE_ID_KEY,
|
|
7
|
+
} from "./identifier.js";
|
|
5
8
|
|
|
6
9
|
test("An IdentifierTransform adds identifiers correctly", () => {
|
|
7
10
|
const data = range(BLOCK_SIZE * 2).map((x) => ({ data: x }));
|
|
@@ -14,15 +17,18 @@ test("An IdentifierTransform adds identifiers correctly", () => {
|
|
|
14
17
|
// The fist block is skipped
|
|
15
18
|
const firstId = BLOCK_SIZE;
|
|
16
19
|
|
|
17
|
-
expect(identifiedData[0]).toEqual({ data: 0, [
|
|
18
|
-
expect(identifiedData[1]).toEqual({
|
|
20
|
+
expect(identifiedData[0]).toEqual({ data: 0, [UNIQUE_ID_KEY]: firstId });
|
|
21
|
+
expect(identifiedData[1]).toEqual({
|
|
22
|
+
data: 1,
|
|
23
|
+
[UNIQUE_ID_KEY]: firstId + 1,
|
|
24
|
+
});
|
|
19
25
|
expect(identifiedData[BLOCK_SIZE]).toEqual({
|
|
20
26
|
data: BLOCK_SIZE,
|
|
21
|
-
[
|
|
27
|
+
[UNIQUE_ID_KEY]: firstId + BLOCK_SIZE,
|
|
22
28
|
});
|
|
23
29
|
expect(identifiedData[BLOCK_SIZE + 1]).toEqual({
|
|
24
30
|
data: BLOCK_SIZE + 1,
|
|
25
|
-
[
|
|
31
|
+
[UNIQUE_ID_KEY]: firstId + BLOCK_SIZE + 1,
|
|
26
32
|
});
|
|
27
33
|
});
|
|
28
34
|
|
|
@@ -37,15 +43,18 @@ test("Another transform instance adds identifiers correctly", () => {
|
|
|
37
43
|
// The fist block was skipped and the previous test case consumed two blocks
|
|
38
44
|
const firstId = BLOCK_SIZE * 3;
|
|
39
45
|
|
|
40
|
-
expect(identifiedData[0]).toEqual({ data: 0, [
|
|
41
|
-
expect(identifiedData[1]).toEqual({
|
|
46
|
+
expect(identifiedData[0]).toEqual({ data: 0, [UNIQUE_ID_KEY]: firstId });
|
|
47
|
+
expect(identifiedData[1]).toEqual({
|
|
48
|
+
data: 1,
|
|
49
|
+
[UNIQUE_ID_KEY]: firstId + 1,
|
|
50
|
+
});
|
|
42
51
|
expect(identifiedData[BLOCK_SIZE]).toEqual({
|
|
43
52
|
data: BLOCK_SIZE,
|
|
44
|
-
[
|
|
53
|
+
[UNIQUE_ID_KEY]: firstId + BLOCK_SIZE,
|
|
45
54
|
});
|
|
46
55
|
expect(identifiedData[BLOCK_SIZE + 1]).toEqual({
|
|
47
56
|
data: BLOCK_SIZE + 1,
|
|
48
|
-
[
|
|
57
|
+
[UNIQUE_ID_KEY]: firstId + BLOCK_SIZE + 1,
|
|
49
58
|
});
|
|
50
59
|
});
|
|
51
60
|
|
|
@@ -57,10 +66,10 @@ test("IdentifierTransform recycles allocated blocks", () => {
|
|
|
57
66
|
|
|
58
67
|
let firstId = BLOCK_SIZE * 5;
|
|
59
68
|
|
|
60
|
-
expect(identifiedData[0]).toEqual({ data: 0, [
|
|
69
|
+
expect(identifiedData[0]).toEqual({ data: 0, [UNIQUE_ID_KEY]: firstId });
|
|
61
70
|
expect(identifiedData[BLOCK_SIZE]).toEqual({
|
|
62
71
|
data: BLOCK_SIZE,
|
|
63
|
-
[
|
|
72
|
+
[UNIQUE_ID_KEY]: firstId + BLOCK_SIZE,
|
|
64
73
|
});
|
|
65
74
|
|
|
66
75
|
data = range(BLOCK_SIZE * 3).map((x) => ({ data: x }));
|
|
@@ -69,15 +78,15 @@ test("IdentifierTransform recycles allocated blocks", () => {
|
|
|
69
78
|
transform.reset();
|
|
70
79
|
identifiedData = processData(transform, data);
|
|
71
80
|
|
|
72
|
-
expect(identifiedData[0]).toEqual({ data: 0, [
|
|
81
|
+
expect(identifiedData[0]).toEqual({ data: 0, [UNIQUE_ID_KEY]: firstId });
|
|
73
82
|
expect(identifiedData[BLOCK_SIZE]).toEqual({
|
|
74
83
|
data: BLOCK_SIZE,
|
|
75
|
-
[
|
|
84
|
+
[UNIQUE_ID_KEY]: firstId + BLOCK_SIZE,
|
|
76
85
|
});
|
|
77
86
|
|
|
78
87
|
// ... and reserve one extra
|
|
79
88
|
expect(identifiedData[BLOCK_SIZE * 2]).toEqual({
|
|
80
89
|
data: BLOCK_SIZE * 2,
|
|
81
|
-
[
|
|
90
|
+
[UNIQUE_ID_KEY]: firstId + BLOCK_SIZE * 2,
|
|
82
91
|
});
|
|
83
92
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"genomeSpy.d.ts","sourceRoot":"","sources":["../../src/genomeSpy.js"],"names":[],"mappings":"AA+CA;IACI;;;;;OAKG;IAEH;;;;;OAKG;IACH,uBAJW,WAAW,qDAEX,OAAO,qBAAqB,EAAE,YAAY,EA2FpD;IAxFG,uBAA0B;IAC1B,oDAAsB;IAItB,6BAA6B;IAC7B,uBADW,CAAC,MAAM,IAAI,CAAC,EAAE,CACM;IAE/B,sCAAsC;IACtC,wCAAgB;IAEhB,iCAA4C;IAC5C,yBAAoC;IAEpC,4CAA4C;IAC5C,oBADW,QAAU,MAAM,KAAE,MAAM,EAAE,CAAC,EAAE,CACZ;IAE5B,mBAAoD;IAEpD,0BAA0B;IAC1B,aADW,WAAW,CACM;IAE5B;;;;;OAKG;IACH,qEAF0B,OAAO,CAE8B;IAE/D,2CAA2C;IAC3C,mBADW,4BAA4B,CACL;IAClC,2CAA2C;IAC3C,iBADW,4BAA4B,CACP;IAEhC,oDAAoD;IACpD,6BAAgC;IAEhC;;;OAGG;IACH,eAFU;QAAE,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,oBAAoB,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAEpF;IAE9B,uBAA+C;IAE/C;;;OAGG;IACH,oBAFU,IAAI,MAAM,EAAE,QAAU,aAAa,KAAE,IAAI,CAAC,EAAE,CAAC,CAEpB;IAEnC;;;;;;OAMG;IACH,yCAFkC,GAAG,KAAK,IAAI,GAEd;IAEhC;;;OAGG;IACH,kDAFkC,GAAG,KAAK,IAAI,GAEL;IAEzC,oFAAoF;IACpF,iBADW,OAAO,MAAM,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC,CAK9E;IAED,mBAAmB;IACnB,2CAAyB;IAEzB;;;;OAIG;IACH;gBAF8B,OAAO,wBAAwB,EAAE,iBAAiB;iBAAW,MAAM;OAEnE;IAE9B;;OAEG;IACH,wBAFU,WAAW,CAEkB;IA2C3C;;;OAGG;IACH,2CAFkB,MAAM,KAAK,GAAG,EAAE,QAIjC;IAED;;OAEG;IACH,+BAFW,MAAM,YAShB;IAED;;;;OAIG;IACH,sBAHW,MAAM,QACN,GAAG,EAAE,QAaf;IAED;;;;;OAKG;IACH,gBAHW,kBAAkB,YAClB,GAAG,QAQb;IAED;;;OAGG;IACH,iCAqDC;IA0DG,uBAQC;IAGD,mCAGE;IAOF,sCAEE;IAGF,iBAAyC;IAW7C;;OAEG;IACH,gBAuBC;IAED,sCA8MC;IAED;;;OAGG;IACH,UAFa,QAAQ,OAAO,CAAC,CA6B5B;IAED,4BAqJC;IA9He,iCAAoC;IAgIpD;;;OAGG;IACH,kBAHW,MAAM,KACN,MAAM,
|
|
1
|
+
{"version":3,"file":"genomeSpy.d.ts","sourceRoot":"","sources":["../../src/genomeSpy.js"],"names":[],"mappings":"AA+CA;IACI;;;;;OAKG;IAEH;;;;;OAKG;IACH,uBAJW,WAAW,qDAEX,OAAO,qBAAqB,EAAE,YAAY,EA2FpD;IAxFG,uBAA0B;IAC1B,oDAAsB;IAItB,6BAA6B;IAC7B,uBADW,CAAC,MAAM,IAAI,CAAC,EAAE,CACM;IAE/B,sCAAsC;IACtC,wCAAgB;IAEhB,iCAA4C;IAC5C,yBAAoC;IAEpC,4CAA4C;IAC5C,oBADW,QAAU,MAAM,KAAE,MAAM,EAAE,CAAC,EAAE,CACZ;IAE5B,mBAAoD;IAEpD,0BAA0B;IAC1B,aADW,WAAW,CACM;IAE5B;;;;;OAKG;IACH,qEAF0B,OAAO,CAE8B;IAE/D,2CAA2C;IAC3C,mBADW,4BAA4B,CACL;IAClC,2CAA2C;IAC3C,iBADW,4BAA4B,CACP;IAEhC,oDAAoD;IACpD,6BAAgC;IAEhC;;;OAGG;IACH,eAFU;QAAE,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,oBAAoB,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAEpF;IAE9B,uBAA+C;IAE/C;;;OAGG;IACH,oBAFU,IAAI,MAAM,EAAE,QAAU,aAAa,KAAE,IAAI,CAAC,EAAE,CAAC,CAEpB;IAEnC;;;;;;OAMG;IACH,yCAFkC,GAAG,KAAK,IAAI,GAEd;IAEhC;;;OAGG;IACH,kDAFkC,GAAG,KAAK,IAAI,GAEL;IAEzC,oFAAoF;IACpF,iBADW,OAAO,MAAM,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC,CAK9E;IAED,mBAAmB;IACnB,2CAAyB;IAEzB;;;;OAIG;IACH;gBAF8B,OAAO,wBAAwB,EAAE,iBAAiB;iBAAW,MAAM;OAEnE;IAE9B;;OAEG;IACH,wBAFU,WAAW,CAEkB;IA2C3C;;;OAGG;IACH,2CAFkB,MAAM,KAAK,GAAG,EAAE,QAIjC;IAED;;OAEG;IACH,+BAFW,MAAM,YAShB;IAED;;;;OAIG;IACH,sBAHW,MAAM,QACN,GAAG,EAAE,QAaf;IAED;;;;;OAKG;IACH,gBAHW,kBAAkB,YAClB,GAAG,QAQb;IAED;;;OAGG;IACH,iCAqDC;IA0DG,uBAQC;IAGD,mCAGE;IAOF,sCAEE;IAGF,iBAAyC;IAW7C;;OAEG;IACH,gBAuBC;IAED,sCA8MC;IAED;;;OAGG;IACH,UAFa,QAAQ,OAAO,CAAC,CA6B5B;IAED,4BAqJC;IA9He,iCAAoC;IAgIpD;;;OAGG;IACH,kBAHW,MAAM,KACN,MAAM,QAqEhB;IAED;;;;;;;OAOG;IACH,oDAHuB,QAAQ,MAAM,GAAG,WAAW,GAAG,OAAO,KAAK,EAAE,cAAc,CAAC,QAYlF;IAED,sBA6CC;IAED,kBAIC;IAED,iCAOC;IAED,iCASC;IAED,qFAWC;;CACJ;;;;iCAv7BY,eAAe,GAAG,YAAY,GAAG,QAAQ,GAAG,gBAAgB;4BAlC7C,uBAAuB;4BA2BP,uBAAuB;qBAZ9C,qBAAqB;wBAIlB,yBAAyB;yCARR,yDAAyD;oBAYvD,oBAAoB;wBAdvC,qBAAqB;oBAXzB,uBAAuB;qBAStB,oBAAoB"}
|
package/dist/src/genomeSpy.js
CHANGED
|
@@ -822,7 +822,10 @@ export default class GenomeSpy {
|
|
|
822
822
|
const pixelValue = this._glHelper.readPickingPixel(x, y);
|
|
823
823
|
|
|
824
824
|
const uniqueId =
|
|
825
|
-
pixelValue[0] |
|
|
825
|
+
pixelValue[0] |
|
|
826
|
+
(pixelValue[1] << 8) |
|
|
827
|
+
(pixelValue[2] << 16) |
|
|
828
|
+
(pixelValue[3] << 24);
|
|
826
829
|
|
|
827
830
|
if (uniqueId == 0) {
|
|
828
831
|
this._currentHover = null;
|
|
@@ -834,10 +837,6 @@ export default class GenomeSpy {
|
|
|
834
837
|
}
|
|
835
838
|
|
|
836
839
|
if (!this._currentHover) {
|
|
837
|
-
// We are doing an exhaustive search of the data. This is a bit slow with
|
|
838
|
-
// millions of items.
|
|
839
|
-
// TODO: Optimize by indexing or something
|
|
840
|
-
|
|
841
840
|
this.viewRoot.visit((view) => {
|
|
842
841
|
if (view instanceof UnitView) {
|
|
843
842
|
if (
|
|
@@ -846,16 +845,16 @@ export default class GenomeSpy {
|
|
|
846
845
|
coords.containsPoint(x, y)
|
|
847
846
|
)
|
|
848
847
|
) {
|
|
849
|
-
const
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
}
|
|
858
|
-
}
|
|
848
|
+
const datum = view
|
|
849
|
+
.getCollector()
|
|
850
|
+
.findDatumByUniqueId(uniqueId);
|
|
851
|
+
if (datum) {
|
|
852
|
+
this._currentHover = {
|
|
853
|
+
mark: view.mark,
|
|
854
|
+
datum,
|
|
855
|
+
uniqueId,
|
|
856
|
+
};
|
|
857
|
+
}
|
|
859
858
|
}
|
|
860
859
|
if (this._currentHover) {
|
|
861
860
|
return VISIT_STOP;
|
|
@@ -60,7 +60,7 @@ export class GeometryBuilder {
|
|
|
60
60
|
addToXIndex(datum: import("../data/flowNode.js").Datum): void;
|
|
61
61
|
xIndexer: {
|
|
62
62
|
(datum: any, startVertexIndex: number, endVertexIndex: number): void;
|
|
63
|
-
getIndex: () => import("../utils/binnedIndex.js").Lookup;
|
|
63
|
+
getIndex: () => import("../utils/binnedIndex.js").Lookup; /** @param {Encoder} encoder */
|
|
64
64
|
};
|
|
65
65
|
toArrays(): {
|
|
66
66
|
/** @type {Record<string, {data: Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array, numComponents: number, divisor?: number}>} */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataToVertices.d.ts","sourceRoot":"","sources":["../../../src/gl/dataToVertices.js"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH;IACI;;;OAGG;IAEH;;;;;;OAMG;IACH;;qBAJW,MAAM,EAAE;sBACR,MAAM;
|
|
1
|
+
{"version":3,"file":"dataToVertices.d.ts","sourceRoot":"","sources":["../../../src/gl/dataToVertices.js"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH;IACI;;;OAGG;IAEH;;;;;;OAMG;IACH;;qBAJW,MAAM,EAAE;sBACR,MAAM;OAgFhB;IA5EG,gEAAwB;IAGxB;;MAKC;IAQD,0BAAoC;IAEpC,8BAAoD;IAsDpD,mBAAmB;IAEnB,yFAAyF;IACzF,UADW,IAAI,GAAG,EAAE,UAAU,CAAC,CACkB;IAGrD;;;;OAIG;IACH,mBAFW,GAAG,QAcb;IAED;;OAEG;IACH,oBAFW,IAAI,GAAG,EAAE,MAAM,EAAE,CAAC,QAM5B;IAED;;;OAGG;IACH,cAHW,GAAG,QACH,MAAM,EAAE,kCAYlB;IAED;;;;OAIG;IACH,sBAJW,OAAO,qBAAqB,EAAE,IAAI,OAClC,MAAM,OACN,MAAM,QA+DhB;IAED;;;;;OAKG;IACH,mBAFW,OAAO,qBAAqB,EAAE,KAAK,QAI7C;IA/DO;;kEAcJ,+BAA+B;MAdF;IAiEjC;QAEQ,kJAAkJ;gBAAvI,OAAO,MAAM,EAAE;YAAC,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,GAAG,YAAY,CAAC;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAC,CAAC;QAE9I,8BAA8B;;QAE9B,8CAA8C;;;MAIrD;CACJ;AAED;IACI;;;;;;OAMG;IACH;;oBAHW,MAAM,EAAE;mBACR,MAAM;OA+BhB;IAnBG,qBAkBW;CA8BlB;AAED;IACI;;;;;;;;;OASG;IACH;;oBANW,MAAM,EAAE;gCACR,MAAM;uBAEN,MAAM,EAAE;mBACR,MAAM;OAwBhB;IARG,uBAAgC;IAEhC,8BAA8D;IAE9D,6CAA+D;IAC/D,4CAA6D;CA6CpE;AAED;IACI;;;;;;OAMG;IACH;;oBAHW,MAAM,EAAE;mBACR,MAAM;OAShB;CACJ;AAED;IACI;;;;;OAKG;IACH;;oBAHW,MAAM,EAAE;mBACR,MAAM;OAShB;CAYJ;AAED;IACI;;;;;;;;;OASG;IACH;;oBANW,MAAM,EAAE;qBACR,OAAO,2BAA2B,EAAE,aAAa;oBACjD,OAAO,MAAM,EAAE,GAAG,CAAC;wBACnB,MAAM;sBACN,OAAO;OA8CjB;IA/BG,4DAA2B;IAC3B,2DAA0B;IAE1B,gCAA4B;IAQ5B,qCAAqC;IACrC,sBADmB,GAAG,KAAK,MAAM,CAMf;IAElB,oDAGC;IACD,qDAGC;IAED,8CAAiE;CAgJxE;;;;;;;;YA1mBS,MAAM;;;;WACN,MAAM;YACN,OAAO,yBAAyB,EAAE,MAAM;;yBAjBzB,mBAAmB"}
|
|
@@ -95,11 +95,14 @@ export class GeometryBuilder {
|
|
|
95
95
|
f,
|
|
96
96
|
numComponents: largeHp ? 2 : 1,
|
|
97
97
|
arrayReference: largeHp ? largeHpArray : undefined,
|
|
98
|
-
targetArrayType:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
targetArrayType:
|
|
99
|
+
channel == "uniqueId"
|
|
100
|
+
? Uint32Array
|
|
101
|
+
: isDiscrete(ce.scale.type)
|
|
102
|
+
? Uint16Array
|
|
103
|
+
: hp
|
|
104
|
+
? Uint32Array
|
|
105
|
+
: Float32Array,
|
|
103
106
|
});
|
|
104
107
|
}
|
|
105
108
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const shader = "/**Based on concepts presented at:*https:*https:*/out highp vec4 vPickingColor;/***Passes the unique id to the fragment shader as a color if picking is enabled.*Returns true if picking is enabled.*/bool setupPicking(){if(uPickingEnabled){\n#ifdef uniqueId_DEFINED\
|
|
1
|
+
const shader = "/**Based on concepts presented at:*https:*https:*/out highp vec4 vPickingColor;/***Passes the unique id to the fragment shader as a color if picking is enabled.*Returns true if picking is enabled.*/bool setupPicking(){if(uPickingEnabled){\n#ifdef uniqueId_DEFINED\nuint id=attr_uniqueId;vPickingColor=vec4(ivec4(id>>0,id>>8,id>>16,id>>24)&0xFF)/float(0xFF);\n#else\nvPickingColor=vec4(1.0);\n#endif\nreturn true;}return false;}";
|
|
2
2
|
export default shader;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const shader = "layout(std140)uniform Mark{uniform float uArcHeightFactor;uniform float uMinArcHeight;uniform float uMinPickingSize;uniform int uShape;uniform int uOrient;uniform bool uClampApex;uniform float uMaxChordLength;uniform vec2 uArcFadingDistance;\n#pragma markUniforms\n};";
|
|
1
|
+
const shader = "layout(std140)uniform Mark{uniform float uArcHeightFactor;uniform float uMinArcHeight;uniform float uMinPickingSize;uniform int uShape;uniform int uOrient;uniform bool uClampApex;uniform float uMaxChordLength;uniform vec2 uArcFadingDistance;uniform int uSegmentBreaks;\n#pragma markUniforms\n};";
|
|
2
2
|
export default shader;
|