@genome-spy/core 0.43.0 → 0.43.1

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.
@@ -8,6 +8,12 @@ export default class BigBedSource extends SingleAxisWindowedSource {
8
8
  parser: import("@gmod/bed").default;
9
9
  /** @type {import("@gmod/bbi").BigBed} */
10
10
  bbi: import("@gmod/bbi").BigBed;
11
+ /** @type {(chrom: string, fields: { start: number, end: number, rest?: string }) => Record<string, any>} */
12
+ parseLine: (chrom: string, fields: {
13
+ start: number;
14
+ end: number;
15
+ rest?: string;
16
+ }) => Record<string, any>;
11
17
  params: import("../../../spec/data.js").BigBedData;
12
18
  initializedPromise: Promise<any>;
13
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bigBedSource.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/lazy/bigBedSource.js"],"names":[],"mappings":"AAGA;IAOI;;;OAGG;IACH,oBAHW,OAAO,uBAAuB,EAAE,UAAU,QAC1C,OAAO,uBAAuB,EAAE,OAAO,EA4CjD;IApDD,0CAA0C;IAC1C,QADW,OAAO,WAAW,EAAE,OAAO,CAC/B;IAEP,yCAAyC;IACzC,KADW,OAAO,WAAW,EAAE,MAAM,CACjC;IAkBA,mDAAgC;IAQhC,iCAqBE;CA4BT;qCAlFoC,+BAA+B"}
1
+ {"version":3,"file":"bigBedSource.d.ts","sourceRoot":"","sources":["../../../../../src/data/sources/lazy/bigBedSource.js"],"names":[],"mappings":"AAGA;IAUI;;;OAGG;IACH,oBAHW,OAAO,uBAAuB,EAAE,UAAU,QAC1C,OAAO,uBAAuB,EAAE,OAAO,EAsDjD;IAjED,0CAA0C;IAC1C,QADW,OAAO,WAAW,EAAE,OAAO,CAC/B;IAEP,yCAAyC;IACzC,KADW,OAAO,WAAW,EAAE,MAAM,CACjC;IAEJ,4GAA4G;IAC5G,mBADmB,MAAM,UAAU;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,MAAM,EAAE,GAAG,CAAC,CAC9F;IAkBN,mDAAgC;IAQhC,iCA+BE;CAuBT;qCA1FoC,+BAA+B"}
@@ -8,6 +8,9 @@ export default class BigBedSource extends SingleAxisWindowedSource {
8
8
  /** @type {import("@gmod/bbi").BigBed} */
9
9
  bbi;
10
10
 
11
+ /** @type {(chrom: string, fields: { start: number, end: number, rest?: string }) => Record<string, any>} */
12
+ parseLine;
13
+
11
14
  /**
12
15
  * @param {import("../../../spec/data.js").BigBedData} params
13
16
  * @param {import("../../../view/view.js").default} view
@@ -49,6 +52,16 @@ export default class BigBedSource extends SingleAxisWindowedSource {
49
52
  this.bbi.getHeader().then(async (header) => {
50
53
  // @ts-ignore TODO: Fix
51
54
  this.parser = new BED({ autoSql: header.autoSql });
55
+ try {
56
+ const fastParser = makeFastParser(this.parser);
57
+ this.parseLine = (chrom, f) =>
58
+ fastParser(chrom, f.start, f.end, f.rest);
59
+ } catch (e) {
60
+ this.parseLine = (chrom, f) =>
61
+ this.parser.parseLine(
62
+ `${chrom}\t${f.start}\t${f.end}\t${f.rest}`
63
+ );
64
+ }
52
65
 
53
66
  resolve();
54
67
  });
@@ -68,12 +81,7 @@ export default class BigBedSource extends SingleAxisWindowedSource {
68
81
  signal,
69
82
  })
70
83
  .then((features) =>
71
- features.map((f) =>
72
- this.parser.parseLine(
73
- `${d.chrom}\t${f.start}\t${f.end}\t${f.rest}`,
74
- { uniqueId: f.uniqueId }
75
- )
76
- )
84
+ features.map((f) => this.parseLine(d.chrom, f))
77
85
  )
78
86
  );
79
87
 
@@ -82,3 +90,117 @@ export default class BigBedSource extends SingleAxisWindowedSource {
82
90
  }
83
91
  }
84
92
  }
93
+
94
+ /**
95
+ * A specific optimization for Hautaniemi Lab's Methylation project, where
96
+ * we have hundreds of columns having small integers (0-100).
97
+ * This parser avoids generating piles of garbage to be collected by the GC.
98
+ * We don't split the line into an array of strings, but instead parse the
99
+ * integer fields directly from the original string.
100
+ * This parser doesn't support arrays, etc. at the moment.
101
+ *
102
+ * @param {import("@gmod/bed").default} bed
103
+ */
104
+ function makeFastParser(bed) {
105
+ // Skip the first three fields: chrom, chromStart, chromEnd
106
+ const fields = bed.autoSql.fields.filter((field) => field.type).slice(3);
107
+
108
+ let i = 0;
109
+ let currentLine = "";
110
+ let lineLength = 0;
111
+
112
+ /** @type {Record<string, any>} */
113
+ let currentObject = {};
114
+
115
+ const delimiter = "\t";
116
+ const delimiterCode = delimiter.charCodeAt(0);
117
+ const zero = "0".charCodeAt(0);
118
+ const minusCode = "-".charCodeAt(0);
119
+
120
+ function parseString() {
121
+ let end = currentLine.indexOf(delimiter, i);
122
+ if (end < 0) {
123
+ end = lineLength;
124
+ }
125
+ const str = currentLine.substring(i, end);
126
+ i = end + 1;
127
+ return str;
128
+ }
129
+
130
+ function parseInt() {
131
+ let value = 0;
132
+
133
+ let charCode = currentLine.charCodeAt(i);
134
+ let sign = 1;
135
+
136
+ if (charCode === minusCode) {
137
+ sign = -1;
138
+ i++;
139
+ charCode = currentLine.charCodeAt(i);
140
+ }
141
+
142
+ do {
143
+ if (charCode === delimiterCode) {
144
+ i++;
145
+ break;
146
+ }
147
+ value = value * 10 + charCode - zero;
148
+ charCode = currentLine.charCodeAt(++i);
149
+ } while (i < lineLength);
150
+
151
+ return value * sign;
152
+ }
153
+
154
+ const fieldParsers = fields.map((field) => {
155
+ const { name, type } = field;
156
+
157
+ if (["ubyte", "int", "uint"].includes(type)) {
158
+ return () => {
159
+ currentObject[name] = parseInt();
160
+ };
161
+ } else if (field.isNumeric) {
162
+ return () => {
163
+ currentObject[name] = Number(parseString());
164
+ };
165
+ } else if (["char", "string", "lstring"].includes(type)) {
166
+ return () => {
167
+ currentObject[name] = parseString();
168
+ };
169
+ } else {
170
+ throw new Error("Unsupported type: " + type);
171
+ }
172
+ });
173
+
174
+ /**
175
+ * @param {string} line
176
+ */
177
+ function setLine(line) {
178
+ currentLine = line;
179
+ lineLength = line.length;
180
+ i = 0;
181
+ }
182
+
183
+ /**
184
+ * @param {string} chrom
185
+ * @param {number} chromStart
186
+ * @param {number} chromEnd
187
+ * @param {string} rest
188
+ */
189
+ function parseLine(chrom, chromStart, chromEnd, rest) {
190
+ setLine(rest);
191
+
192
+ currentObject = {
193
+ chrom,
194
+ chromStart,
195
+ chromEnd,
196
+ };
197
+
198
+ for (let j = 0, n = fieldParsers.length; j < n; j++) {
199
+ fieldParsers[j]();
200
+ }
201
+
202
+ return currentObject;
203
+ }
204
+
205
+ return parseLine;
206
+ }
@@ -5,6 +5,7 @@
5
5
  * @prop {function(object):any} f The converter
6
6
  * @prop {number[]} [arrayReference] An optimization for fp64 mainly
7
7
  * @prop {number} [numComponents]
8
+ * @prop {typeof Uint16Array | typeof Int16Array | typeof Uint32Array | typeof Int32Array | typeof Float32Array} [targetArrayType] Defaults to Float32Array
8
9
  */
9
10
  export default class ArrayBuilder {
10
11
  /**
@@ -13,10 +14,10 @@ export default class ArrayBuilder {
13
14
  */
14
15
  constructor(size: number);
15
16
  size: number;
16
- /** @type {Object.<string, {data: number[] | Float32Array, numComponents: number, divisor?: number}>} */
17
+ /** @type {Object.<string, {data: Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array, numComponents: number, divisor?: number}>} */
17
18
  arrays: {
18
19
  [x: string]: {
19
- data: number[] | Float32Array;
20
+ data: Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array;
20
21
  numComponents: number;
21
22
  divisor?: number;
22
23
  };
@@ -26,6 +27,7 @@ export default class ArrayBuilder {
26
27
  /** @type {(function(any):void)[]} */
27
28
  dataUpdaters: ((arg0: any) => void)[];
28
29
  vertexCount: number;
30
+ configure(): void;
29
31
  /**
30
32
  *
31
33
  * @param {string} attribute
@@ -36,13 +38,13 @@ export default class ArrayBuilder {
36
38
  *
37
39
  * @param {string} attributeName
38
40
  * @param {number} numComponents
41
+ * @param {typeof Uint16Array | typeof Int16Array | typeof Uint32Array | typeof Int32Array | typeof Float32Array} [targetArrayType]
39
42
  * @param {number[]} [arrayReference]
40
43
  * @return {function(number|number[])}
41
44
  */
42
- createUpdater(attributeName: string, numComponents: number, arrayReference?: number[]): (arg0: number | number[]) => any;
45
+ createUpdater(attributeName: string, numComponents: number, targetArrayType?: typeof Uint16Array | typeof Int16Array | typeof Uint32Array | typeof Int32Array | typeof Float32Array, arrayReference?: number[]): (arg0: number | number[]) => any;
43
46
  pushAll(): void;
44
47
  /**
45
- *
46
48
  * @param {object} datum
47
49
  */
48
50
  updateFromDatum(datum: object): void;
@@ -51,6 +53,7 @@ export default class ArrayBuilder {
51
53
  * @param {object} datum
52
54
  */
53
55
  pushFromDatum(datum: object): void;
56
+ #private;
54
57
  }
55
58
  /**
56
59
  * A function that extracts a raw attribute from a datum (optionally) converts
@@ -66,5 +69,9 @@ export type ConverterMetadata = {
66
69
  */
67
70
  arrayReference?: number[];
68
71
  numComponents?: number;
72
+ /**
73
+ * Defaults to Float32Array
74
+ */
75
+ targetArrayType?: typeof Uint16Array | typeof Int16Array | typeof Uint32Array | typeof Int32Array | typeof Float32Array;
69
76
  };
70
77
  //# sourceMappingURL=arrayBuilder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"arrayBuilder.d.ts","sourceRoot":"","sources":["../../../src/gl/arrayBuilder.js"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH;IAGI;;;OAGG;IACH,kBAFW,MAAM,EAehB;IAZG,aAAgB;IAEhB,wGAAwG;IACxG;YADmB,MAAM,GAAE;YAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAC;MACnF;IAEhB,kCAAkC;IAClC,SADW,OAAY,IAAI,CAAC,EAAE,CACb;IAEjB,qCAAqC;IACrC,cADW,QAAU,GAAG,KAAE,IAAI,CAAC,EAAE,CACX;IAEtB,oBAAoB;IAGxB;;;;OAIG;IACH,wBAHW,MAAM,YACN,iBAAiB,QAc3B;IAED;;;;;;OAMG;IACH,6BALW,MAAM,iBACN,MAAM,mBACN,MAAM,EAAE,UACE,MAAM,GAAC,MAAM,EAAE,SA8EnC;IAED,gBA0BC;IAED;;;OAGG;IACH,uBAFW,MAAM,QA6BhB;IAED;;;OAGG;IACH,qBAFW,MAAM,QAKhB;CACJ;;;;;;;;;cAvMkB,MAAM,KAAE,GAAG;;;;qBACpB,MAAM,EAAE;oBACR,MAAM"}
1
+ {"version":3,"file":"arrayBuilder.d.ts","sourceRoot":"","sources":["../../../src/gl/arrayBuilder.js"],"names":[],"mappings":"AASA;;;;;;;;GAQG;AACH;IAKI;;;OAGG;IACH,kBAFW,MAAM,EAehB;IAZG,aAAgB;IAEhB,mJAAmJ;IACnJ;YADmB,MAAM,GAAE;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;MAC9H;IAEhB,kCAAkC;IAClC,SADW,OAAY,IAAI,CAAC,EAAE,CACb;IAEjB,qCAAqC;IACrC,cADW,QAAU,GAAG,KAAE,IAAI,CAAC,EAAE,CACX;IAEtB,oBAAoB;IAGxB,kBAOC;IAED;;;;OAIG;IACH,wBAHW,MAAM,YACN,iBAAiB,QAe3B;IAED;;;;;;;OAOG;IACH,6BANW,MAAM,iBACN,MAAM,oBACN,kBAAkB,GAAG,iBAAiB,GAAG,kBAAkB,GAAG,iBAAiB,GAAG,mBAAmB,mBACrG,MAAM,EAAE,UACE,MAAM,GAAC,MAAM,EAAE,SAoFnC;IAED,gBAEC;IAED;;OAEG;IACH,uBAFW,MAAM,QAIhB;IAuDD;;;OAGG;IACH,qBAFW,MAAM,QAKhB;;CACJ;;;;;;;;;cA9NkB,MAAM,KAAE,GAAG;;;;qBACpB,MAAM,EAAE;oBACR,MAAM;;;;sBACN,kBAAkB,GAAG,iBAAiB,GAAG,kBAAkB,GAAG,iBAAiB,GAAG,mBAAmB"}
@@ -14,10 +14,13 @@ const UNROLL_LIMIT = 10000;
14
14
  * @prop {function(object):any} f The converter
15
15
  * @prop {number[]} [arrayReference] An optimization for fp64 mainly
16
16
  * @prop {number} [numComponents]
17
+ * @prop {typeof Uint16Array | typeof Int16Array | typeof Uint32Array | typeof Int32Array | typeof Float32Array} [targetArrayType] Defaults to Float32Array
17
18
  */
18
19
  export default class ArrayBuilder {
19
20
  // TODO: Support strided layout. May yield better performance or not. No consensus in literature.
20
21
 
22
+ #configured = false;
23
+
21
24
  /**
22
25
  *
23
26
  * @param {number} size Size if known, uses TypedArray
@@ -25,7 +28,7 @@ export default class ArrayBuilder {
25
28
  constructor(size) {
26
29
  this.size = size;
27
30
 
28
- /** @type {Object.<string, {data: number[] | Float32Array, numComponents: number, divisor?: number}>} */
31
+ /** @type {Object.<string, {data: Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array, numComponents: number, divisor?: number}>} */
29
32
  this.arrays = {};
30
33
 
31
34
  /** @type {(function():void)[]} */
@@ -37,6 +40,15 @@ export default class ArrayBuilder {
37
40
  this.vertexCount = 0;
38
41
  }
39
42
 
43
+ configure() {
44
+ if (this.#configured) {
45
+ throw new Error("Already configured!");
46
+ }
47
+ this.#configurePushAll();
48
+ this.#configureUpdateFromDatum();
49
+ this.#configured = true;
50
+ }
51
+
40
52
  /**
41
53
  *
42
54
  * @param {string} attribute
@@ -46,6 +58,7 @@ export default class ArrayBuilder {
46
58
  const updater = this.createUpdater(
47
59
  ATTRIBUTE_PREFIX + attribute,
48
60
  metadata.numComponents || 1,
61
+ metadata.targetArrayType ?? Float32Array,
49
62
  metadata.arrayReference
50
63
  );
51
64
  const f = metadata.f;
@@ -60,10 +73,16 @@ export default class ArrayBuilder {
60
73
  *
61
74
  * @param {string} attributeName
62
75
  * @param {number} numComponents
76
+ * @param {typeof Uint16Array | typeof Int16Array | typeof Uint32Array | typeof Int32Array | typeof Float32Array} [targetArrayType]
63
77
  * @param {number[]} [arrayReference]
64
78
  * @return {function(number|number[])}
65
79
  */
66
- createUpdater(attributeName, numComponents, arrayReference) {
80
+ createUpdater(
81
+ attributeName,
82
+ numComponents,
83
+ targetArrayType = Float32Array,
84
+ arrayReference = undefined
85
+ ) {
67
86
  if (!isNumber(this.size)) {
68
87
  throw new Error("The number of vertices must be defined!");
69
88
  }
@@ -73,7 +92,8 @@ export default class ArrayBuilder {
73
92
  let updater;
74
93
  let i = 0;
75
94
 
76
- const array = new Float32Array(this.size * numComponents);
95
+ // eslint-disable-next-line new-cap
96
+ const array = new targetArrayType(this.size * numComponents);
77
97
 
78
98
  this.arrays[attributeName] = {
79
99
  data: array,
@@ -142,6 +162,17 @@ export default class ArrayBuilder {
142
162
  }
143
163
 
144
164
  pushAll() {
165
+ throw new Error("Call configure() first!");
166
+ }
167
+
168
+ /**
169
+ * @param {object} datum
170
+ */
171
+ updateFromDatum(datum) {
172
+ throw new Error("Call configure() first!");
173
+ }
174
+
175
+ #configurePushAll() {
145
176
  if (this.size > UNROLL_LIMIT) {
146
177
  const preps = this.pushers
147
178
  .map((_v, i) => `const p${i} = that.pushers[${i}];`)
@@ -165,15 +196,9 @@ ${pushs}
165
196
  this.vertexCount++;
166
197
  };
167
198
  }
168
-
169
- this.pushAll();
170
199
  }
171
200
 
172
- /**
173
- *
174
- * @param {object} datum
175
- */
176
- updateFromDatum(datum) {
201
+ #configureUpdateFromDatum() {
177
202
  if (this.size > UNROLL_LIMIT) {
178
203
  const preps = this.dataUpdaters
179
204
  .map((_v, i) => `const u${i} = that.dataUpdaters[${i}];`)
@@ -192,14 +217,12 @@ ${updates}
192
217
  };`
193
218
  )(this);
194
219
  } else {
195
- this.updateFromDatum = (datum) => {
220
+ this.updateFromDatum = (/** @type {object} */ datum) => {
196
221
  for (let i = 0; i < this.dataUpdaters.length; i++) {
197
222
  this.dataUpdaters[i](datum);
198
223
  }
199
224
  };
200
225
  }
201
-
202
- this.updateFromDatum(datum);
203
226
  }
204
227
 
205
228
  /**
@@ -63,9 +63,9 @@ export class GeometryBuilder {
63
63
  getIndex: () => import("../utils/binnedIndex.js").Lookup;
64
64
  };
65
65
  toArrays(): {
66
- /** @type {Record<string, {data: number[] | Float32Array, numComponents: number, divisor?: number}>} */
66
+ /** @type {Record<string, {data: Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array, numComponents: number, divisor?: number}>} */
67
67
  arrays: Record<string, {
68
- data: number[] | Float32Array;
68
+ data: Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array;
69
69
  numComponents: number;
70
70
  divisor?: number;
71
71
  }>;
@@ -89,6 +89,7 @@ export class RectVertexBuilder extends GeometryBuilder {
89
89
  attributes: string[];
90
90
  numItems?: number;
91
91
  });
92
+ pushAllSixTimes: any;
92
93
  }
93
94
  export class RuleVertexBuilder extends GeometryBuilder {
94
95
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"dataToVertices.d.ts","sourceRoot":"","sources":["../../../src/gl/dataToVertices.js"],"names":[],"mappings":"AAeA;;;;;GAKG;AACH;IACI;;;OAGG;IAEH;;;;;;OAMG;IACH;;qBAJW,MAAM,EAAE;sBACR,MAAM;OAuEhB;IAnEG,gEAAwB;IAGxB;;MAKC;IAQD,0BAAoC;IAEpC,8BAAoD;IA6CpD,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;;;MAAyB;IAiEjC;QAEQ,uGAAuG;gBAA5F,OAAO,MAAM,EAAE;YAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAC,CAAC;QAEnG,8BAA8B;;QAE9B,8CAA8C;;;MAIrD;CACJ;AAED;IACI;;;;;;OAMG;IACH;;oBAHW,MAAM,EAAE;mBACR,MAAM;OAQhB;CAkCJ;AAED;IACI;;;;;;;;;OASG;IACH;;oBANW,MAAM,EAAE;gCACR,MAAM;uBAEN,MAAM,EAAE;mBACR,MAAM;OAsBhB;IANG,uBAAgC;IAEhC,8BAA8D;IAE9D,6CAA+D;IAC/D,4CAA6D;CA2CpE;AAED;IACI;;;;;;OAMG;IACH;;oBAHW,MAAM,EAAE;mBACR,MAAM;OAQhB;CACJ;AAED;IACI;;;;;OAKG;IACH;;oBAHW,MAAM,EAAE;mBACR,MAAM;OAQhB;CAYJ;AAED;IACI;;;;;;;;;OASG;IACH;;oBANW,MAAM,EAAE;qBACR,OAAO,2BAA2B,EAAE,aAAa;oBACjD,OAAO,MAAM,EAAE,GAAG,CAAC;wBACnB,MAAM;sBACN,OAAO;OA4CjB;IA7BG,4DAA2B;IAC3B,2DAA0B;IAE1B,gCAA4B;IAQ5B,qCAAqC;IACrC,sBADmB,GAAG,KAAK,MAAM,CAMf;IAElB,oDAGC;IACD,qDAGC;IAED,8CAAiE;CA8IxE;;;;;;;;YAzkBS,MAAM;;;;WACN,MAAM;YACN,OAAO,yBAAyB,EAAE,MAAM;;yBAhBzB,mBAAmB"}
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;OA6EhB;IAzEG,gEAAwB;IAGxB;;MAKC;IAQD,0BAAoC;IAEpC,8BAAoD;IAmDpD,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;;;MAAyB;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;;;;;;;;YAvmBS,MAAM;;;;WACN,MAAM;YACN,OAAO,yBAAyB,EAAE,MAAM;;yBAjBzB,mBAAmB"}
@@ -8,10 +8,11 @@ import { isValueDef } from "../encoder/encoder.js";
8
8
  import {
9
9
  dedupeEncodingFields,
10
10
  isHighPrecisionScale,
11
+ isLargeGenome,
11
12
  makeAttributeName,
12
- splitHighPrecision,
13
+ splitLargeHighPrecision,
13
14
  } from "./glslScaleGenerator.js";
14
- import { isContinuous } from "vega-scale";
15
+ import { isContinuous, isDiscrete } from "vega-scale";
15
16
 
16
17
  /**
17
18
  * @typedef {object} RangeEntry Represents a location of a vertex subset
@@ -67,8 +68,9 @@ export class GeometryBuilder {
67
68
 
68
69
  const accessor = ce.accessor;
69
70
 
70
- const doubleArray = [0, 0];
71
71
  const hp = isHighPrecisionScale(ce.scale.type);
72
+ const largeHp = hp && isLargeGenome(ce.scale.domain());
73
+ const largeHpArray = [0, 0];
72
74
 
73
75
  const indexer = ce.indexer;
74
76
 
@@ -81,8 +83,8 @@ export class GeometryBuilder {
81
83
  */
82
84
  const f = indexer
83
85
  ? (d) => indexer(accessor(d))
84
- : hp
85
- ? (d) => splitHighPrecision(accessor(d), doubleArray)
86
+ : largeHp
87
+ ? (d) => splitLargeHighPrecision(accessor(d), largeHpArray)
86
88
  : accessor;
87
89
 
88
90
  const attributeName = sharedChannels
@@ -91,8 +93,13 @@ export class GeometryBuilder {
91
93
 
92
94
  this.variableBuilder.addConverter(attributeName, {
93
95
  f,
94
- numComponents: hp ? 2 : 1,
95
- arrayReference: hp ? doubleArray : undefined,
96
+ numComponents: largeHp ? 2 : 1,
97
+ arrayReference: largeHp ? largeHpArray : undefined,
98
+ targetArrayType: isDiscrete(ce.scale.type)
99
+ ? Uint16Array
100
+ : hp
101
+ ? Uint32Array
102
+ : Float32Array,
96
103
  });
97
104
  }
98
105
 
@@ -226,7 +233,7 @@ export class GeometryBuilder {
226
233
 
227
234
  toArrays() {
228
235
  return {
229
- /** @type {Record<string, {data: number[] | Float32Array, numComponents: number, divisor?: number}>} */
236
+ /** @type {Record<string, {data: Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array, numComponents: number, divisor?: number}>} */
230
237
  arrays: this.variableBuilder.arrays,
231
238
  /** Number of vertices used */
232
239
  vertexCount: this.variableBuilder.vertexCount,
@@ -251,6 +258,29 @@ export class RectVertexBuilder extends GeometryBuilder {
251
258
  attributes,
252
259
  numVertices: numItems * 6,
253
260
  });
261
+ this.variableBuilder.configure();
262
+
263
+ const pushAll = this.variableBuilder.pushAll;
264
+
265
+ this.pushAllSixTimes =
266
+ // TODO: Don't do this stupid comparison. Instead, reuse the previous GeometryBuilder.
267
+ numItems > 500
268
+ ? // Make a new function instance where the JS engine can inline
269
+ // all pushAll calls to avoid the function call overhead.
270
+ new Function(
271
+ "pushAll",
272
+ `return function unrolledPushAllSixTimes() {
273
+ pushAll(); pushAll(); pushAll(); pushAll(); pushAll(); pushAll();
274
+ };`
275
+ )(pushAll)
276
+ : function pushAllSixTimes() {
277
+ pushAll();
278
+ pushAll();
279
+ pushAll();
280
+ pushAll();
281
+ pushAll();
282
+ pushAll();
283
+ };
254
284
  }
255
285
 
256
286
  /**
@@ -273,12 +303,7 @@ export class RectVertexBuilder extends GeometryBuilder {
273
303
 
274
304
  // Six vertices per rect. The vertex shader is using gl_VertexID to
275
305
  // determine the vertex position within the rect.
276
- this.variableBuilder.pushAll();
277
- this.variableBuilder.pushAll();
278
- this.variableBuilder.pushAll();
279
- this.variableBuilder.pushAll();
280
- this.variableBuilder.pushAll();
281
- this.variableBuilder.pushAll();
306
+ this.pushAllSixTimes();
282
307
 
283
308
  this.addToXIndex(d);
284
309
  }
@@ -318,6 +343,8 @@ export class RuleVertexBuilder extends GeometryBuilder {
318
343
 
319
344
  this.updateSide = this.variableBuilder.createUpdater("side", 1);
320
345
  this.updatePos = this.variableBuilder.createUpdater("pos", 1);
346
+
347
+ this.variableBuilder.configure();
321
348
  }
322
349
 
323
350
  /* eslint-disable complexity */
@@ -376,6 +403,7 @@ export class PointVertexBuilder extends GeometryBuilder {
376
403
  attributes,
377
404
  numVertices: numItems,
378
405
  });
406
+ this.variableBuilder.configure();
379
407
  }
380
408
  }
381
409
 
@@ -392,6 +420,7 @@ export class LinkVertexBuilder extends GeometryBuilder {
392
420
  attributes,
393
421
  numVertices: numItems,
394
422
  });
423
+ this.variableBuilder.configure();
395
424
  }
396
425
 
397
426
  toArrays() {
@@ -459,6 +488,8 @@ export class TextVertexBuilder extends GeometryBuilder {
459
488
  );
460
489
 
461
490
  this.updateWidth = this.variableBuilder.createUpdater("width", 1);
491
+
492
+ this.variableBuilder.configure();
462
493
  }
463
494
 
464
495
  /**
@@ -18,15 +18,27 @@ export function generateScaleGlsl(channel: Channel, scale: any, channelDef: impo
18
18
  domainUniform: string;
19
19
  };
20
20
  /**
21
+ * True if scale needs more than 24 bits (float32) of precision.
21
22
  *
22
23
  * @param {string} type
23
24
  */
24
25
  export function isHighPrecisionScale(type: string): boolean;
26
+ /**
27
+ * True if Uint32 cannot represent the domain.
28
+ *
29
+ * @param {number[]} domain
30
+ */
31
+ export function isLargeGenome(domain: number[]): boolean;
25
32
  /**
26
33
  * @param {number} x Must be an integer
27
34
  * @param {number[]} [arr]
28
35
  */
29
36
  export function splitHighPrecision(x: number, arr?: number[]): number[];
37
+ /**
38
+ * @param {number} x Must be an integer
39
+ * @param {number[]} [arr]
40
+ */
41
+ export function splitLargeHighPrecision(x: number, arr?: number[]): number[];
30
42
  /**
31
43
  * @param {number[]} domain
32
44
  */
@@ -1 +1 @@
1
- {"version":3,"file":"glslScaleGenerator.d.ts","sourceRoot":"","sources":["../../../src/gl/glslScaleGenerator.js"],"names":[],"mappings":";AAmDA;;;;GAIG;AACH,2CAHW,OAAO,SACP,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,OAAO,UAsC9C;AAED;;;;;;GAMG;AAEH,2CANW,OAAO,SACP,GAAG,cACH,OAAO,oBAAoB,EAAE,UAAU,+BACvC,OAAO,EAAE;;;;EAmRnB;AA+FD;;;GAGG;AACH,2CAFW,MAAM,WAIhB;AAOD;;;GAGG;AACH,sCAHW,MAAM,QACN,MAAM,EAAE,YAYlB;AAYD;;GAEG;AACH,qDAFW,MAAM,EAAE,YAIlB;AAED;;GAEG;AAEH;;;;;GAKG;AACH,+CAFW,QAAQ,OAAO,OAAO,oBAAoB,EAAE,OAAO,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC,CAAC,+DA4BtG;AAED;;GAEG;AACH,2CAFW,OAAO,oBAAoB,EAAE,OAAO,GAAG,OAAO,oBAAoB,EAAE,OAAO,EAAE,UAIvF;AAzhBD,uCAAwC;AACxC,uCAAwC;AACxC,oCAAqC;AACrC,6CAA8C;AAC9C,kDAAmD;AACnD,oDAAqD;sBAMxC,OAAO,oBAAoB,EAAE,OAAO;;;;8BAsXpC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;;;uBA8GhD,CAAC,MAAM,EAAE,OAAO,CAAC;0BAjfJ,WAAW"}
1
+ {"version":3,"file":"glslScaleGenerator.d.ts","sourceRoot":"","sources":["../../../src/gl/glslScaleGenerator.js"],"names":[],"mappings":";AAmDA;;;;GAIG;AACH,2CAHW,OAAO,SACP,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,OAAO,UAsC9C;AAED;;;;;;GAMG;AAEH,2CANW,OAAO,SACP,GAAG,cACH,OAAO,oBAAoB,EAAE,UAAU,+BACvC,OAAO,EAAE;;;;EA8RnB;AA+FD;;;;GAIG;AACH,2CAFW,MAAM,WAIhB;AAED;;;;GAIG;AACH,sCAFW,MAAM,EAAE,WAIlB;AAMD;;;GAGG;AACH,sCAHW,MAAM,QACN,MAAM,EAAE,YAYlB;AAED;;;GAGG;AACH,2CAHW,MAAM,QACN,MAAM,EAAE,YAUlB;AAYD;;GAEG;AACH,qDAFW,MAAM,EAAE,YAIlB;AAED;;GAEG;AAEH;;;;;GAKG;AACH,+CAFW,QAAQ,OAAO,OAAO,oBAAoB,EAAE,OAAO,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC,CAAC,+DA4BtG;AAED;;GAEG;AACH,2CAFW,OAAO,oBAAoB,EAAE,OAAO,GAAG,OAAO,oBAAoB,EAAE,OAAO,EAAE,UAIvF;AA3jBD,uCAAwC;AACxC,uCAAwC;AACxC,oCAAqC;AACrC,6CAA8C;AAC9C,kDAAmD;AACnD,oDAAqD;sBAMxC,OAAO,oBAAoB,EAAE,OAAO;;;;8BAiYpC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;;;uBAqIhD,CAAC,MAAM,EAAE,OAAO,CAAC;0BAnhBJ,WAAW"}
@@ -122,8 +122,19 @@ export function generateScaleGlsl(
122
122
  const domainUniformName = DOMAIN_PREFIX + primary;
123
123
  const rangeName = RANGE_PREFIX + primary;
124
124
 
125
+ // The attribute has discrete values
126
+ const discrete = isDiscrete(scale.type);
127
+
125
128
  const hp = isHighPrecisionScale(scale.type);
126
- const attributeType = hp ? "vec2" : "float";
129
+ const largeHp = hp && isLargeGenome(scale.domain());
130
+
131
+ const attributeType = largeHp
132
+ ? "uvec2"
133
+ : hp
134
+ ? "uint"
135
+ : discrete
136
+ ? "uint"
137
+ : "float";
127
138
 
128
139
  const domainLength = scale.domain ? scale.domain().length : undefined;
129
140
 
@@ -468,6 +479,7 @@ function makeFunctionCall(name, ...args) {
468
479
  }
469
480
 
470
481
  /**
482
+ * True if scale needs more than 24 bits (float32) of precision.
471
483
  *
472
484
  * @param {string} type
473
485
  */
@@ -475,9 +487,17 @@ export function isHighPrecisionScale(type) {
475
487
  return type == "index" || type == "locus";
476
488
  }
477
489
 
478
- // Maximum precise index number is 2^(23 + 11) ~ 17G
479
- // Higher number increases precision but makes zooming unstable
480
- const BS = 2 ** 11;
490
+ /**
491
+ * True if Uint32 cannot represent the domain.
492
+ *
493
+ * @param {number[]} domain
494
+ */
495
+ export function isLargeGenome(domain) {
496
+ return domain[1] > 2 ** 32;
497
+ }
498
+
499
+ const LOW_BITS = 12;
500
+ const BS = 2 ** LOW_BITS;
481
501
  const BM = BS - 1;
482
502
 
483
503
  /**
@@ -496,6 +516,20 @@ export function splitHighPrecision(x, arr = []) {
496
516
  return arr;
497
517
  }
498
518
 
519
+ /**
520
+ * @param {number} x Must be an integer
521
+ * @param {number[]} [arr]
522
+ */
523
+ export function splitLargeHighPrecision(x, arr = []) {
524
+ const lo = x % BS;
525
+ const hi = (x - lo) / BS;
526
+
527
+ arr[0] = hi;
528
+ arr[1] = lo;
529
+
530
+ return arr;
531
+ }
532
+
499
533
  /**
500
534
  * @param {number} x
501
535
  */
@@ -1,2 +1,2 @@
1
- const shader = "uniform highp float uZero;vec3 getDiscreteColor(sampler2D s,int index){return texelFetch(s,ivec2(index % textureSize(s,0).x,0),0).rgb;}vec3 getInterpolatedColor(sampler2D s,float unitValue){return texture(s,vec2(unitValue,0.0)).rgb;}float clampToRange(float value,vec2 range){return clamp(value,min(range[0],range[1]),max(range[0],range[1]));}float scaleIdentity(float value){return value;}float scaleLinear(float value,vec2 domain,vec2 range){float domainSpan=domain[1]-domain[0];float rangeSpan=range[1]-range[0];return(value-domain[0])/domainSpan*rangeSpan+range[0];}float scaleLog(float value,vec2 domain,vec2 range,float base){return scaleLinear(log(value)/log(base),log(domain)/log(base),range);}float symlog(float value,float constant){return sign(value)*log(abs(value/constant)+1.0);}float scaleSymlog(float value,vec2 domain,vec2 range,float constant){return scaleLinear(symlog(value,constant),vec2(symlog(domain[0],constant),symlog(domain[1],constant)),range);}float scalePow(float value,vec2 domain,vec2 range,float exponent){return scaleLinear(pow(abs(value),exponent)*sign(value),pow(abs(domain),vec2(exponent))*sign(domain),range);}float scaleBand(float value,vec2 domainExtent,vec2 range,float paddingInner,float paddingOuter,float align,float band){float start=range[0];float stop=range[1];float rangeSpan=stop-start;float n=domainExtent[1]-domainExtent[0];paddingInner=int(n)>1 ? paddingInner : 0.0;float step=rangeSpan/max(1.0,n-paddingInner+paddingOuter*2.0);start+=(rangeSpan-step*(n-paddingInner))*align;float bandwidth=step*(1.0-paddingInner);return start+(value-domainExtent[0])*step+bandwidth*band;}float scaleBandHp(vec2 value,vec3 domainExtent,vec2 range,float paddingInner,float paddingOuter,float align,float band){float start=range[0];float stop=range[1];float rangeSpan=stop-start;vec2 domainStart=domainExtent.xy;float n=domainExtent[2];float step=rangeSpan/max(1.0,n-paddingInner+paddingOuter*2.0);start+=(rangeSpan-step*(n-paddingInner))*align;float bandwidth=step*(1.0-paddingInner);float inf=1.0/uZero;float hi=max(value[0]-domainStart[0],-inf);float lo=max(value[1]-domainStart[1],-inf);return dot(vec4(start,hi,lo,bandwidth),vec4(1.0,step,step,band));}";
1
+ const shader = "uniform highp float uZero;vec3 getDiscreteColor(sampler2D s,int index){return texelFetch(s,ivec2(index % textureSize(s,0).x,0),0).rgb;}vec3 getInterpolatedColor(sampler2D s,float unitValue){return texture(s,vec2(unitValue,0.0)).rgb;}float clampToRange(float value,vec2 range){return clamp(value,min(range[0],range[1]),max(range[0],range[1]));}float scaleIdentity(float value){return value;}float scaleIdentity(uint value){return float(value);}float scaleLinear(float value,vec2 domain,vec2 range){float domainSpan=domain[1]-domain[0];float rangeSpan=range[1]-range[0];return(value-domain[0])/domainSpan*rangeSpan+range[0];}float scaleLog(float value,vec2 domain,vec2 range,float base){return scaleLinear(log(value)/log(base),log(domain)/log(base),range);}float symlog(float value,float constant){return sign(value)*log(abs(value/constant)+1.0);}float scaleSymlog(float value,vec2 domain,vec2 range,float constant){return scaleLinear(symlog(value,constant),vec2(symlog(domain[0],constant),symlog(domain[1],constant)),range);}float scalePow(float value,vec2 domain,vec2 range,float exponent){return scaleLinear(pow(abs(value),exponent)*sign(value),pow(abs(domain),vec2(exponent))*sign(domain),range);}float scaleBand(uint value,vec2 domainExtent,vec2 range,float paddingInner,float paddingOuter,float align,float band){float start=range[0];float stop=range[1];float rangeSpan=stop-start;float n=domainExtent[1]-domainExtent[0];paddingInner=int(n)>1 ? paddingInner : 0.0;float step=rangeSpan/max(1.0,n-paddingInner+paddingOuter*2.0);start+=(rangeSpan-step*(n-paddingInner))*align;float bandwidth=step*(1.0-paddingInner);return start+(float(value)-domainExtent[0])*step+bandwidth*band;}const int lowBits=12;const float lowDivisor=pow(2.0,float(lowBits));const uint lowMask=uint(lowDivisor-1.0);vec2 splitUint(uint value){uint valueLo=value&lowMask;uint valueHi=value-valueLo;return vec2(float(valueHi),float(valueLo));}/***High precision variant of scaleBand for index/locus scales*/float scaleBandHp(uint value,vec3 domainExtent,vec2 range,float paddingInner,float paddingOuter,float align,float band){float start=range[0];float stop=range[1];float rangeSpan=stop-start;vec2 domainStart=domainExtent.xy;float n=domainExtent[2];float step=rangeSpan/max(1.0,n-paddingInner+paddingOuter*2.0);start+=(rangeSpan-step*(n-paddingInner))*align;float bandwidth=step*(1.0-paddingInner);vec2 splitValue=splitUint(value);float inf=1.0/uZero;float hi=max(splitValue[0]-domainStart[0],-inf);float lo=max(splitValue[1]-domainStart[1],-inf);return dot(vec4(start,hi,lo,bandwidth),vec4(1.0,step,step,band));}/***High precision variant of scaleBand for index/locus scales for large*domains where 32bit uints are not sufficient to represent the domain.*/float scaleBandHp(uvec2 value,vec3 domainExtent,vec2 range,float paddingInner,float paddingOuter,float align,float band){float start=range[0];float stop=range[1];float rangeSpan=stop-start;vec2 domainStart=domainExtent.xy;float n=domainExtent[2];float step=rangeSpan/max(1.0,n-paddingInner+paddingOuter*2.0);start+=(rangeSpan-step*(n-paddingInner))*align;float bandwidth=step*(1.0-paddingInner);vec2 splitValue=vec2(float(value[0])*lowDivisor,float(value[1]));float inf=1.0/uZero;float hi=max(splitValue[0]-domainStart[0],-inf);float lo=max(splitValue[1]-domainStart[1],-inf);return dot(vec4(start,hi,lo,bandwidth),vec4(1.0,step,step,band));}";
2
2
  export default shader;
@@ -1 +1 @@
1
- {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/marks/link.js"],"names":[],"mappings":"AAWA;IA+BQ;;;;;OAKG;IACH,yBAEC;IAkGD;;;;;;MAKC;CAoFR;iBAxOgB,WAAW"}
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/marks/link.js"],"names":[],"mappings":"AAWA;IA+BQ;;;;;OAKG;IACH,yBAEC;IAmGD;;;;;;MAKC;CAoFR;iBAzOgB,WAAW"}