@vitessce/neuroglancer 3.9.7 → 3.9.8

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.
@@ -28,7 +28,7 @@ function rgbToHex(rgb) {
28
28
  : `#${rgb.map(c => c.toString(16).padStart(2, '0')).join('')}`);
29
29
  }
30
30
  export function NeuroglancerSubscriber(props) {
31
- const { uuid, coordinationScopes: coordinationScopesRaw, coordinationScopesBy: coordinationScopesByRaw, closeButtonVisible, downloadButtonVisible, removeGridComponent, theme, title = 'Spatial', subtitle = 'Powered by Neuroglancer', helpText = ViewHelpMapping.NEUROGLANCER,
31
+ const { uuid, coordinationScopes: coordinationScopesRaw, coordinationScopesBy: coordinationScopesByRaw, closeButtonVisible, downloadButtonVisible, removeGridComponent, theme, showAxisLines = false, title = 'Spatial', subtitle = 'Powered by Neuroglancer', helpText = ViewHelpMapping.NEUROGLANCER,
32
32
  // Note: this is a temporary mechanism
33
33
  // to pass an initial NG camera state.
34
34
  // Ideally, all camera state should be passed via
@@ -95,6 +95,7 @@ export function NeuroglancerSubscriber(props) {
95
95
  CoordinationType.TOOLTIPS_VISIBLE,
96
96
  CoordinationType.TOOLTIP_CROSSHAIRS_VISIBLE,
97
97
  CoordinationType.LEGEND_VISIBLE,
98
+ CoordinationType.SPATIAL_POINT_STROKE_WIDTH,
98
99
  ], coordinationScopes, coordinationScopesBy, CoordinationType.POINT_LAYER);
99
100
  // Points data
100
101
  const [obsPointsData, obsPointsDataStatus, obsPointsUrls, obsPointsErrors] = useMultiObsPoints(coordinationScopes, coordinationScopesBy, loaders, dataset, mergeCoordination, uuid);
@@ -133,7 +134,7 @@ export function NeuroglancerSubscriber(props) {
133
134
  result[layerScope] = {};
134
135
  segmentationChannelScopesByLayer?.[layerScope]?.forEach((channelScope) => {
135
136
  const { obsSets: layerSets, obsIndex: layerIndex } = obsSegmentationsSetsData?.[layerScope]?.[channelScope] || {};
136
- const { obsSetColor, obsColorEncoding, obsSetSelection, additionalObsSets, spatialChannelColor, } = segmentationChannelCoordination[0][layerScope][channelScope];
137
+ const { obsSetColor, obsColorEncoding, obsSetSelection, additionalObsSets, spatialChannelColor, spatialChannelOpacity, } = segmentationChannelCoordination[0][layerScope][channelScope];
137
138
  if (obsColorEncoding === 'spatialChannelColor') {
138
139
  // All segments get the same static channel color
139
140
  if (layerIndex && spatialChannelColor) {
@@ -157,6 +158,7 @@ export function NeuroglancerSubscriber(props) {
157
158
  });
158
159
  }
159
160
  result[layerScope][channelScope] = ngCellColors;
161
+ result[layerScope].opacity = spatialChannelOpacity ?? 1.0;
160
162
  }
161
163
  }
162
164
  else if (layerSets && layerIndex) {
@@ -174,6 +176,7 @@ export function NeuroglancerSubscriber(props) {
174
176
  ngCellColors[i] = rgbToHex(color);
175
177
  });
176
178
  result[layerScope][channelScope] = ngCellColors;
179
+ result[layerScope].opacity = spatialChannelOpacity ?? 1.0;
177
180
  }
178
181
  });
179
182
  });
@@ -188,7 +191,7 @@ export function NeuroglancerSubscriber(props) {
188
191
  theme,
189
192
  }, customIsEqualForCellColors);
190
193
  // Obtain the Neuroglancer viewerState object.
191
- const initalViewerState = useNeuroglancerViewerState(theme, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationLayerCoordination, segmentationChannelCoordination, obsSegmentationsUrls, obsSegmentationsData, pointLayerScopes, pointLayerCoordination, obsPointsUrls, obsPointsData, pointMultiIndicesData);
194
+ const initalViewerState = useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationLayerCoordination, segmentationChannelCoordination, obsSegmentationsUrls, obsSegmentationsData, pointLayerScopes, pointLayerCoordination, obsPointsUrls, obsPointsData, pointMultiIndicesData);
192
195
  const [latestViewerStateIteration, incrementLatestViewerStateIteration] = useReducer(x => x + 1, 0);
193
196
  const latestViewerStateRef = useRef({
194
197
  ...initalViewerState,
@@ -225,6 +228,8 @@ export function NeuroglancerSubscriber(props) {
225
228
  z: spatialRotationZ,
226
229
  orbit: spatialRotationOrbit,
227
230
  });
231
+ // Track layer loading state for showing loading indicator
232
+ const [isLayersLoaded, setIsLayersLoaded] = useState(false);
228
233
  // Track the last coord values we saw, and only mark "vitessce"
229
234
  // when *those* actually change. This prevents cell set renders
230
235
  // from spoofing the source.
@@ -367,7 +372,10 @@ export function NeuroglancerSubscriber(props) {
367
372
  const result = {};
368
373
  segmentationLayerScopes?.forEach((layerScope) => {
369
374
  const channelScope = segmentationChannelScopesByLayer?.[layerScope]?.[0];
370
- result[layerScope] = segmentationColorMapping?.[layerScope]?.[channelScope] ?? {};
375
+ result[layerScope] = {
376
+ colors: segmentationColorMapping?.[layerScope]?.[channelScope] ?? {},
377
+ opacity: segmentationColorMapping?.[layerScope]?.opacity ?? 1.0,
378
+ };
371
379
  });
372
380
  return result;
373
381
  }, [segmentationColorMapping, segmentationLayerScopes, segmentationChannelScopesByLayer]);
