@vitessce/scatterplot 3.5.8 → 3.5.10

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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A, E, S, a, b, f, e, d, c } from "./index-6f1ecd35.js";
1
+ import { A, E, S, a, b, f, e, d, c } from "./index-d522aebb.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-6f1ecd35.js";
1
+ import { B as BaseDecoder } from "./index-d522aebb.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,5 +1,5 @@
1
1
  import { i as inflate_1 } from "./pako.esm-68f84e2a.js";
2
- import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-6f1ecd35.js";
2
+ import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-d522aebb.js";
3
3
  import "react";
4
4
  import "@vitessce/vit-s";
5
5
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-6f1ecd35.js";
1
+ import { B as BaseDecoder } from "./index-d522aebb.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-6f1ecd35.js";
1
+ import { B as BaseDecoder } from "./index-d522aebb.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-6f1ecd35.js";
1
+ import { B as BaseDecoder } from "./index-d522aebb.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-6f1ecd35.js";
1
+ import { B as BaseDecoder } from "./index-d522aebb.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1 +1 @@
1
- {"version":3,"file":"Scatterplot.d.ts","sourceRoot":"","sources":["../src/Scatterplot.js"],"names":[],"mappings":";AA+hBA;;;;;;GAMG;AACH,sCAKG"}
1
+ {"version":3,"file":"Scatterplot.d.ts","sourceRoot":"","sources":["../src/Scatterplot.js"],"names":[],"mappings":";AA+jBA;;;;;;GAMG;AACH,sCAKG"}
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import React, { forwardRef } from 'react';
4
4
  import { forceSimulation } from 'd3-force';
5
5
  import { isEqual } from 'lodash-es';
6
- import { deck, getSelectionLayer, ScaledExpressionExtension, SelectionExtension, } from '@vitessce/gl';
6
+ import { deck, getSelectionLayer, ScaledExpressionExtension, SelectionExtension, ContourLayerWithText, } from '@vitessce/gl';
7
7
  import { getDefaultColor } from '@vitessce/utils';
8
8
  import { AbstractSpatialOrScatterplot, createQuadTree, forceCollideRects, getOnHoverCallback, } from './shared-spatial-scatterplot/index.js';
9
9
  const CELLS_LAYER_ID = 'scatterplot';
@@ -108,7 +108,11 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
108
108
  // - Array of per-selected-sampleSet layers (filter to sampleSet members)
109
109
  // - Array of per-sampleSet, per-obsSet layers (filter to sampleSet and obsSet members)
