@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.
- package/dist/{ReactNeuroglancer-pv4bM8Yp.js → ReactNeuroglancer-Bxe4YcLF.js} +64 -9
- package/dist/{index-BEPd2Tds.js → index-anGvS-pL.js} +120 -46
- package/dist/index.js +1 -1
- package/dist-tsc/Neuroglancer.d.ts.map +1 -1
- package/dist-tsc/Neuroglancer.js +2 -2
- package/dist-tsc/NeuroglancerSubscriber.d.ts.map +1 -1
- package/dist-tsc/NeuroglancerSubscriber.js +22 -6
- package/dist-tsc/ReactNeuroglancer.d.ts +11 -0
- package/dist-tsc/ReactNeuroglancer.d.ts.map +1 -1
- package/dist-tsc/ReactNeuroglancer.js +60 -5
- package/dist-tsc/data-hook-ng-utils.d.ts +1 -1
- package/dist-tsc/data-hook-ng-utils.d.ts.map +1 -1
- package/dist-tsc/data-hook-ng-utils.js +18 -4
- package/dist-tsc/shader-utils.d.ts +12 -12
- package/dist-tsc/shader-utils.d.ts.map +1 -1
- package/dist-tsc/shader-utils.js +51 -26
- package/dist-tsc/shader-utils.test.js +20 -0
- package/dist-tsc/use-memo-custom-comparison.d.ts.map +1 -1
- package/dist-tsc/use-memo-custom-comparison.js +5 -0
- package/package.json +9 -9
- package/src/Neuroglancer.js +2 -1
- package/src/NeuroglancerSubscriber.js +28 -4
- package/src/ReactNeuroglancer.js +67 -5
- package/src/data-hook-ng-utils.js +21 -2
- package/src/shader-utils.js +79 -26
- package/src/shader-utils.test.js +20 -0
- package/src/use-memo-custom-comparison.js +6 -0
|
@@ -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] =
|
|
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,
|
|
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
|
|
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
|
-
|
|
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.
|
|
699
|
+
? Object.values(prevProps.cellColorMapping)
|
|
700
|
+
.reduce((acc, v) => acc + Object.keys(v?.colors || {}).length, 0) : 0;
|
|
647
701
|
const currSize = cellColorMappingByLayer
|
|
648
|
-
? Object.
|
|
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,
|
|
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
|
|
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
|
-
...
|
|
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":"
|
|
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"}
|