@@ -487,12 +495,13 @@ export function NeuroglancerSubscriber(props) {
487
495
  }
488
496
  const updatedLayers = current?.layers?.map((layer, idx) => {
489
497
  const layerScope = segmentationLayerScopes?.[idx];
490
- const layerColorMapping = cellColorMappingByLayer?.[layerScope] ?? {};
498
+ const layerColorMapping = cellColorMappingByLayer?.[layerScope]?.colors ?? {};
491
499
  const layerSegments = Object.keys(layerColorMapping);
492
500
  return {
493
501
  ...layer,
494
502
  segments: layerSegments,
495
503
  segmentColors: layerColorMapping,
504
+ objectAlpha: cellColorMappingByLayer?.[layerScope]?.opacity ?? 1.0,
496
505
  };
497
506
  }) ?? [];
498
507
  const updated = {
@@ -519,11 +528,18 @@ export function NeuroglancerSubscriber(props) {
519
528
  const onSegmentHighlight = useCallback((obsId) => {
520
529
  setCellHighlight(String(obsId));
521
530
  }, [setCellHighlight]);
531
+ const handleLayerLoadingChange = useCallback((isLoaded) => {
532
+ setIsLayersLoaded(isLoaded);
533
+ }, []);
522
534
  // TODO: if all cells are deselected, a black view is shown, rather we want to show empty NG view?
523
535
  // if (!cellColorMapping || Object.keys(cellColorMapping).length === 0) {
524
536
  // return;
525
537
  // }
526
538
  const hasLayers = derivedViewerState?.layers?.length > 0;
527
539
  // console.log(derivedViewerState);
528
- return (_jsx(TitleInfo, { title: title, info: subtitle, helpText: helpText, isSpatial: true, theme: theme, closeButtonVisible: closeButtonVisible, downloadButtonVisible: downloadButtonVisible, removeGridComponent: removeGridComponent, isReady: isReady, errors: errors, withPadding: false, guideUrl: GUIDE_URL, children: hasLayers ? (_jsxs("div", { style: { position: 'relative', width: '100%', height: '100%' }, ref: containerRef, children: [_jsx("div", { style: { position: 'absolute', top: 0, right: 0, zIndex: 50 }, children: _jsx(MultiLegend, { theme: "dark", maxHeight: ngHeight, segmentationLayerScopes: segmentationLayerScopes, segmentationLayerCoordination: segmentationLayerCoordination, segmentationChannelScopesByLayer: segmentationChannelScopesByLayer, segmentationChannelCoordination: segmentationChannelCoordination }) }), _jsx(NeuroglancerComp, { classes: classes, onSegmentClick: onSegmentClick, onSelectHoveredCoords: onSegmentHighlight, viewerState: derivedViewerState, cellColorMapping: cellColorMappingByLayer, setViewerState: handleStateUpdate })] })) : null }));
540
+ return (_jsx(TitleInfo, { title: title, info: subtitle, helpText: helpText, isSpatial: true, theme: theme, closeButtonVisible: closeButtonVisible, downloadButtonVisible: downloadButtonVisible, removeGridComponent: removeGridComponent, isReady: isReady && isLayersLoaded, errors: errors, withPadding: false, guideUrl: GUIDE_URL, children: hasLayers ? (_jsxs("div", { style: { position: 'relative', width: '100%', height: '100%' }, ref: containerRef, children: [_jsx("div", { style: { position: 'absolute', top: 0, right: 0, zIndex: 50 }, children: _jsx(MultiLegend, { theme: "dark", maxHeight: ngHeight,
541
+ // Segmentations
542
+ segmentationLayerScopes: segmentationLayerScopes, segmentationLayerCoordination: segmentationLayerCoordination, segmentationChannelScopesByLayer: segmentationChannelScopesByLayer, segmentationChannelCoordination: segmentationChannelCoordination,
543
+ // Points
544
+ pointLayerScopes: pointLayerScopes, pointLayerCoordination: pointLayerCoordination, pointMultiIndicesData: pointMultiIndicesData }) }), _jsx(NeuroglancerComp, { classes: classes, onSegmentClick: onSegmentClick, onSelectHoveredCoords: onSegmentHighlight, viewerState: derivedViewerState, cellColorMapping: cellColorMappingByLayer, setViewerState: handleStateUpdate, onLayerLoadingChange: handleLayerLoadingChange })] })) : null }));
529
545
  }
@@ -34,6 +34,10 @@
34
34
  * @property {() => void} onSelectionDetailsStateChanged
35
35
  * A function of the form `() => {}` to respond to selection changes in the viewer.
36
36
  * @property {() => void} onViewerStateChanged
37
+ * @property {(isLoaded: boolean) => void} onLayerLoadingChange
38
+ * A function of the form `(isLoaded) => {}`, called when layer loading state changes.
39
+ * The `isLoaded` argument will be `true` when all segmentation layers have finished loading
40
+ * their data sources, or `false` when layers are still loading.
37
41
  *
38
42
  * @property {Array<Object>} callbacks
39
43
  * // ngServer: string,
@@ -64,6 +68,7 @@ export default class Neuroglancer {
64
68
  onVisibleChanged: null;
65
69
  onSelectionDetailsStateChanged: null;
66
70
  onViewerStateChanged: null;
71
+ onLayerLoadingChange: null;
67
72
  key: null;
68
73
  callbacks: never[];
69
74
  ngServer: string;
@@ -140,6 +145,12 @@ export type NgProps = {
140
145
  */
141
146
  onSelectionDetailsStateChanged: () => void;
142
147
  onViewerStateChanged: () => void;
148
+ /**
149
+ * A function of the form `(isLoaded) => {}`, called when layer loading state changes.
150
+ * The `isLoaded` argument will be `true` when all segmentation layers have finished loading
151
+ * their data sources, or `false` when layers are still loading.
152
+ */
153
+ onLayerLoadingChange: (isLoaded: boolean) => void;
143
154
  /**
144
155
  * // ngServer: string,
145
156
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ReactNeuroglancer.d.ts","sourceRoot":"","sources":["../src/ReactNeuroglancer.js"],"names":[],"mappings":"AAiCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAGH,4CAsBC;AAED,0DAGC;AAED,gEA2BC;AAED,kDAKC;AAED,+CAMC;AAED,0DAMC;AAED,6DAMC;AAED,8DAWC;AAED,+EAYC;AAED,yFAcC;AAED,+EA6BC;AAmFD,2FAwBC;AAED,kGAeC;AAED,gFAUC;AAED,uEA0BC;AAED,0CAA0C;AAC1C;IACE;;;;;;;;;;;;MAYE;IAEF,wBAYC;IAVC,iBAAoC;IACpC,YAAkB;IAElB,2BAA8B;IAC9B,yBAA+B;IAC/B,kBAAwB;IACxB,iBAAmB;IACnB,6BAAmC;IACnC,wBAA6C;IAC7C,uBAA4B;IAG9B;;;;MASE;IAGF,+BAWE;IAGF,mCAKE;IAGF,uDASE;IAGF,iEAsCE;IAEF,0BA+GC;IAED,yDAiIC;IAED,6BAUC;IAcD,mCAQC;IAED,0DAyCE;IAEF,yCAOE;IAEF,0BAsCE;IA5BI,mCAAyB;IA+B/B,sCAWE;IAEF,qCAUE;IAEF,sBAOC;CACF;;qBAv3Ba,MAAM;iBACN,MAAM;uBACN,MAAM;SACN,MAAM;sBACN;YAAO,MAAM,GAAE,MAAM;KAAC;;;;;;;;;;;;;;;;;;;uBAWtB,CAAC,OAAO,EAAC,GAAG,GAAC,IAAI,EAAE,KAAK,EAAC,GAAG,KAAK,IAAI;;;;;;;;;sBAQrC,CAAC,QAAQ,EAAC,GAAG,EAAE,KAAK,EAAC,GAAG,KAAK,IAAI;;;;oCAQjC,MAAM,IAAI;0BAEV,MAAM,IAAI;;;;eAEV,KAAK,CAAC,MAAM,CAAC"}
1
+ {"version":3,"file":"ReactNeuroglancer.d.ts","sourceRoot":"","sources":["../src/ReactNeuroglancer.js"],"names":[],"mappings":"AAiCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAGH,4CAsBC;AAED,0DAGC;AAED,gEA2BC;AAED,kDAKC;AAED,+CAMC;AAED,0DAMC;AAED,6DAMC;AAED,8DAWC;AAED,+EAYC;AAED,yFAcC;AAED,+EA6BC;AAmFD,2FAwBC;AAED,kGAeC;AAED,gFAUC;AAED,uEA0BC;AAED,0CAA0C;AAC1C;IACE;;;;;;;;;;;;;MAaE;IAEF,wBAYC;IAVC,iBAAoC;IACpC,YAAkB;IAElB,2BAA8B;IAC9B,yBAA+B;IAC/B,kBAAwB;IACxB,iBAAmB;IACnB,6BAAmC;IACnC,wBAA6C;IAC7C,uBAA4B;IAG9B;;;;MASE;IAGF,+BAWE;IAGF,mCAKE;IAGF,uDASE;IAGF,iEAsCE;IAEF,0BAiJC;IAED,yDAwJC;IAED,6BAUC;IAcD,mCAQC;IAED,0DAyCE;IAEF,yCAOE;IAEF,0BAsCE;IA5BI,mCAAyB;IA+B/B,sCAWE;IAEF,qCAUE;IAEF,sBAOC;CACF;;qBAr7Ba,MAAM;iBACN,MAAM;uBACN,MAAM;SACN,MAAM;sBACN;YAAO,MAAM,GAAE,MAAM;KAAC;;;;;;;;;;;;;;;;;;;uBAWtB,CAAC,OAAO,EAAC,GAAG,GAAC,IAAI,EAAE,KAAK,EAAC,GAAG,KAAK,IAAI;;;;;;;;;sBAQrC,CAAC,QAAQ,EAAC,GAAG,EAAE,KAAK,EAAC,GAAG,KAAK,IAAI;;;;oCAQjC,MAAM,IAAI;0BAEV,MAAM,IAAI;;;;;;0BACV,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI;;;;eAK3B,KAAK,CAAC,MAAM,CAAC"}
@@ -63,6 +63,10 @@ let viewerNoKey;
63
63
  * @property {() => void} onSelectionDetailsStateChanged
64
64
  * A function of the form `() => {}` to respond to selection changes in the viewer.
65
65
  * @property {() => void} onViewerStateChanged
66
+ * @property {(isLoaded: boolean) => void} onLayerLoadingChange
67
+ * A function of the form `(isLoaded) => {}`, called when layer loading state changes.
68
+ * The `isLoaded` argument will be `true` when all segmentation layers have finished loading
69
+ * their data sources, or `false` when layers are still loading.
66
70
  *
67
71
  * @property {Array<Object>} callbacks
68
72
  * // ngServer: string,
@@ -358,6 +362,7 @@ export default class Neuroglancer extends React.Component {
358
362
  onVisibleChanged: null,
359
363
  onSelectionDetailsStateChanged: null,
360
364
  onViewerStateChanged: null,
365
+ onLayerLoadingChange: null,
361
366
  key: null,
362
367
  callbacks: [],
363
368
  ngServer: 'https://neuroglancer-demo.appspot.com/',
@@ -434,7 +439,7 @@ export default class Neuroglancer extends React.Component {
434
439
  // NG layer names are of the form:
435
440
  // "obsSegmentations-init_A_obsSegmentations_0-init_A_obsSegmentations_0"
436
441
  const layerScope = Object.keys(cellColorMappingByLayer).find(scope => layer.name?.includes(scope));
437
- const selected = { ...(cellColorMappingByLayer[layerScope] || {}) };
442
+ const selected = { ...(cellColorMappingByLayer[layerScope]?.colors || {}) };
438
443
  // Track all known IDs for this layer scope
439
444
  if (!this.allKnownIdsByLayer)
440
445
  this.allKnownIdsByLayer = {};
@@ -554,7 +559,38 @@ export default class Neuroglancer extends React.Component {
554
559
  else {
555
560
  viewerNoKey = this.viewer;
556
561
  }
557
- // TODO: This is purely for debugging and we need to remove it.
562
+ const { visibleChunksChanged } = this.viewer.chunkQueueManager;
563
+ let firstChunkLoaded = false;
564
+ this.disposers.push(visibleChunksChanged.add(() => {
565
+ if (!firstChunkLoaded) {
566
+ for (const layer of this.viewer.layerManager.managedLayers) {
567
+ if (layer.layer instanceof SegmentationUserLayer) {
568
+ const hasVisibleChunk = layer.layer.renderLayers?.some((rl) => {
569
+ const { numVisibleChunksAvailable, numVisibleChunksNeeded, } = rl.layerChunkProgressInfo || {};
570
+ if (!numVisibleChunksNeeded || !numVisibleChunksAvailable)
571
+ return false;
572
+ // Neuroglancer only shows chunks when a certain % is loaded.
573
+ // The 0.25 is from testing different values, can be reduced to 0.2 to shorten loader time
574
+ return (numVisibleChunksAvailable / numVisibleChunksNeeded) > 0.25;
575
+ });
576
+ if (hasVisibleChunk) {
577
+ firstChunkLoaded = true;
578
+ // Two frames to avoid flash while the following two happens
579
+ // Neuroglancer issues WebGL draw calls
580
+ requestAnimationFrame(() => {
581
+ // GPU has painted, pixels visible on screen
582
+ requestAnimationFrame(() => {
583
+ this.props.onLayerLoadingChange?.(true);
584
+ });
585
+ });
586
+ return;
587
+ }
588
+ }
589
+ }
590
+ }
591
+ }));
592
+ this.disposers.push(() => { firstChunkLoaded = false; });
593
+ // TODO: This is purely for debugging - exposes the NG viewer to be tested via console
558
594
  // window.viewer = this.viewer;
559
595
  }
560
596
  componentDidUpdate(prevProps, prevState) {
@@ -594,6 +630,23 @@ export default class Neuroglancer extends React.Component {
594
630
  if (layer.layer instanceof SegmentationUserLayer) {
595
631
  const { segmentSelectionState } = layer.layer.displayState;
596
632
  segmentSelectionState.set(selectedSegments[layer.name]);
633
+ const layerScope = Object.keys(cellColorMappingByLayer).find(scope => layer.name?.includes(scope));
634
+ if (layerScope) {
635
+ const opacity = cellColorMappingByLayer[layerScope]?.opacity ?? 1.0;
636
+ layer.layer.displayState.objectAlpha.value = opacity;
637
+ }
638
+ }
639
+ // Update annotation layer shaders from viewerState config,
640
+ // skipping update if shader is unchanged to avoid costly re-renders
641
+ if (layer.layer instanceof AnnotationUserLayer) {
642
+ const matchingLayer = (viewerState?.layers || []).find(l => l.name === layer.name);
643
+ if (matchingLayer?.shader) {
644
+ /* eslint-disable-next-line no-underscore-dangle */
645
+ const currentShader = layer.layer.annotationDisplayState.shader.value_;
646
+ if (currentShader !== matchingLayer.shader) {
647
+ layer.layer.annotationDisplayState.shader.value = matchingLayer.shader;
648
+ }
649
+ }
597
650
  }
598
651
  }