110
110
  createContourLayers() {
111
- const { theme, obsSetColor, sampleSetColor, contourColorEncoding, contourThresholds, contoursFilled, contourColor: contourColorProp, } = this.props;
111
+ const { theme, obsSetColor, sampleSetColor, contourColorEncoding, contourThresholds, contoursFilled, contourColor: contourColorProp, circleInfo, cellSetLabelsVisible, cellSetLabelSize, featureSelection, } = this.props;
112
+ const circlePointSet = new Set();
113
+ const [getWeight, aggregation] = Array.isArray(featureSelection) && featureSelection.length > 0
114
+ ? ([contourGetWeight, 'MEAN'])
115
+ : ([1, 'COUNT']);
112
116
  const layers = Array.from(this.stratifiedData.entries())
113
117
  .flatMap(([obsSetKey, sampleSetMap]) => Array.from(sampleSetMap.entries())
114
118
  .map(([sampleSetKey, arrs]) => {
@@ -127,30 +131,34 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
127
131
  contourColor = (sampleSetColor?.find(d => isEqual(sampleSetKey, d.path))?.color
128
132
  || contourColor);
129
133
  }
130
- return new deck.ContourLayer({
134
+ const contours = contourThresholds.map((threshold, i) => ({
135
+ i,
136
+ threshold: (contoursFilled ? [threshold, threshold[i + 1] || Infinity] : threshold),
137
+ // TODO: should the opacity steps be uniform? Should align with human perception.
138
+ // TODO: support usage of static colors.
139
+ color: [
140
+ // r, g, b
141
+ ...contourColor,
142
+ // a
143
+ (contoursFilled
144
+ ? ((i + 0.5) / contourThresholds.length * 255)
145
+ : ((i + 1) / (contourThresholds.length)) * 255),
146
+ ],
147
+ strokeWidth: 2,
148
+ // We need to specify a greater z-index so that the contour layers
149
+ // will render on top of the point layer.
150
+ zIndex: POINT_LAYER_Z_INDEX + 1 + i,
151
+ }));
152
+ return new ContourLayerWithText({
131
153
  id: `contour-${JSON.stringify(obsSetKey)}-${JSON.stringify(sampleSetKey)}`,
132
154
  coordinateSystem: deck.COORDINATE_SYSTEM.CARTESIAN,
133
155
  data: deckData,
134
- getWeight: contourGetWeight,
156
+ getWeight,
135
157
  getPosition: contourGetPosition,
136
- contours: contourThresholds.map((threshold, i) => ({
137
- threshold: (contoursFilled ? [threshold, threshold[i + 1] || Infinity] : threshold),
138
- // TODO: should the opacity steps be uniform? Should align with human perception.
139
- // TODO: support usage of static colors.
140
- color: [
141
- // r, g, b
142
- ...contourColor,
143
- // a
144
- (contoursFilled
145
- ? ((i + 0.5) / contourThresholds.length * 255)
146
- : ((i + 1) / (contourThresholds.length)) * 255),
147
- ],
148
- strokeWidth: 2,
149
- // We need to specify a greater z-index so that the contour layers
150
- // will render on top of the point layer.
151
- zIndex: POINT_LAYER_Z_INDEX + 1 + i,
152
- })),
153
- aggregation: 'MEAN',
158
+ obsSetPath: obsSetKey,
159
+ sampleSetPath: sampleSetKey,
160
+ contours,
161
+ aggregation,
154
162
  gpuAggregation: true,
155
163
  visible: true,
156
164
  pickable: false,
@@ -158,6 +166,14 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
158
166
  filled: contoursFilled,
159
167
  cellSize: 0.25,
160
168
  zOffset: 0.005,
169
+ // Info for text/line rendering
170
+ circleInfo,
171
+ circlePointSet,
172
+ obsSetLabelsVisible: cellSetLabelsVisible,
173
+ obsSetLabelSize: cellSetLabelSize,
174
+ updateTriggers: {
175
+ getWeight: [getWeight],
176
+ },
161
177
  });
162
178
  }));
163
179
  return layers;
@@ -349,11 +365,16 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
349
365
  }
350
366
  }
