@vitessce/neuroglancer 3.9.7 → 3.9.9

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.
@@ -12,6 +12,16 @@ import { PALETTE, getDefaultColor } from '@vitessce/utils';
12
12
  function normalizeColor(rgbColor) {
13
13
  return rgbColor.map(c => c / 255);
14
14
  }
15
+ /**
16
+ * GLSL call to set point marker border width.
17
+ * Set to 0.0 to remove the outline.
18
+ * @param {number} borderWidth
19
+ * @returns {string}
20
+ */
21
+ function borderWidthGlsl(borderWidth = 0.0) {
22
+ // must be decimal/float value
23
+ return `setPointMarkerBorderWidth(${borderWidth.toFixed(1)});`;
24
+ }
15
25
  /**
16
26
  * Format a normalized color as a GLSL vec3 literal.
17
27
  * @param {[number, number, number]} normalizedColor
@@ -39,12 +49,13 @@ function toVec4(normalizedColor, alpha) {
39
49
  * @param {number} opacity Opacity (0-1).
40
50
  * @returns {string} A GLSL shader string.
41
51
  */
42
- export function getSpatialLayerColorShader(staticColor, opacity) {
52
+ export function getSpatialLayerColorShader(staticColor, opacity, borderWidth = 0.0) {
43
53
  const norm = normalizeColor(staticColor);
44
54
  // lang: glsl
45
55
  return `
46
56
  void main() {
47
57
  setColor(${toVec4(norm, opacity)});
58
+ ${borderWidthGlsl(borderWidth)}
48
59
  }
49
60
  `;
50
61
  }
@@ -58,7 +69,7 @@ export function getSpatialLayerColorShader(staticColor, opacity) {
58
69
  * @param {string} featureIndexProp The property name for the feature index in the shader.
59
70
  * @returns {string} A GLSL shader string.
60
71
  */
61
- export function getSpatialLayerColorWithSelectionShader(staticColor, opacity, featureIndices, defaultColor, featureIndexProp) {
72
+ export function getSpatialLayerColorWithSelectionShader(staticColor, opacity, featureIndices, defaultColor, featureIndexProp, borderWidth = 0.0) {
62
73
  const normStatic = normalizeColor(staticColor);
63
74
  const normDefault = normalizeColor(defaultColor);
64
75
  const numFeatures = featureIndices.length;
@@ -76,8 +87,10 @@ export function getSpatialLayerColorWithSelectionShader(staticColor, opacity, fe
76
87
  }
77
88
  if (isSelected) {
78
89
  setColor(${toVec4(normStatic, opacity)});
90
+ ${borderWidthGlsl(borderWidth)}
79
91
  } else {
80
92
  setColor(${toVec4(normDefault, opacity)});
93
+ ${borderWidthGlsl(borderWidth)}
81
94
  }
82
95
  }
83
96
  `;
@@ -91,7 +104,7 @@ export function getSpatialLayerColorWithSelectionShader(staticColor, opacity, fe
91
104
  * @param {string} featureIndexProp The property name for the feature index in the shader.
92
105
  * @returns {string} A GLSL shader string.
93
106
  */
94
- export function getSpatialLayerColorFilteredShader(staticColor, opacity, featureIndices, featureIndexProp) {
107
+ export function getSpatialLayerColorFilteredShader(staticColor, opacity, featureIndices, featureIndexProp, borderWidth = 0.0) {
95
108
  const normStatic = normalizeColor(staticColor);
96
109
  const numFeatures = featureIndices.length;
97
110
  const indicesArr = `int selectedIndices[${numFeatures}] = int[${numFeatures}](${featureIndices.join(', ')});`;
@@ -110,6 +123,7 @@ export function getSpatialLayerColorFilteredShader(staticColor, opacity, feature
110
123
  discard;
111
124
  }
112
125
  setColor(${toVec4(normStatic, opacity)});
126
+ ${borderWidthGlsl(borderWidth)}
113
127
  }
114
128
  `;
115
129
  }
@@ -124,12 +138,13 @@ export function getSpatialLayerColorFilteredShader(staticColor, opacity, feature
124
138
  * @param {number} opacity Opacity (0-1).
125
139
  * @returns {string} A GLSL shader string.
126
140
  */
127
- export function getGeneSelectionNoSelectionShader(staticColor, opacity) {
141
+ export function getGeneSelectionNoSelectionShader(staticColor, opacity, borderWidth = 0.0) {
128
142
  const norm = normalizeColor(staticColor);
129
143
  // lang: glsl
130
144
  return `
131
145
  void main() {
132
146
  setColor(${toVec4(norm, opacity)});
147
+ ${borderWidthGlsl(borderWidth)}
133
148
  }
134
149
  `;
135
150
  }
@@ -148,7 +163,7 @@ export function getGeneSelectionNoSelectionShader(staticColor, opacity) {
148
163
  * @param {string} featureIndexProp The property name for the feature index in the shader.
149
164
  * @returns {string} A GLSL shader string.
150
165
  */
151
- export function getGeneSelectionWithSelectionShader(featureIndices, featureColors, staticColor, defaultColor, opacity, featureIndexProp) {
166
+ export function getGeneSelectionWithSelectionShader(featureIndices, featureColors, staticColor, defaultColor, opacity, featureIndexProp, borderWidth = 0.0) {
152
167
  const numFeatures = featureIndices.length;
153
168
  const normDefault = normalizeColor(defaultColor);
154
169
  const normColors = featureColors.map(c => normalizeColor(c));
@@ -169,6 +184,7 @@ export function getGeneSelectionWithSelectionShader(featureIndices, featureColor
169
184
  }
170
185
  }
171
186
  setColor(color);
187
+ ${borderWidthGlsl(borderWidth)}
172
188
  }
173
189
  `;
174
190
  }
@@ -183,7 +199,7 @@ export function getGeneSelectionWithSelectionShader(featureIndices, featureColor
183
199
  * @param {string} featureIndexProp The property name for the feature index in the shader.
184
200
  * @returns {string} A GLSL shader string.
185
201
  */
186
- export function getGeneSelectionFilteredShader(featureIndices, featureColors, staticColor, opacity, featureIndexProp) {
202
+ export function getGeneSelectionFilteredShader(featureIndices, featureColors, staticColor, opacity, featureIndexProp, borderWidth = 0.0) {
187
203
  const numFeatures = featureIndices.length;
188
204
  const normColors = featureColors.map(c => normalizeColor(c));
189
205
  const normStatic = normalizeColor(staticColor);
@@ -208,6 +224,7 @@ export function getGeneSelectionFilteredShader(featureIndices, featureColors, st
208
224
  discard;
209
225
  }
210
226
  setColor(vec4(matchedColor, ${opacity}));
227
+ ${borderWidthGlsl(borderWidth)}
211
228
  }
212
229
  `;
213
230
  }
@@ -221,7 +238,7 @@ export function getGeneSelectionFilteredShader(featureIndices, featureColors, st
221
238
  * @param {string} featureIndexProp The property name for the feature index in the shader.
222
239
  * @returns {string} A GLSL shader string.
223
240
  */
224
- export function getRandomByFeatureShader(opacity, featureIndexProp) {
241
+ export function getRandomByFeatureShader(opacity, featureIndexProp, borderWidth = 0.0) {
225
242
  const paletteSize = PALETTE.length;
226
243
  const normPalette = PALETTE.map(c => normalizeColor(c));
227
244
  const paletteDecl = `vec3 palette[${paletteSize}] = vec3[${paletteSize}](${normPalette.map(c => toVec3(c)).join(', ')});`;
@@ -234,6 +251,7 @@ export function getRandomByFeatureShader(opacity, featureIndexProp) {
234
251
  if (colorIdx < 0) { colorIdx = -colorIdx; }
235
252
  vec3 color = palette[colorIdx];
236
253
  setColor(vec4(color, ${opacity}));
254
+ ${borderWidthGlsl(borderWidth)}
237
255
  }
238
256
  `;
239
257
  }
@@ -247,7 +265,7 @@ export function getRandomByFeatureShader(opacity, featureIndexProp) {
247
265
  * @param {string} featureIndexProp The property name for the feature index in the shader.
248
266
  * @returns {string} A GLSL shader string.
249
267
  */
250
- export function getRandomByFeatureWithSelectionShader(featureIndices, defaultColor, opacity, featureIndexProp) {
268
+ export function getRandomByFeatureWithSelectionShader(featureIndices, defaultColor, opacity, featureIndexProp, borderWidth = 0.0) {
251
269
  const paletteSize = PALETTE.length;
252
270
  const normPalette = PALETTE.map(c => normalizeColor(c));
253
271
  const normDefault = normalizeColor(defaultColor);
@@ -270,8 +288,10 @@ export function getRandomByFeatureWithSelectionShader(featureIndices, defaultCol
270
288
  int colorIdx = geneIndex - (geneIndex / ${paletteSize}) * ${paletteSize};
271
289
  if (colorIdx < 0) { colorIdx = -colorIdx; }
272
290
  setColor(vec4(palette[colorIdx], ${opacity}));
291
+ ${borderWidthGlsl(borderWidth)}
273
292
  } else {
274
293
  setColor(${toVec4(normDefault, opacity)});
294
+ ${borderWidthGlsl(borderWidth)}
275
295
  }
276
296
  }
277
297
  `;
@@ -284,7 +304,7 @@ export function getRandomByFeatureWithSelectionShader(featureIndices, defaultCol
284
304
  * @param {string} featureIndexProp The property name for the feature index in the shader.
285
305
  * @returns {string} A GLSL shader string.
286
306
  */
287
- export function getRandomByFeatureFilteredShader(featureIndices, opacity, featureIndexProp) {
307
+ export function getRandomByFeatureFilteredShader(featureIndices, opacity, featureIndexProp, borderWidth = 0.0) {
288
308
  const paletteSize = PALETTE.length;
289
309
  const normPalette = PALETTE.map(c => normalizeColor(c));
290
310
  const numFeatures = featureIndices.length;
@@ -308,6 +328,7 @@ export function getRandomByFeatureFilteredShader(featureIndices, opacity, featur
308
328
  int colorIdx = geneIndex - (geneIndex / ${paletteSize}) * ${paletteSize};
309
329
  if (colorIdx < 0) { colorIdx = -colorIdx; }
310
330
  setColor(vec4(palette[colorIdx], ${opacity}));
331
+ ${borderWidthGlsl(borderWidth)}
311
332
  }
312
333
  `;
313
334
  }
@@ -338,7 +359,7 @@ function hashToFloatGlsl() {
338
359
  * @param {string} pointIndexProp The property name for the point index in the shader.
339
360
  * @returns {string} A GLSL shader string.
340
361
  */
341
- export function getRandomPerPointShader(opacity, featureIndexProp, pointIndexProp) {
362
+ export function getRandomPerPointShader(opacity, featureIndexProp, pointIndexProp, borderWidth = 0.0) {
342
363
  // lang: glsl
343
364
  return `
344
365
  ${hashToFloatGlsl()}
@@ -349,6 +370,7 @@ export function getRandomPerPointShader(opacity, featureIndexProp, pointIndexPro
349
370
  float g = hashToFloat(pointIndex, 1);
350
371
  float b = hashToFloat(pointIndex, 2);
351
372
  setColor(vec4(r, g, b, ${opacity}));
373
+ ${borderWidthGlsl(borderWidth)}
352
374
  }
353
375
  `;
354
376
  }
@@ -362,7 +384,7 @@ export function getRandomPerPointShader(opacity, featureIndexProp, pointIndexPro
362
384
  * @param {string} pointIndexProp The property name for the point index in the shader.
363
385
  * @returns {string} A GLSL shader string.
364
386
  */
365
- export function getRandomPerPointWithSelectionShader(featureIndices, defaultColor, opacity, featureIndexProp, pointIndexProp) {
387
+ export function getRandomPerPointWithSelectionShader(featureIndices, defaultColor, opacity, featureIndexProp, pointIndexProp, borderWidth = 0.0) {
366
388
  const normDefault = normalizeColor(defaultColor);
367
389
  const numFeatures = featureIndices.length;
368
390
  const indicesDecl = `int selectedIndices[${numFeatures}] = int[${numFeatures}](${featureIndices.join(', ')});`;
@@ -384,8 +406,10 @@ export function getRandomPerPointWithSelectionShader(featureIndices, defaultColo
384
406
  float g = hashToFloat(pointIndex, 1);
385
407
  float b = hashToFloat(pointIndex, 2);
386
408
  setColor(vec4(r, g, b, ${opacity}));
409
+ ${borderWidthGlsl(borderWidth)}
387
410
  } else {
388
411
  setColor(${toVec4(normDefault, opacity)});
412
+ ${borderWidthGlsl(borderWidth)}
389
413
  }
390
414
  }
391
415
  `;
@@ -399,7 +423,7 @@ export function getRandomPerPointWithSelectionShader(featureIndices, defaultColo
399
423
  * @param {string} pointIndexProp The property name for the point index in the shader.
400
424
  * @returns {string} A GLSL shader string.
401
425
  */
402
- export function getRandomPerPointFilteredShader(featureIndices, opacity, featureIndexProp, pointIndexProp) {
426
+ export function getRandomPerPointFilteredShader(featureIndices, opacity, featureIndexProp, pointIndexProp, borderWidth = 0.0) {
403
427
  const numFeatures = featureIndices.length;
404
428
  const indicesDecl = `int selectedIndices[${numFeatures}] = int[${numFeatures}](${featureIndices.join(', ')});`;
405
429
  // lang: glsl
@@ -422,11 +446,12 @@ export function getRandomPerPointFilteredShader(featureIndices, opacity, feature
422
446
  float g = hashToFloat(pointIndex, 1);
423
447
  float b = hashToFloat(pointIndex, 2);
424
448
  setColor(vec4(r, g, b, ${opacity}));
449
+ ${borderWidthGlsl(borderWidth)}
425
450
  }
426
451
  `;
427
452
  }
428
453
  export function getPointsShader(layerCoordination) {
429
- const { theme, featureIndex, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, featureIndexProp, pointIndexProp, } = layerCoordination;
454
+ const { theme, featureIndex, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, pointMarkerBorderWidth = 0.0, featureIndexProp, pointIndexProp, } = layerCoordination;
430
455
  const defaultColor = getDefaultColor(theme);
431
456
  const opacity = spatialLayerOpacity ?? 1.0;
432
457
  const staticColor = (Array.isArray(spatialLayerColor) && spatialLayerColor.length === 3
@@ -496,12 +521,12 @@ export function getPointsShader(layerCoordination) {
496
521
  // ---- spatialLayerColor ----
497
522
  if (obsColorEncoding === 'spatialLayerColor') {
498
523
  if (!hasFeatureSelection || !hasResolvedIndices) {
499
- return getSpatialLayerColorShader(staticColor, opacity);
524
+ return getSpatialLayerColorShader(staticColor, opacity, pointMarkerBorderWidth);
500
525
  }
501
526
  if (isFiltered) {
502
- return getSpatialLayerColorFilteredShader(staticColor, opacity, featureIndices, featureIndexProp);
527
+ return getSpatialLayerColorFilteredShader(staticColor, opacity, featureIndices, featureIndexProp, pointMarkerBorderWidth);
503
528
  }
504
- return getSpatialLayerColorWithSelectionShader(staticColor, opacity, featureIndices, defaultColor, featureIndexProp);
529
+ return getSpatialLayerColorWithSelectionShader(staticColor, opacity, featureIndices, defaultColor, featureIndexProp, pointMarkerBorderWidth);
505
530
  }
506
531
  // ---- geneSelection ----
507
532
  if (obsColorEncoding === 'geneSelection') {
@@ -509,12 +534,12 @@ export function getPointsShader(layerCoordination) {
509
534
  throw new Error('In order to use gene-based color encoding for Neuroglancer Points, options.featureIndexProp must be specified for the obsPoints.ng-annotations fileType in the Vitessce configuration.');
510
535
  }
511
536
  if (!hasFeatureSelection || !hasResolvedIndices) {
512
- return getGeneSelectionNoSelectionShader(staticColor, opacity);
537
+ return getGeneSelectionNoSelectionShader(staticColor, opacity, pointMarkerBorderWidth);
513
538
  }
514
539
  if (isFiltered) {
515
- return getGeneSelectionFilteredShader(featureIndices, resolvedFeatureColors, staticColor, opacity, featureIndexProp);
540
+ return getGeneSelectionFilteredShader(featureIndices, resolvedFeatureColors, staticColor, opacity, featureIndexProp, pointMarkerBorderWidth);
516
541
  }
517
- return getGeneSelectionWithSelectionShader(featureIndices, resolvedFeatureColors, staticColor, defaultColor, opacity, featureIndexProp);
542
+ return getGeneSelectionWithSelectionShader(featureIndices, resolvedFeatureColors, staticColor, defaultColor, opacity, featureIndexProp, pointMarkerBorderWidth);
518
543
  }
519
544
  // ---- randomByFeature ----
520
545
  if (obsColorEncoding === 'randomByFeature') {
@@ -522,12 +547,12 @@ export function getPointsShader(layerCoordination) {
522
547
  throw new Error('In order to use gene-based color encoding for Neuroglancer Points, options.featureIndexProp must be specified for the obsPoints.ng-annotations fileType in the Vitessce configuration.');
523
548
  }
524
549
  if (!hasFeatureSelection || !hasResolvedIndices) {
525
- return getRandomByFeatureShader(opacity, featureIndexProp);
550
+ return getRandomByFeatureShader(opacity, featureIndexProp, pointMarkerBorderWidth);
526
551
  }
527
552
  if (isFiltered) {
528
- return getRandomByFeatureFilteredShader(featureIndices, opacity, featureIndexProp);
553
+ return getRandomByFeatureFilteredShader(featureIndices, opacity, featureIndexProp, pointMarkerBorderWidth);
529
554
  }
530
- return getRandomByFeatureWithSelectionShader(featureIndices, defaultColor, opacity, featureIndexProp);
555
+ return getRandomByFeatureWithSelectionShader(featureIndices, defaultColor, opacity, featureIndexProp, pointMarkerBorderWidth);
531
556
  }
532
557
  // ---- random (per point) ----
533
558
  if (obsColorEncoding === 'random') {
@@ -535,13 +560,13 @@ export function getPointsShader(layerCoordination) {
535
560
  throw new Error('In order to use per-point color encoding for Neuroglancer Points, options.pointIndexProp must be specified for the obsPoints.ng-annotations fileType in the Vitessce configuration.');
536
561
  }
537
562
  if (!hasFeatureSelection || !hasResolvedIndices) {
538
- return getRandomPerPointShader(opacity, featureIndexProp, pointIndexProp);
563
+ return getRandomPerPointShader(opacity, featureIndexProp, pointIndexProp, pointMarkerBorderWidth);
539
564
  }
540
565
  if (isFiltered) {
541
- return getRandomPerPointFilteredShader(featureIndices, opacity, featureIndexProp, pointIndexProp);
566
+ return getRandomPerPointFilteredShader(featureIndices, opacity, featureIndexProp, pointIndexProp, pointMarkerBorderWidth);
542
567
  }
543
- return getRandomPerPointWithSelectionShader(featureIndices, defaultColor, opacity, featureIndexProp, pointIndexProp);
568
+ return getRandomPerPointWithSelectionShader(featureIndices, defaultColor, opacity, featureIndexProp, pointIndexProp, pointMarkerBorderWidth);
544
569
  }
545
570
  // Fallback: static color.
546
- return getSpatialLayerColorShader(staticColor, opacity);
571
+ return getSpatialLayerColorShader(staticColor, opacity, pointMarkerBorderWidth);
547
572
  }
@@ -29,6 +29,7 @@ describe('getSpatialLayerColorShader', () => {
29
29
  const expected = `
30
30
  void main() {
31
31
  setColor(vec4(1, 0.5019607843137255, 0, 0.8));
32
+ setPointMarkerBorderWidth(0.0);
32
33
  }
33
34
  `;
34
35
  expectShaderEqual(result, expected);
@@ -38,6 +39,7 @@ void main() {
38
39
  const expected = `
39
40
  void main() {
40
41
  setColor(vec4(0, 0, 0, 0));
42
+ setPointMarkerBorderWidth(0.0);
41
43
  }
42
44
  `;
43
45
  expectShaderEqual(result, expected);
@@ -47,6 +49,7 @@ void main() {
47
49
  const expected = `
48
50
  void main() {
49
51
  setColor(vec4(1, 1, 1, 1));
52
+ setPointMarkerBorderWidth(0.0);
50
53
  }
51
54
  `;
52
55
  expectShaderEqual(result, expected);
@@ -67,8 +70,10 @@ void main() {
67
70
  }
68
71
  if (isSelected) {
69
72
  setColor(vec4(1, 0, 0, 0.5));
73
+ setPointMarkerBorderWidth(0.0);
70
74
  } else {
71
75
  setColor(vec4(0.5019607843137255, 0.5019607843137255, 0.5019607843137255, 0.5));
76
+ setPointMarkerBorderWidth(0.0);
72
77
  }
73
78
  }
74
79
  `;
@@ -88,8 +93,10 @@ void main() {
88
93
  }
89
94
  if (isSelected) {
90
95
  setColor(vec4(0, 1, 0, 1));
96
+ setPointMarkerBorderWidth(0.0);
91
97
  } else {
92
98
  setColor(vec4(0, 0, 0, 1));
99
+ setPointMarkerBorderWidth(0.0);
93
100
  }
94
101
  }
95
102
  `;
@@ -113,6 +120,7 @@ void main() {
113
120
  discard;
114
121
  }
115
122
  setColor(vec4(0, 0, 1, 0.9));
123
+ setPointMarkerBorderWidth(0.0);
116
124
  }
117
125
  `;
118
126
  expectShaderEqual(result, expected);
@@ -127,6 +135,7 @@ describe('getGeneSelectionNoSelectionShader', () => {
127
135
  const expected = `
128
136
  void main() {
129
137
  setColor(vec4(0.39215686274509803, 0.7843137254901961, 0.19607843137254902, 0.7));
138
+ setPointMarkerBorderWidth(0.0);
130
139
  }
131
140
  `;
132
141
  expectShaderEqual(result, expected);
@@ -147,6 +156,7 @@ void main() {
147
156
  }
148
157
  }
149
158
  setColor(color);
159
+ setPointMarkerBorderWidth(0.0);
150
160
  }
151
161
  `;
152
162
  expectShaderEqual(result, expected);
@@ -169,6 +179,7 @@ void main() {
169
179
  }
170
180
  }
171
181
  setColor(color);
182
+ setPointMarkerBorderWidth(0.0);
172
183
  }
173
184
  `;
174
185
  expectShaderEqual(result, expected);
@@ -194,6 +205,7 @@ void main() {
194
205
  discard;
195
206
  }
196
207
  setColor(vec4(matchedColor, 0.75));
208
+ setPointMarkerBorderWidth(0.0);
197
209
  }
198
210
  `;
199
211
  expectShaderEqual(result, expected);
@@ -213,6 +225,7 @@ void main() {
213
225
  if (colorIdx < 0) { colorIdx = -colorIdx; }
214
226
  vec3 color = palette[colorIdx];
215
227
  setColor(vec4(color, 0.5));
228
+ setPointMarkerBorderWidth(0.0);
216
229
  }
217
230
  `;
218
231
  expectShaderEqual(result, expected);
@@ -236,8 +249,10 @@ void main() {
236
249
  int colorIdx = geneIndex - (geneIndex / 3) * 3;
237
250
  if (colorIdx < 0) { colorIdx = -colorIdx; }
238
251
  setColor(vec4(palette[colorIdx], 0.8));
252
+ setPointMarkerBorderWidth(0.0);
239
253
  } else {
240
254
  setColor(vec4(0.19607843137254902, 0.19607843137254902, 0.19607843137254902, 0.8));
255
+ setPointMarkerBorderWidth(0.0);
241
256
  }
242
257
  }
243
258
  `;
@@ -264,6 +279,7 @@ void main() {
264
279
  int colorIdx = geneIndex - (geneIndex / 3) * 3;
265
280
  if (colorIdx < 0) { colorIdx = -colorIdx; }
266
281
  setColor(vec4(palette[colorIdx], 1));
282
+ setPointMarkerBorderWidth(0.0);
267
283
  }
268
284
  `;
269
285
  expectShaderEqual(result, expected);
@@ -290,6 +306,7 @@ void main() {
290
306
  float g = hashToFloat(pointIndex, 1);
291
307
  float b = hashToFloat(pointIndex, 2);
292
308
  setColor(vec4(r, g, b, 0.9));
309
+ setPointMarkerBorderWidth(0.0);
293
310
  }
294
311
  `;
295
312
  expectShaderEqual(result, expected);
@@ -321,8 +338,10 @@ void main() {
321
338
  float g = hashToFloat(pointIndex, 1);
322
339
  float b = hashToFloat(pointIndex, 2);
323
340
  setColor(vec4(r, g, b, 0.5));
341
+ setPointMarkerBorderWidth(0.0);
324
342
  } else {
325
343
  setColor(vec4(0.39215686274509803, 0.39215686274509803, 0.39215686274509803, 0.5));
344
+ setPointMarkerBorderWidth(0.0);
326
345
  }
327
346
  }
328
347
  `;
@@ -357,6 +376,7 @@ void main() {
357
376
  float g = hashToFloat(pointIndex, 1);
358
377
  float b = hashToFloat(pointIndex, 2);
359
378
  setColor(vec4(r, g, b, 1));
379
+ setPointMarkerBorderWidth(0.0);
360
380
  }
361
381
  `;
362
382
  expectShaderEqual(result, expected);
@@ -1 +1 @@
1
- {"version":3,"file":"use-memo-custom-comparison.d.ts","sourceRoot":"","sources":["../src/use-memo-custom-comparison.js"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AACH,wCAPa,CAAC,WACH,MAAM,CAAC,gBACP,GAAG,iBACH,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,OAAO,GAEvC,CAAC,CAUb;AA6CD,kFA+CC;AAED,0FAyEC"}
1
+ {"version":3,"file":"use-memo-custom-comparison.d.ts","sourceRoot":"","sources":["../src/use-memo-custom-comparison.js"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AACH,wCAPa,CAAC,WACH,MAAM,CAAC,gBACP,GAAG,iBACH,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,OAAO,GAEvC,CAAC,CAUb;AA6CD,kFAgDC;AAED,0FA8EC"}
@@ -74,6 +74,7 @@ export function customIsEqualForCellColors(prevDeps, nextDeps) {
74
74
  'obsSetSelection',
75
75
  'additionalObsSets',
76
76
  'spatialChannelColor',
77
+ 'spatialChannelOpacity',
77
78
  ])) {
78
79
  forceUpdate = true;
79
80
  }
@@ -96,6 +97,9 @@ export function customIsEqualForInitialViewerState(prevDeps, nextDeps) {
96
97
  const curriedShallowDiffByLayerCoordinationWithKeys = (depName, layerScope, keys) => shallowDiffByLayerCoordinationWithKeys(prevDeps, nextDeps, depName, layerScope, keys);
97
98
  const curriedShallowDiffByChannelCoordination = (depName, layerScope, channelScope) => shallowDiffByChannelCoordination(prevDeps, nextDeps, depName, layerScope, channelScope);
98
99
  const curriedShallowDiffByChannelCoordinationWithKeys = (depName, layerScope, channelScope, keys) => shallowDiffByChannelCoordinationWithKeys(prevDeps, nextDeps, depName, layerScope, channelScope, keys);
100
+ if (['theme', 'showAxisLines'].some(curriedShallowDiff)) {
101
+ forceUpdate = true;
102
+ }
99
103
  // Segmentation layers/channels.
100
104
  if (['segmentationLayerScopes', 'segmentationChannelScopesByLayer'].some(curriedShallowDiff)) {
101
105
  // Force update for all layers since the layerScopes array changed.
@@ -136,6 +140,7 @@ export function customIsEqualForInitialViewerState(prevDeps, nextDeps) {
136
140
  'featureSelection',
137
141
  'featureFilterMode',
138
142
  'featureColor',
143
+ 'spatialPointStrokeWidth',
139
144
  ])
140
145
  // For opacity, use an epsilon comparison to avoid too many re-renders, as it affects performance.
141
146
  || (Math.abs(prevDeps?.pointLayerCoordination?.[0]?.[layerScope]?.spatialLayerOpacity - nextDeps?.pointLayerCoordination?.[0]?.[layerScope]?.spatialLayerOpacity)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/neuroglancer",
3
- "version": "3.9.7",
3
+ "version": "3.9.9",
4
4
  "author": "Gehlenborg Lab",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -20,14 +20,14 @@
20
20
  "lodash-es": "^4.17.21",
21
21
  "three": "^0.154.0",
22
22
  "react": "18.3.1",
23
- "@vitessce/styles": "3.9.7",
24
- "@vitessce/constants-internal": "3.9.7",
25
- "@vitessce/vit-s": "3.9.7",
26
- "@vitessce/utils": "3.9.7",
27
- "@vitessce/neuroglancer-workers": "3.9.7",
28
- "@vitessce/sets-utils": "3.9.7",
29
- "@vitessce/tooltip": "3.9.7",
30
- "@vitessce/legend": "3.9.7"
23
+ "@vitessce/constants-internal": "3.9.9",
24
+ "@vitessce/neuroglancer-workers": "3.9.9",
25
+ "@vitessce/styles": "3.9.9",
26
+ "@vitessce/sets-utils": "3.9.9",
27
+ "@vitessce/utils": "3.9.9",
28
+ "@vitessce/vit-s": "3.9.9",
29
+ "@vitessce/tooltip": "3.9.9",
30
+ "@vitessce/legend": "3.9.9"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@testing-library/jest-dom": "^6.6.3",
@@ -92,7 +92,7 @@ export class NeuroglancerComp extends PureComponent {
92
92
  }
93
93
 
94
94
  render() {
95
- const { classes, viewerState, cellColorMapping } = this.props;
95
+ const { classes, viewerState, cellColorMapping, onLayerLoadingChange } = this.props;
96
96
 
97
97
  return (
98
98
  <>
@@ -103,6 +103,7 @@ export class NeuroglancerComp extends PureComponent {
103
103
  brainMapsClientId="NOT_A_VALID_ID"
104
104
  viewerState={viewerState}
105
105
  onViewerStateChanged={this.onViewerStateChanged}
106
+ onLayerLoadingChange={onLayerLoadingChange}
106
107
  bundleRoot={this.bundleRoot}
107
108
  cellColorMapping={cellColorMapping}
108
109
  ref={this.onRef}
@@ -79,6 +79,7 @@ export function NeuroglancerSubscriber(props) {
79
79
  downloadButtonVisible,
80
80
  removeGridComponent,
81
81
  theme,
82
+ showAxisLines = false,
82
83
  title = 'Spatial',
83
84
  subtitle = 'Powered by Neuroglancer',
84
85
  helpText = ViewHelpMapping.NEUROGLANCER,
@@ -207,6 +208,7 @@ export function NeuroglancerSubscriber(props) {
207
208
  CoordinationType.TOOLTIPS_VISIBLE,
208
209
  CoordinationType.TOOLTIP_CROSSHAIRS_VISIBLE,
209
210
  CoordinationType.LEGEND_VISIBLE,
211
+ CoordinationType.SPATIAL_POINT_STROKE_WIDTH,
210
212
  ],
211
213
  coordinationScopes,
212
214
  coordinationScopesBy,
@@ -289,8 +291,8 @@ export function NeuroglancerSubscriber(props) {
289
291
  obsSetSelection,
290
292
  additionalObsSets,
291
293
  spatialChannelColor,
294
+ spatialChannelOpacity,
292
295
  } = segmentationChannelCoordination[0][layerScope][channelScope];
293
-
294
296
  if (obsColorEncoding === 'spatialChannelColor') {
295
297
  // All segments get the same static channel color
296
298
  if (layerIndex && spatialChannelColor) {
@@ -315,6 +317,7 @@ export function NeuroglancerSubscriber(props) {
315
317
  });
316
318
  }
317
319
  result[layerScope][channelScope] = ngCellColors;
320
+ result[layerScope].opacity = spatialChannelOpacity ?? 1.0;
318
321
  }
319
322
  } else if (layerSets && layerIndex) {
320
323
  const mergedCellSets = mergeObsSets(layerSets, additionalObsSets);
@@ -331,6 +334,7 @@ export function NeuroglancerSubscriber(props) {
331
334
  ngCellColors[i] = rgbToHex(color);
332
335
  });
333
336
  result[layerScope][channelScope] = ngCellColors;
337
+ result[layerScope].opacity = spatialChannelOpacity ?? 1.0;
334
338
  }
335
339
  });
336
340
  });
@@ -349,6 +353,7 @@ export function NeuroglancerSubscriber(props) {
349
353
  // Obtain the Neuroglancer viewerState object.
350
354
  const initalViewerState = useNeuroglancerViewerState(
351
355
  theme,
356
+ showAxisLines,
352
357
  segmentationLayerScopes,
353
358
  segmentationChannelScopesByLayer,
354
359
  segmentationLayerCoordination,
@@ -403,6 +408,9 @@ export function NeuroglancerSubscriber(props) {
403
408
  orbit: spatialRotationOrbit,
404
409
  });
405
410
 
411
+ // Track layer loading state for showing loading indicator
412
+ const [isLayersLoaded, setIsLayersLoaded] = useState(false);
413
+
406
414
  // Track the last coord values we saw, and only mark "vitessce"
407
415
  // when *those* actually change. This prevents cell set renders
408
416
  // from spoofing the source.
@@ -566,7 +574,10 @@ export function NeuroglancerSubscriber(props) {
566
574
  const result = {};
567
575
  segmentationLayerScopes?.forEach((layerScope) => {
568
576
  const channelScope = segmentationChannelScopesByLayer?.[layerScope]?.[0];
569
- result[layerScope] = segmentationColorMapping?.[layerScope]?.[channelScope] ?? {};
577
+ result[layerScope] = {
578
+ colors: segmentationColorMapping?.[layerScope]?.[channelScope] ?? {},
579
+ opacity: segmentationColorMapping?.[layerScope]?.opacity ?? 1.0,
580
+ };
570
581
  });
571
582
  return result;
572
583
  }, [segmentationColorMapping, segmentationLayerScopes, segmentationChannelScopesByLayer]);
@@ -720,12 +731,13 @@ export function NeuroglancerSubscriber(props) {
720
731
 
721
732
  const updatedLayers = current?.layers?.map((layer, idx) => {
722
733
  const layerScope = segmentationLayerScopes?.[idx];
723
- const layerColorMapping = cellColorMappingByLayer?.[layerScope] ?? {};
734
+ const layerColorMapping = cellColorMappingByLayer?.[layerScope]?.colors ?? {};
724
735
  const layerSegments = Object.keys(layerColorMapping);
725
736
  return {
726
737
  ...layer,
727
738
  segments: layerSegments,
728
739
  segmentColors: layerColorMapping,
740
+ objectAlpha: cellColorMappingByLayer?.[layerScope]?.opacity ?? 1.0,
729
741
  };
730
742
  }) ?? [];
731
743
 
@@ -758,6 +770,10 @@ export function NeuroglancerSubscriber(props) {
758
770
  setCellHighlight(String(obsId));
759
771
  }, [setCellHighlight]);
760
772
 
773
+ const handleLayerLoadingChange = useCallback((isLoaded) => {
774
+ setIsLayersLoaded(isLoaded);
775
+ }, []);
776
+
761
777
  // TODO: if all cells are deselected, a black view is shown, rather we want to show empty NG view?
762
778
  // if (!cellColorMapping || Object.keys(cellColorMapping).length === 0) {
763
779
  // return;
@@ -777,7 +793,7 @@ export function NeuroglancerSubscriber(props) {
777
793
  closeButtonVisible={closeButtonVisible}
778
794
  downloadButtonVisible={downloadButtonVisible}
779
795
  removeGridComponent={removeGridComponent}
780
- isReady={isReady}
796
+ isReady={isReady && isLayersLoaded}
781
797
  errors={errors}
782
798
  withPadding={false}
783
799
  guideUrl={GUIDE_URL}
@@ -788,10 +804,17 @@ export function NeuroglancerSubscriber(props) {
788
804
  <MultiLegend
789
805
  theme="dark"
790
806
  maxHeight={ngHeight}
807
+
808
+ // Segmentations
791
809
  segmentationLayerScopes={segmentationLayerScopes}
792
810
  segmentationLayerCoordination={segmentationLayerCoordination}
793
811
  segmentationChannelScopesByLayer={segmentationChannelScopesByLayer}
794
812
  segmentationChannelCoordination={segmentationChannelCoordination}
813
+
814
+ // Points
815
+ pointLayerScopes={pointLayerScopes}
816
+ pointLayerCoordination={pointLayerCoordination}
817
+ pointMultiIndicesData={pointMultiIndicesData}
795
818
  />
796
819
  </div>
797
820
 
@@ -802,6 +825,7 @@ export function NeuroglancerSubscriber(props) {
802
825
  viewerState={derivedViewerState}
803
826
  cellColorMapping={cellColorMappingByLayer}
804
827
  setViewerState={handleStateUpdate}
828
+ onLayerLoadingChange={handleLayerLoadingChange}
805
829
  />
806
830
  </div>
807
831
  ) : null}