599
652
  // For some reason setting position to an empty array doesn't reset
@@ -643,9 +696,11 @@ export default class Neuroglancer extends React.Component {
643
696
  // If colors changed (but layers didn’t): re-apply colors
644
697
  // this was to avid NG randomly assigning colors to the segments by resetting them
645
698
  const prevSize = prevProps.cellColorMapping
646
- ? Object.keys(prevProps.cellColorMapping).length : 0;
699
+ ? Object.values(prevProps.cellColorMapping)
700
+ .reduce((acc, v) => acc + Object.keys(v?.colors || {}).length, 0) : 0;
647
701
  const currSize = cellColorMappingByLayer
648
- ? Object.keys(cellColorMappingByLayer).length : 0;
702
+ ? Object.values(cellColorMappingByLayer)
703
+ .reduce((acc, v) => acc + Object.keys(v?.colors || {}).length, 0) : 0;
649
704
  const mappingRefChanged = prevProps.cellColorMapping !== this.props.cellColorMapping;
650
705
  if (!this.didLayersChange(prevVS, viewerState)
651
706
  && (mappingRefChanged || prevSize !== currSize)) {
@@ -658,7 +713,7 @@ export default class Neuroglancer extends React.Component {
658
713
  const stripSegFields = layers => (layers || []).map((l) => {
659
714
  if (!l)
660
715
  return l;
661
- const { segments, segmentColors, ...rest } = l;
716
+ const { segments, segmentColors, objectAlpha, ...rest } = l;
662
717
  return rest; // ignore segments + segmentColors for comparison
663
718
  });
664
719
  const prevLayers = prevProps.viewerState?.layers;
@@ -21,7 +21,7 @@ export function toNgLayerName(dataType: any, layerScope: any, channelScope?: nul
21
21
  /**
22
22
  * @returns [viewerState]
23
23
  */
24
- export function useNeuroglancerViewerState(theme: any, segmentationLayerScopes: any, segmentationChannelScopesByLayer: any, segmentationLayerCoordination: any, segmentationChannelCoordination: any, obsSegmentationsUrls: any, obsSegmentationsData: any, pointLayerScopes: any, pointLayerCoordination: any, obsPointsUrls: any, obsPointsData: any, pointMultiIndicesData: any): any;
24
+ export function useNeuroglancerViewerState(theme: any, showAxisLines: any, segmentationLayerScopes: any, segmentationChannelScopesByLayer: any, segmentationLayerCoordination: any, segmentationChannelCoordination: any, obsSegmentationsUrls: any, obsSegmentationsData: any, pointLayerScopes: any, pointLayerCoordination: any, obsPointsUrls: any, obsPointsData: any, pointMultiIndicesData: any): any;
25
25
  export namespace DEFAULT_NG_PROPS {
26
26
  let layout: string;
27
27
  let position: number[];
@@ -1 +1 @@
1
- {"version":3,"file":"data-hook-ng-utils.d.ts","sourceRoot":"","sources":["../src/data-hook-ng-utils.js"],"names":[],"mappings":"AAgDA;;;;KAIK;AACL,sDAHa,MAAM,GACJ;IAAE,CAAC,EAAC,CAAC,MAAM,EAAC,IAAI,CAAC,CAAC;IAAC,CAAC,EAAC,CAAC,MAAM,EAAC,IAAI,CAAC,CAAC;IAAC,CAAC,EAAC,CAAC,MAAM,EAAC,IAAI,CAAC,CAAA;CAAE,CA+BnE;AAED,2FAQC;AAED;;;;;;;;GAQG;AACH;;GAEG;AACH,yXAiJC"}
1
+ {"version":3,"file":"data-hook-ng-utils.d.ts","sourceRoot":"","sources":["../src/data-hook-ng-utils.js"],"names":[],"mappings":"AAgDA;;;;KAIK;AACL,sDAHa,MAAM,GACJ;IAAE,CAAC,EAAC,CAAC,MAAM,EAAC,IAAI,CAAC,CAAC;IAAC,CAAC,EAAC,CAAC,MAAM,EAAC,IAAI,CAAC,CAAC;IAAC,CAAC,EAAC,CAAC,MAAM,EAAC,IAAI,CAAC,CAAA;CAAE,CA+BnE;AAED,2FAQC;AAED;;;;;;;;GAQG;AACH;;GAEG;AACH,6YAoKC"}
@@ -88,7 +88,7 @@ export function toNgLayerName(dataType, layerScope, channelScope = null) {
88
88
  /**
89
89
  * @returns [viewerState]
90
90
  */
91
- export function useNeuroglancerViewerState(theme, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationLayerCoordination, segmentationChannelCoordination, obsSegmentationsUrls, obsSegmentationsData, pointLayerScopes, pointLayerCoordination, obsPointsUrls, obsPointsData, pointMultiIndicesData) {
91
+ export function useNeuroglancerViewerState(theme, showAxisLines, segmentationLayerScopes, segmentationChannelScopesByLayer, segmentationLayerCoordination, segmentationChannelCoordination, obsSegmentationsUrls, obsSegmentationsData, pointLayerScopes, pointLayerCoordination, obsPointsUrls, obsPointsData, pointMultiIndicesData) {
92
92
  const viewerState = useMemoCustomComparison(() => {
93
93
  let result = cloneDeep(DEFAULT_NG_PROPS);
94
94
  // ======= SEGMENTATIONS =======
@@ -103,20 +103,32 @@ export function useNeuroglancerViewerState(theme, segmentationLayerScopes, segme
103
103
  channelScopes.forEach((channelScope) => {
104
104
  const channelCoordination = segmentationChannelCoordination[0]?.[layerScope]?.[channelScope];
105
105
  const { spatialChannelVisible, } = channelCoordination || {};
106
+ const { source: ngSource, ...otherNgOptions } = layerData.neuroglancerOptions ?? {};
107
+ // Build source: if neuroglancerOptions has subsources
108
+ const hasNgSourceOptions = layerData.neuroglancerOptions?.subsources
109
+ || layerData.neuroglancerOptions?.enableDefaultSubsources !== undefined;
110
+ const source = hasNgSourceOptions
111
+ ? {
112
+ url: toPrecomputedSource(layerUrl),
113
+ subsources: layerData.neuroglancerOptions.subsources,
114
+ enableDefaultSubsources: layerData.neuroglancerOptions.enableDefaultSubsources
115
+ ?? false,
116
+ }
117
+ : toPrecomputedSource(layerUrl);
106
118
  result = {
107
119
  ...result,
108
120
  layers: [
109
121
  ...result.layers,
110
122
  {
111
123
  type: 'segmentation',
112
- source: toPrecomputedSource(layerUrl),
124
+ source,
113
125
  segments: [],
114
126
  name: toNgLayerName(DataType.OBS_SEGMENTATIONS, layerScope, channelScope),
115
127
  visible: spatialLayerVisible && spatialChannelVisible, // Both layer and channel
116
128
  // visibility must be true for the layer to be visible.
117
129
  // TODO: update this to extract specific properties from
118
130
  // neuroglancerOptions as needed.
119
- ...(layerData.neuroglancerOptions ?? {}),
131
+ ...otherNgOptions,
120
132
  },
121
133
  ],
122
134
  };
@@ -131,7 +143,7 @@ export function useNeuroglancerViewerState(theme, segmentationLayerScopes, segme
131
143
  const layerUrl = obsPointsUrls[layerScope]?.[0]?.url;
132
144
  const featureIndex = pointMultiIndicesData[layerScope]?.featureIndex;
133
145
  if (layerUrl && layerData) {
134
- const { spatialLayerVisible, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, } = layerCoordination || {};
146
+ const { spatialLayerVisible, spatialLayerOpacity, obsColorEncoding, spatialLayerColor, featureSelection, featureFilterMode, featureColor, spatialPointStrokeWidth, } = layerCoordination || {};
135
147
  // Dynamically construct the shader based on the color encoding
136
148
  // and other coordination values.
137
149
  const shader = getPointsShader({
@@ -145,6 +157,7 @@ export function useNeuroglancerViewerState(theme, segmentationLayerScopes, segme
145
157
  featureColor,
146
158
  featureIndexProp: layerData.neuroglancerOptions?.featureIndexProp,
147
159
  pointIndexProp: layerData.neuroglancerOptions?.pointIndexProp,
160
+ pointMarkerBorderWidth: spatialPointStrokeWidth ?? 0.0,
148
161
  });
149
162
  result = {
150
163
  ...result,
@@ -182,6 +195,7 @@ export function useNeuroglancerViewerState(theme, segmentationLayerScopes, segme
182
195
  return result;
183
196
  }, {
184
197
  theme,
198
+ showAxisLines,
185
199
  segmentationLayerScopes,
186
200
  segmentationChannelScopesByLayer,
187
201
  segmentationLayerCoordination,
@@ -5,7 +5,7 @@
5
5
  * @param {number} opacity Opacity (0-1).
6
6
  * @returns {string} A GLSL shader string.
7
7
  */
8
- export function getSpatialLayerColorShader(staticColor: [number, number, number], opacity: number): string;
8
+ export function getSpatialLayerColorShader(staticColor: [number, number, number], opacity: number, borderWidth?: number): string;
9
9
  /**
10
10
  * Generate a shader for spatialLayerColor encoding with feature selection.
11
11
  * Selected features get the static color; unselected get the default color.
@@ -16,7 +16,7 @@ export function getSpatialLayerColorShader(staticColor: [number, number, number]
16
16
  * @param {string} featureIndexProp The property name for the feature index in the shader.
17
17
  * @returns {string} A GLSL shader string.
18
18
  */
19
- export function getSpatialLayerColorWithSelectionShader(staticColor: [number, number, number], opacity: number, featureIndices: number[], defaultColor: [number, number, number], featureIndexProp: string): string;
19
+ export function getSpatialLayerColorWithSelectionShader(staticColor: [number, number, number], opacity: number, featureIndices: number[], defaultColor: [number, number, number], featureIndexProp: string, borderWidth?: number): string;
20
20
  /**
21
21
  * Generate a shader for spatialLayerColor encoding with feature selection
22
22
  * and featureFilterMode='featureSelection' (hide unselected points).
@@ -26,7 +26,7 @@ export function getSpatialLayerColorWithSelectionShader(staticColor: [number, nu
26
26
  * @param {string} featureIndexProp The property name for the feature index in the shader.
27
27
  * @returns {string} A GLSL shader string.
28
28
  */
29
- export function getSpatialLayerColorFilteredShader(staticColor: [number, number, number], opacity: number, featureIndices: number[], featureIndexProp: string): string;
29
+ export function getSpatialLayerColorFilteredShader(staticColor: [number, number, number], opacity: number, featureIndices: number[], featureIndexProp: string, borderWidth?: number): string;
30
30
  /**
31
31
  * Generate a shader for geneSelection encoding with no feature selection.
32
32
  * All points get the static color (since no features are selected to
@@ -35,7 +35,7 @@ export function getSpatialLayerColorFilteredShader(staticColor: [number, number,
35
35
  * @param {number} opacity Opacity (0-1).
36
36
  * @returns {string} A GLSL shader string.
37
37
  */
38
- export function getGeneSelectionNoSelectionShader(staticColor: [number, number, number], opacity: number): string;
38
+ export function getGeneSelectionNoSelectionShader(staticColor: [number, number, number], opacity: number, borderWidth?: number): string;
39
39
  /**
40
40
  * Generate a shader for geneSelection encoding with feature selection.
41
41
  * Each selected feature gets its color from featureColor; unselected
@@ -51,7 +51,7 @@ export function getGeneSelectionNoSelectionShader(staticColor: [number, number,
51
51
  * @param {string} featureIndexProp The property name for the feature index in the shader.
52
52
  * @returns {string} A GLSL shader string.
53
53
  */
54
- export function getGeneSelectionWithSelectionShader(featureIndices: number[], featureColors: [number, number, number][], staticColor: [number, number, number], defaultColor: [number, number, number], opacity: number, featureIndexProp: string): string;
54
+ export function getGeneSelectionWithSelectionShader(featureIndices: number[], featureColors: [number, number, number][], staticColor: [number, number, number], defaultColor: [number, number, number], opacity: number, featureIndexProp: string, borderWidth?: number): string;
55
55
  /**
56
56
  * Generate a shader for geneSelection encoding with feature selection
57
57
  * and featureFilterMode='featureSelection' (hide unselected points).
@@ -63,7 +63,7 @@ export function getGeneSelectionWithSelectionShader(featureIndices: number[], fe
63
63
  * @param {string} featureIndexProp The property name for the feature index in the shader.
64
64
  * @returns {string} A GLSL shader string.
65
65
  */
66
- export function getGeneSelectionFilteredShader(featureIndices: number[], featureColors: [number, number, number][], staticColor: [number, number, number], opacity: number, featureIndexProp: string): string;
66
+ export function getGeneSelectionFilteredShader(featureIndices: number[], featureColors: [number, number, number][], staticColor: [number, number, number], opacity: number, featureIndexProp: string, borderWidth?: number): string;
67
67
  /**
68
68
  * Generate a shader for randomByFeature encoding with no feature selection.
69
69
  * Each feature gets a deterministic color from PALETTE based on its index.
@@ -71,7 +71,7 @@ export function getGeneSelectionFilteredShader(featureIndices: number[], feature
71
71
  * @param {string} featureIndexProp The property name for the feature index in the shader.
72
72
  * @returns {string} A GLSL shader string.
73
73
  */
74
- export function getRandomByFeatureShader(opacity: number, featureIndexProp: string): string;
74
+ export function getRandomByFeatureShader(opacity: number, featureIndexProp: string, borderWidth?: number): string;
75
75
  /**
76
76
  * Generate a shader for randomByFeature encoding with feature selection.
77
77
  * Selected features get their deterministic palette color; unselected
@@ -82,7 +82,7 @@ export function getRandomByFeatureShader(opacity: number, featureIndexProp: stri
82
82
  * @param {string} featureIndexProp The property name for the feature index in the shader.
83
83
  * @returns {string} A GLSL shader string.
84
84
  */
85
- export function getRandomByFeatureWithSelectionShader(featureIndices: number[], defaultColor: [number, number, number], opacity: number, featureIndexProp: string): string;
85
+ export function getRandomByFeatureWithSelectionShader(featureIndices: number[], defaultColor: [number, number, number], opacity: number, featureIndexProp: string, borderWidth?: number): string;
86
86
  /**
87
87
  * Generate a shader for randomByFeature encoding with feature selection
88
88
  * and featureFilterMode='featureSelection' (hide unselected points).
@@ -91,7 +91,7 @@ export function getRandomByFeatureWithSelectionShader(featureIndices: number[],
91
91
  * @param {string} featureIndexProp The property name for the feature index in the shader.
92
92
  * @returns {string} A GLSL shader string.
93
93
  */
94
- export function getRandomByFeatureFilteredShader(featureIndices: number[], opacity: number, featureIndexProp: string): string;
94
+ export function getRandomByFeatureFilteredShader(featureIndices: number[], opacity: number, featureIndexProp: string, borderWidth?: number): string;
95
95
  /**
96
96
  * Generate a shader for random-per-point encoding with no feature selection.
97
97
  * Each point gets a pseudo-random color based on its index.
@@ -100,7 +100,7 @@ export function getRandomByFeatureFilteredShader(featureIndices: number[], opaci
100
100
  * @param {string} pointIndexProp The property name for the point index in the shader.
101
101
  * @returns {string} A GLSL shader string.
102
102
  */
103
- export function getRandomPerPointShader(opacity: number, featureIndexProp: string, pointIndexProp: string): string;
103
+ export function getRandomPerPointShader(opacity: number, featureIndexProp: string, pointIndexProp: string, borderWidth?: number): string;
104
104
  /**
105
105
  * Generate a shader for random-per-point encoding with feature selection.
106
106
  * Selected points get a pseudo-random color; unselected get the default color.
@@ -111,7 +111,7 @@ export function getRandomPerPointShader(opacity: number, featureIndexProp: strin
111
111
  * @param {string} pointIndexProp The property name for the point index in the shader.
112
112
  * @returns {string} A GLSL shader string.
113
113
  */
114
- export function getRandomPerPointWithSelectionShader(featureIndices: number[], defaultColor: [number, number, number], opacity: number, featureIndexProp: string, pointIndexProp: string): string;
114
+ export function getRandomPerPointWithSelectionShader(featureIndices: number[], defaultColor: [number, number, number], opacity: number, featureIndexProp: string, pointIndexProp: string, borderWidth?: number): string;
115
115
  /**
116
116
  * Generate a shader for random-per-point encoding with feature selection
117
117
  * and featureFilterMode='featureSelection' (hide unselected points).
@@ -121,6 +121,6 @@ export function getRandomPerPointWithSelectionShader(featureIndices: number[], d
121
121
  * @param {string} pointIndexProp The property name for the point index in the shader.
122
122
  * @returns {string} A GLSL shader string.
123
123
  */
124
- export function getRandomPerPointFilteredShader(featureIndices: number[], opacity: number, featureIndexProp: string, pointIndexProp: string): string;
124
+ export function getRandomPerPointFilteredShader(featureIndices: number[], opacity: number, featureIndexProp: string, pointIndexProp: string, borderWidth?: number): string;
125
125
  export function getPointsShader(layerCoordination: any): string;
126
126
  //# sourceMappingURL=shader-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shader-utils.d.ts","sourceRoot":"","sources":["../src/shader-utils.js"],"names":[],"mappings":"AAwCA;;;;;;GAMG;AACH,wDAJW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,GACJ,MAAM,CAUlB;AAGD;;;;;;;;;GASG;AACH,qEAPW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,kBACN,MAAM,EAAE,gBACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,oBACxB,MAAM,GACJ,MAAM,CA4BlB;AAGD;;;;;;;;GAQG;AACH,gEANW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,kBACN,MAAM,EAAE,oBACR,MAAM,GACJ,MAAM,CA0BlB;AAMD;;;;;;;GAOG;AACH,+DAJW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;;;;;;;;;GAcG;AACH,oEAXW,MAAM,EAAE,iBACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAE1B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,gBAExB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WAExB,MAAM,oBACN,MAAM,GACJ,MAAM,CA+BlB;AAED;;;;;;;;;;GAUG;AACH,+DARW,MAAM,EAAE,iBACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAE1B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,oBACN,MAAM,GACJ,MAAM,CAoClB;AAMD;;;;;;GAMG;AACH,kDAJW,MAAM,oBACN,MAAM,GACJ,MAAM,CAkBlB;AAED;;;;;;;;;GASG;AACH,sEANW,MAAM,EAAE,gBACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,oBACN,MAAM,GACJ,MAAM,CAkClB;AAED;;;;;;;GAOG;AACH,iEALW,MAAM,EAAE,WACR,MAAM,oBACN,MAAM,GACJ,MAAM,CA8BlB;AAuBD;;;;;;;GAOG;AACH,iDALW,MAAM,oBACN,MAAM,kBACN,MAAM,GACJ,MAAM,CAelB;AAED;;;;;;;;;GASG;AACH,qEAPW,MAAM,EAAE,gBACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,oBACN,MAAM,kBACN,MAAM,GACJ,MAAM,CAgClB;AAED;;;;;;;;GAQG;AACH,gEANW,MAAM,EAAE,WACR,MAAM,oBACN,MAAM,kBACN,MAAM,GACJ,MAAM,CA8BlB;AAGD,gEAwKC"}
1
+ {"version":3,"file":"shader-utils.d.ts","sourceRoot":"","sources":["../src/shader-utils.js"],"names":[],"mappings":"AAoDA;;;;;;GAMG;AACH,wDAJW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,yBACJ,MAAM,CAWlB;AAGD;;;;;;;;;GASG;AACH,qEAPW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,kBACN,MAAM,EAAE,gBACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,oBACxB,MAAM,yBACJ,MAAM,CA8BlB;AAGD;;;;;;;;GAQG;AACH,gEANW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,kBACN,MAAM,EAAE,oBACR,MAAM,yBACJ,MAAM,CA2BlB;AAMD;;;;;;;GAOG;AACH,+DAJW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,yBACJ,MAAM,CAWlB;AAED;;;;;;;;;;;;;;GAcG;AACH,oEAXW,MAAM,EAAE,iBACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAE1B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,gBAExB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WAExB,MAAM,oBACN,MAAM,yBACJ,MAAM,CAsClB;AAED;;;;;;;;;;GAUG;AACH,+DARW,MAAM,EAAE,iBACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAE1B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,oBACN,MAAM,yBACJ,MAAM,CAqClB;AAMD;;;;;;GAMG;AACH,kDAJW,MAAM,oBACN,MAAM,yBACJ,MAAM,CAmBlB;AAED;;;;;;;;;GASG;AACH,sEANW,MAAM,EAAE,gBACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,oBACN,MAAM,yBACJ,MAAM,CAoClB;AAED;;;;;;;GAOG;AACH,iEALW,MAAM,EAAE,WACR,MAAM,oBACN,MAAM,yBACJ,MAAM,CAoClB;AAuBD;;;;;;;GAOG;AACH,iDALW,MAAM,oBACN,MAAM,kBACN,MAAM,yBACJ,MAAM,CAqBlB;AAED;;;;;;;;;GASG;AACH,qEAPW,MAAM,EAAE,gBACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,WACxB,MAAM,oBACN,MAAM,kBACN,MAAM,yBACJ,MAAM,CAkClB;AAED;;;;;;;;GAQG;AACH,gEANW,MAAM,EAAE,WACR,MAAM,oBACN,MAAM,kBACN,MAAM,yBACJ,MAAM,CA+BlB;AAGD,gEAkLC"}