351
367
  onUpdateCellSetsLayers(onlyViewStateChange) {
352
- // Because the label sizes for the force simulation depend on the zoom level,
353
- // we _could_ run the simulation every time the zoom level changes.
354
- // However, this has a performance impact in firefox.
355
- if (onlyViewStateChange) {
356
- const { viewState, cellSetLabelsVisible } = this.props;
368
+ const { viewState, cellSetLabelsVisible, embeddingContoursVisible } = this.props;
369
+ if (embeddingContoursVisible) {
370
+ // If rendering contours, we do not want to render text labels using this method,
371
+ // as the ContourLayerWithText implements its own text labeling internally.
372
+ this.cellSetsLayers = [];
373
+ }
374
+ else if (onlyViewStateChange) {
375
+ // Because the label sizes for the force simulation depend on the zoom level,
376
+ // we _could_ run the simulation every time the zoom level changes.
377
+ // However, this has a performance impact in firefox.
357
378
  const { zoom } = viewState;
358
379
  const { cellSetsLabelPrevZoom } = this;
359
380
  // Instead, we can just check if the zoom level has changed
@@ -412,7 +433,11 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
412
433
  this.onUpdateCellsLayer();
413
434
  forceUpdate = true;
414
435
  }
415
- if (['stratifiedData', 'contourColorEncoding', 'contoursFilled', 'contourThresholds', 'embeddingContoursVisible'].some(shallowDiff)) {
436
+ if ([
437
+ 'stratifiedData', 'contourColorEncoding', 'contoursFilled',
438
+ 'contourThresholds', 'embeddingContoursVisible',
439
+ 'cellSetLabelsVisible', 'cellSetLabelSize',
440
+ ].some(shallowDiff)) {
416
441
  // Cells data changed.
417
442
  this.onUpdateContourLayers();
418
443
  forceUpdate = true;
@@ -420,6 +445,7 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
420
445
  if ([
421
446
  'cellSetPolygons', 'cellSetPolygonsVisible',
422
447
  'cellSetLabelsVisible', 'cellSetLabelSize',
448
+ 'embeddingContoursVisible',
423
449
  ].some(shallowDiff)) {
424
450
  // Cell sets layer props changed.
425
451
  this.onUpdateCellSetsLayers(false);
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-opacity.d.ts","sourceRoot":"","sources":["../../src/shared-spatial-scatterplot/dynamic-opacity.js"],"names":[],"mappings":"AAKA,mIA0BC;AAGD,uIAuBC"}
1
+ {"version":3,"file":"dynamic-opacity.d.ts","sourceRoot":"","sources":["../../src/shared-spatial-scatterplot/dynamic-opacity.js"],"names":[],"mappings":"AAKA,mIA0BC;AAGD,uIA6BC"}
@@ -24,11 +24,20 @@ export function getPointSizeDevicePixels(devicePixelRatio, zoom, xRange, yRange,
24
24
  // Reference: https://observablehq.com/@rreusser/selecting-the-right-opacity-for-2d-point-clouds
25
25
  export function getPointOpacity(zoom, xRange, yRange, width, height, numCells, avgFillDensity) {
26
26
  const N = numCells;
27
- const [minX, minY, maxX, maxY] = new deck.OrthographicView({ zoom }).makeViewport({
28
- height,
29
- width,
30
- viewState: { zoom, target: [0, 0, 0] },
31
- }).getBounds();
27
+ let minX;
28
+ let minY;
29
+ let maxX;
30
+ let maxY;
31
+ try {
32
+ [minX, minY, maxX, maxY] = new deck.OrthographicView({ zoom }).makeViewport({
33
+ height,
34
+ width,
35
+ viewState: { zoom, target: [0, 0, 0] },
36
+ }).getBounds();
37
+ }
38
+ catch {
39
+ return 1.0;
40
+ }
32
41
  const X = maxY - minY;
33
42
  const Y = maxX - minX;
34
43
  const X0 = xRange;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/scatterplot",
3
- "version": "3.5.8",
3
+ "version": "3.5.10",
4
4
  "author": "HIDIVE Lab at HMS",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -23,12 +23,12 @@
23
23
  "d3-quadtree": "^1.0.7",
24
24
  "lodash-es": "^4.17.21",
25
25
  "react-aria": "^3.28.0",
26
- "@vitessce/constants-internal": "3.5.8",
27
- "@vitessce/gl": "3.5.8",
28
- "@vitessce/icons": "3.5.8",
29
- "@vitessce/tooltip": "3.5.8",
30
- "@vitessce/utils": "3.5.8",
31
- "@vitessce/vit-s": "3.5.8"
26
+ "@vitessce/constants-internal": "3.5.10",
27
+ "@vitessce/gl": "3.5.10",
28
+ "@vitessce/icons": "3.5.10",
29
+ "@vitessce/tooltip": "3.5.10",
30
+ "@vitessce/utils": "3.5.10",
31
+ "@vitessce/vit-s": "3.5.10"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@testing-library/jest-dom": "^5.16.4",
@@ -4,6 +4,7 @@ import { forceSimulation } from 'd3-force';
4
4
  import { isEqual } from 'lodash-es';
5
5
  import {
6
6
  deck, getSelectionLayer, ScaledExpressionExtension, SelectionExtension,
7
+ ContourLayerWithText,
7
8
  } from '@vitessce/gl';
8
9
  import { getDefaultColor } from '@vitessce/utils';
9
10
  import {
@@ -130,8 +131,17 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
130
131
  contourThresholds,
131
132
  contoursFilled,
132
133
  contourColor: contourColorProp,
134
+ circleInfo,
135
+ cellSetLabelsVisible,
136
+ cellSetLabelSize,
137
+ featureSelection,
133
138
  } = this.props;
134
139
 
140
+ const circlePointSet = new Set();
141
+ const [getWeight, aggregation] = Array.isArray(featureSelection) && featureSelection.length > 0
142
+ ? ([contourGetWeight, 'MEAN'])
143
+ : ([1, 'COUNT']);
144
+
135
145
  const layers = Array.from(this.stratifiedData.entries())
136
146
  .flatMap(([obsSetKey, sampleSetMap]) => Array.from(sampleSetMap.entries())
137
147
  .map(([sampleSetKey, arrs]) => {
@@ -155,30 +165,35 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
155
165
  || contourColor
156
166
  );
157
167
  }
158
- return new deck.ContourLayer({
168
+ const contours = contourThresholds.map((threshold, i) => ({
169
+ i,
170
+ threshold: (contoursFilled ? [threshold, threshold[i + 1] || Infinity] : threshold),
171
+ // TODO: should the opacity steps be uniform? Should align with human perception.
172
+ // TODO: support usage of static colors.
173
+ color: [
174
+ // r, g, b
175
+ ...contourColor,
176
+ // a
177
+ (contoursFilled
178
+ ? ((i + 0.5) / contourThresholds.length * 255)
179
+ : ((i + 1) / (contourThresholds.length)) * 255),
180
+ ],
181
+ strokeWidth: 2,
182
+ // We need to specify a greater z-index so that the contour layers
183
+ // will render on top of the point layer.
184
+ zIndex: POINT_LAYER_Z_INDEX + 1 + i,
185
+ }));
186
+
187
+ return new ContourLayerWithText({
159
188
  id: `contour-${JSON.stringify(obsSetKey)}-${JSON.stringify(sampleSetKey)}`,
160
189
  coordinateSystem: deck.COORDINATE_SYSTEM.CARTESIAN,
161
190
  data: deckData,
162
- getWeight: contourGetWeight,
191
+ getWeight,
163
192
  getPosition: contourGetPosition,
164
- contours: contourThresholds.map((threshold, i) => ({
165
- threshold: (contoursFilled ? [threshold, threshold[i + 1] || Infinity] : threshold),
166
- // TODO: should the opacity steps be uniform? Should align with human perception.
167
- // TODO: support usage of static colors.
168
- color: [
169
- // r, g, b
170
- ...contourColor,
171
- // a
172
- (contoursFilled
173
- ? ((i + 0.5) / contourThresholds.length * 255)
174
- : ((i + 1) / (contourThresholds.length)) * 255),
175
- ],
176
- strokeWidth: 2,
177
- // We need to specify a greater z-index so that the contour layers
178
- // will render on top of the point layer.
179
- zIndex: POINT_LAYER_Z_INDEX + 1 + i,
180
- })),
181
- aggregation: 'MEAN',
193
+ obsSetPath: obsSetKey,
194
+ sampleSetPath: sampleSetKey,
195
+ contours,
196
+ aggregation,
182
197
  gpuAggregation: true,
183
198
  visible: true,
184
199
  pickable: false,
@@ -186,6 +201,14 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
186
201
  filled: contoursFilled,
187
202
  cellSize: 0.25,
188
203
  zOffset: 0.005,
204
+ // Info for text/line rendering
205
+ circleInfo,
206
+ circlePointSet,
207
+ obsSetLabelsVisible: cellSetLabelsVisible,
208
+ obsSetLabelSize: cellSetLabelSize,
209
+ updateTriggers: {
210
+ getWeight: [getWeight],
211
+ },
189
212
  });
190
213
  }));
191
214
  return layers;
@@ -429,30 +452,34 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
429
452
  }
430
453
 
431
454
  onUpdateCellSetsLayers(onlyViewStateChange) {
432
- // Because the label sizes for the force simulation depend on the zoom level,
433
- // we _could_ run the simulation every time the zoom level changes.
434
- // However, this has a performance impact in firefox.
435
- if (onlyViewStateChange) {
436
- const { viewState, cellSetLabelsVisible } = this.props;
455
+ const { viewState, cellSetLabelsVisible, embeddingContoursVisible } = this.props;
456
+ if (embeddingContoursVisible) {
457
+ // If rendering contours, we do not want to render text labels using this method,
458
+ // as the ContourLayerWithText implements its own text labeling internally.
459
+ this.cellSetsLayers = [];
460
+ } else if (onlyViewStateChange) {
461
+ // Because the label sizes for the force simulation depend on the zoom level,
462
+ // we _could_ run the simulation every time the zoom level changes.
463
+ // However, this has a performance impact in firefox.
437
464
  const { zoom } = viewState;
438
465
  const { cellSetsLabelPrevZoom } = this;
439
466
  // Instead, we can just check if the zoom level has changed
440
467
  // by some relatively large delta, to be more conservative
441
468
  // about re-running the force simulation.
442
469
  if (cellSetLabelsVisible
443
- && (
444
- cellSetsLabelPrevZoom === null
445
- || Math.abs(cellSetsLabelPrevZoom - zoom) > LABEL_UPDATE_ZOOM_DELTA
446
- )
470
+ && (
471
+ cellSetsLabelPrevZoom === null
472
+ || Math.abs(cellSetsLabelPrevZoom - zoom) > LABEL_UPDATE_ZOOM_DELTA
473
+ )
447
474
  ) {
448
475
  this.cellSetsLayers = this.createCellSetsLayers();
449
476
  this.cellSetsLabelPrevZoom = zoom;
450
477
  }
451
478
  } else {
452
- // Otherwise, something more substantial than just
453
- // the viewState has changed, such as the label array
454
- // itself, so we always want to update the layer
455
- // in this case.
479
+ // Otherwise, something more substantial than just
480
+ // the viewState has changed, such as the label array
481
+ // itself, so we always want to update the layer
482
+ // in this case.
456
483
  this.cellSetsLayers = this.createCellSetsLayers();
457
484
  }
458
485
  }
@@ -507,7 +534,11 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
507
534
  forceUpdate = true;
508
535
  }
509
536
 
510
- if (['stratifiedData', 'contourColorEncoding', 'contoursFilled', 'contourThresholds', 'embeddingContoursVisible'].some(shallowDiff)) {
537
+ if ([
538
+ 'stratifiedData', 'contourColorEncoding', 'contoursFilled',
539
+ 'contourThresholds', 'embeddingContoursVisible',
540
+ 'cellSetLabelsVisible', 'cellSetLabelSize',
541
+ ].some(shallowDiff)) {
511
542
  // Cells data changed.
512
543
  this.onUpdateContourLayers();
513
544
  forceUpdate = true;
@@ -516,6 +547,7 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
516
547
  if ([
517
548
  'cellSetPolygons', 'cellSetPolygonsVisible',
518
549
  'cellSetLabelsVisible', 'cellSetLabelSize',
550
+ 'embeddingContoursVisible',
519
551
  ].some(shallowDiff)) {
520
552
  // Cell sets layer props changed.
521
553
  this.onUpdateCellSetsLayers(false);
@@ -34,11 +34,17 @@ export function getPointSizeDevicePixels(devicePixelRatio, zoom, xRange, yRange,
34
34
  // Reference: https://observablehq.com/@rreusser/selecting-the-right-opacity-for-2d-point-clouds
35
35
  export function getPointOpacity(zoom, xRange, yRange, width, height, numCells, avgFillDensity) {
36
36
  const N = numCells;
37
- const [minX, minY, maxX, maxY] = new deck.OrthographicView({ zoom }).makeViewport({
38
- height,
39
- width,
40
- viewState: { zoom, target: [0, 0, 0] },
41
- }).getBounds();
37
+ let minX; let minY; let maxX; let
38
+ maxY;
39
+ try {
40
+ [minX, minY, maxX, maxY] = new deck.OrthographicView({ zoom }).makeViewport({
41
+ height,
42
+ width,
43
+ viewState: { zoom, target: [0, 0, 0] },
44
+ }).getBounds();
45
+ } catch {
46
+ return 1.0;
47
+ }
42
48
  const X = maxY - minY;
43
49
  const Y = maxX - minX;
44
50
  const X0 = xRange;