@aics/vole-core 4.1.0 → 4.3.0

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/es/ImageInfo.js CHANGED
@@ -6,7 +6,7 @@ export function defaultImageInfo() {
6
6
  atlasTileDims: [1, 1],
7
7
  subregionSize: [1, 1, 1],
8
8
  subregionOffset: [0, 0, 0],
9
- combinedNumChannels: 1,
9
+ numChannelsPerSource: [1],
10
10
  channelNames: ["0"],
11
11
  channelColors: [[255, 255, 255]],
12
12
  multiscaleLevel: 0,
@@ -34,7 +34,12 @@ export class CImageInfo {
34
34
 
35
35
  /** Number of channels in the image */
36
36
  get numChannels() {
37
- return this.imageInfo.combinedNumChannels;
37
+ return this.imageInfo.numChannelsPerSource.reduce((a, b) => a + b, 0);
38
+ }
39
+
40
+ /** Number of channels per source, ordered by source index */
41
+ get numChannelsPerSource() {
42
+ return this.imageInfo.numChannelsPerSource;
38
43
  }
39
44
 
40
45
  /** XYZ size of the *original* (not downsampled) volume, in pixels */
package/es/MeshVolume.js CHANGED
@@ -19,6 +19,10 @@ export default class MeshVolume {
19
19
  this.meshRoot = new Object3D(); //create an empty container
20
20
  this.meshRoot.name = "Mesh Surface Group";
21
21
 
22
+ // compensating for generated isosurface vertex coordinates
23
+ // arguably this could be set on meshPivot
24
+ this.meshRoot.scale.setScalar(0.5);
25
+
22
26
  // handle transform ordering for giving the meshroot a rotation about a pivot point
23
27
  this.meshPivot = new Group();
24
28
  this.meshPivot.name = "MeshContainerNode";
package/es/View3d.js CHANGED
@@ -928,12 +928,12 @@ export class View3d {
928
928
  color: {
929
929
  type: "float"
930
930
  }
931
- });
931
+ }).on("change", _event => this.redraw());
932
932
  folder.addInput(light, "intensity", {
933
933
  min: 0
934
- });
934
+ }).on("change", _event => this.redraw());
935
935
  if (!light.isAmbientLight) {
936
- folder.addInput(light, "position");
936
+ folder.addInput(light, "position").on("change", _event => this.redraw());
937
937
  }
938
938
  };
939
939
  addFolderForLight(this.spotLight, "spot light");
package/es/Volume.js CHANGED
@@ -72,7 +72,7 @@ export default class Volume {
72
72
  this.setVoxelSize(this.physicalPixelSize);
73
73
  this.numChannels = this.imageInfo.numChannels;
74
74
  this.channelNames = this.imageInfo.channelNames.slice();
75
- this.channelColorsDefault = this.imageInfo.channelColors ? this.imageInfo.channelColors.slice() : this.channelNames.map((name, index) => getColorByChannelIndex(index));
75
+ this.channelColorsDefault = this.imageInfo.channelColors ? this.imageInfo.channelColors.map((color, index) => color ?? getColorByChannelIndex(index)) : this.channelNames.map((name, index) => getColorByChannelIndex(index));
76
76
  // fill in gaps
77
77
  if (this.channelColorsDefault.length < this.imageInfo.numChannels) {
78
78
  for (let i = this.channelColorsDefault.length - 1; i < this.imageInfo.numChannels; ++i) {
@@ -45,7 +45,9 @@ export default class VolumeDrawable {
45
45
  this.viewMode = Axis.NONE; // 3D mode
46
46
 
47
47
  this.channelColors = this.volume.channelColorsDefault.slice();
48
- this.channelOptions = new Array(this.volume.imageInfo.numChannels).fill({});
48
+ this.channelOptions = Array.from({
49
+ length: this.volume.imageInfo.numChannels
50
+ }, () => ({}));
49
51
  this.fusion = this.channelColors.map((col, index) => {
50
52
  let rgbColor;
51
53
  // take copy of original channel color
@@ -709,7 +711,7 @@ export default class VolumeDrawable {
709
711
 
710
712
  // remove old 3d object from scene
711
713
  if (this.renderMode === RenderMode.SLICE || this.renderMode === RenderMode.RAYMARCH) {
712
- this.sceneRoot.remove(this.meshVolume.get3dObject());
714
+ this.childObjectsGroup.remove(this.meshVolume.get3dObject());
713
715
  }
714
716
  this.sceneRoot.remove(this.volumeRendering.get3dObject());
715
717
 
@@ -745,7 +747,7 @@ export default class VolumeDrawable {
745
747
  if (this.renderUpdateListener) {
746
748
  this.renderUpdateListener(0);
747
749
  }
748
- this.sceneRoot.add(this.meshVolume.get3dObject());
750
+ this.childObjectsGroup.add(this.meshVolume.get3dObject());
749
751
  }
750
752
 
751
753
  // add new 3d object to scene
@@ -3,7 +3,7 @@ const spotlightSettings = Object.freeze({
3
3
  angle: 36 * MathUtils.DEG2RAD,
4
4
  castShadow: false,
5
5
  color: 0xffffff,
6
- intensity: 0.4,
6
+ intensity: 15.0,
7
7
  position: {
8
8
  x: -4,
9
9
  y: 3.5,
@@ -12,12 +12,12 @@ const spotlightSettings = Object.freeze({
12
12
  });
13
13
  const ambientLightSettings = Object.freeze({
14
14
  color: 0xffffff,
15
- intensity: 0.6
15
+ intensity: 1.75
16
16
  });
17
17
  const reflectedLightSettings = Object.freeze({
18
18
  castShadow: false,
19
19
  color: 0xff88aa,
20
- intensity: 0.2,
20
+ intensity: 2.0,
21
21
  position: {
22
22
  x: 1,
23
23
  y: -5,
@@ -27,7 +27,7 @@ const reflectedLightSettings = Object.freeze({
27
27
  const fillLightSettings = Object.freeze({
28
28
  castShadow: false,
29
29
  color: 0xe8d1a9,
30
- intensity: 0.15,
30
+ intensity: 1.5,
31
31
  position: {
32
32
  x: 2.5,
33
33
  y: 0.2,
@@ -29,7 +29,7 @@ const convertImageInfo = json => {
29
29
  atlasTileDims: [json.cols, json.rows],
30
30
  subregionSize: [json.tile_width, json.tile_height, json.tiles],
31
31
  subregionOffset: [0, 0, 0],
32
- combinedNumChannels: json.channels,
32
+ numChannelsPerSource: json.images.map(image => image.channels.length),
33
33
  channelNames: json.channel_names,
34
34
  channelColors: json.channel_colors,
35
35
  multiscaleLevel: 0,
@@ -7,7 +7,7 @@ import SubscribableRequestQueue from "../utils/SubscribableRequestQueue.js";
7
7
  import { ThreadableVolumeLoader } from "./IVolumeLoader.js";
8
8
  import { composeSubregion, computePackedAtlasDims, convertSubregionToPixels, pickLevelToLoad, unitNameToSymbol } from "./VolumeLoaderUtils.js";
9
9
  import ChunkPrefetchIterator from "./zarr_utils/ChunkPrefetchIterator.js";
10
- import { getScale, getSourceChannelNames, matchSourceScaleLevels, orderByDimension, orderByTCZYX, remapAxesToTCZYX } from "./zarr_utils/utils.js";
10
+ import { getScale, getSourceChannelMeta, matchSourceScaleLevels, orderByDimension, orderByTCZYX, remapAxesToTCZYX } from "./zarr_utils/utils.js";
11
11
  import { VolumeLoadError, VolumeLoadErrorType, wrapVolumeLoadError } from "./VolumeLoadError.js";
12
12
  import wrapArray, { RelaxedFetchStore } from "./zarr_utils/wrappers.js";
13
13
  import { assertMetadataHasMultiscales, toOMEZarrMetaV4, validateOMEZarrMetadata } from "./zarr_utils/validation.js";
@@ -247,12 +247,14 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
247
247
  const levelToLoad = pickLevelToLoad(loadSpec, this.getLevelShapesZYX());
248
248
  const shapeLv = source0.scaleLevels[levelToLoad].shape;
249
249
  const [spatialUnit, timeUnit] = this.getUnitSymbols();
250
+ const numChannelsPerSource = [];
251
+ for (let i = 0; i < this.sources.length; i++) {
252
+ const source = this.sources[i];
253
+ const cIndex = source.axesTCZYX[1];
254
+ const sourceChannelCount = cIndex > -1 ? source.scaleLevels[levelToLoad].shape[cIndex] : 1;
255
+ numChannelsPerSource.push(sourceChannelCount);
256
+ }
250
257
 
251
- // Now we care about other sources: # of channels is the `channelOffset` of the last source plus its # of channels
252
- const sourceLast = this.sources[this.sources.length - 1];
253
- const cLast = sourceLast.axesTCZYX[1];
254
- const lastHasC = cLast > -1;
255
- const numChannels = sourceLast.channelOffset + (lastHasC ? sourceLast.scaleLevels[levelToLoad].shape[cLast] : 1);
256
258
  // we need to make sure that the corresponding matched shapes
257
259
  // use the min size of T
258
260
  let times = 1;
@@ -279,11 +281,16 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
279
281
  // Channel names is the other place where we have to check every source
280
282
  // Track which channel names we've seen so far, so that we can rename them to avoid name collisions
281
283
  const channelNamesMap = new Map();
282
- const channelNames = this.sources.flatMap(src => {
283
- const sourceChannelNames = getSourceChannelNames(src);
284
+ const channelNames = [];
285
+ const channelColors = [];
286
+ for (const src of this.sources) {
287
+ const {
288
+ names,
289
+ colors
290
+ } = getSourceChannelMeta(src);
284
291
 
285
292
  // Resolve name collisions
286
- return sourceChannelNames.map(channelName => {
293
+ const resolvedNames = names.map(channelName => {
287
294
  const numMatchingChannels = channelNamesMap.get(channelName);
288
295
  if (numMatchingChannels !== undefined) {
289
296
  // If e.g. we've seen channel "Membrane" once before, rename this one to "Membrane (1)"
@@ -294,7 +301,9 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
294
301
  return channelName;
295
302
  }
296
303
  });
297
- });
304
+ channelNames.push(...resolvedNames);
305
+ channelColors.push(...colors);
306
+ }
298
307
  const alldims = source0.scaleLevels.map((level, i) => {
299
308
  const dims = {
300
309
  spaceUnit: spatialUnit,
@@ -310,8 +319,9 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
310
319
  atlasTileDims: [atlasTileDims.x, atlasTileDims.y],
311
320
  subregionSize: [pxSizeLv.x, pxSizeLv.y, pxSizeLv.z],
312
321
  subregionOffset: [0, 0, 0],
313
- combinedNumChannels: numChannels,
322
+ numChannelsPerSource,
314
323
  channelNames,
324
+ channelColors,
315
325
  multiscaleLevel: levelToLoad,
316
326
  multiscaleLevelDims: alldims,
317
327
  transform: {
@@ -423,9 +433,10 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
423
433
  const updatedImageInfo = this.updateImageInfoForLoad(imageInfo, loadSpec);
424
434
  onUpdateMetadata(updatedImageInfo);
425
435
  const {
426
- combinedNumChannels,
436
+ numChannelsPerSource,
427
437
  multiscaleLevel
428
438
  } = updatedImageInfo;
439
+ const combinedNumChannels = numChannelsPerSource.reduce((a, b) => a + b, 0);
429
440
  const channelIndexes = loadSpec.channels ?? Array.from({
430
441
  length: combinedNumChannels
431
442
  }, (_, i) => i);
@@ -24,7 +24,7 @@ class OpenCellLoader extends ThreadableVolumeLoader {
24
24
  atlasTileDims: [27, 1],
25
25
  subregionSize: [600, 600, 27],
26
26
  subregionOffset: [0, 0, 0],
27
- combinedNumChannels: numChannels,
27
+ numChannelsPerSource: [numChannels],
28
28
  channelNames: chnames,
29
29
  multiscaleLevel: 0,
30
30
  multiscaleLevelDims: [{
@@ -37,7 +37,7 @@ const convertImageInfo = (json, dtype) => {
37
37
  atlasTileDims: [atlasTileDims.x, atlasTileDims.y],
38
38
  subregionSize: [json.sizeX, json.sizeY, json.sizeZ],
39
39
  subregionOffset: [0, 0, 0],
40
- combinedNumChannels: json.sizeC,
40
+ numChannelsPerSource: [json.sizeC],
41
41
  channelNames: json.channelNames,
42
42
  channelColors: undefined,
43
43
  multiscaleLevel: 0,
@@ -92,7 +92,8 @@ class RawArrayLoader extends ThreadableVolumeLoader {
92
92
  multiscaleLevel: 0
93
93
  };
94
94
  onUpdateMetadata(undefined, adjustedLoadSpec);
95
- for (let chindex = 0; chindex < imageInfo.combinedNumChannels; ++chindex) {
95
+ const totalChannels = imageInfo.numChannelsPerSource.reduce((a, b) => a + b, 0);
96
+ for (let chindex = 0; chindex < totalChannels; ++chindex) {
96
97
  if (requestedChannels && requestedChannels.length > 0 && !requestedChannels.includes(chindex)) {
97
98
  continue;
98
99
  }
@@ -157,13 +157,13 @@ class TiffLoader extends ThreadableVolumeLoader {
157
157
  const tilesizey = Math.floor(targetSize / atlasDims.y);
158
158
 
159
159
  // load tiff and check metadata
160
-
160
+ const numChannelsPerSource = this.url.length > 1 ? Array(this.url.length).fill(1) : [dims.sizec];
161
161
  const imgdata = {
162
162
  name: "TEST",
163
163
  atlasTileDims: [atlasDims.x, atlasDims.y],
164
164
  subregionSize: [tilesizex, tilesizey, dims.sizez],
165
165
  subregionOffset: [0, 0, 0],
166
- combinedNumChannels: dims.sizec,
166
+ numChannelsPerSource,
167
167
  channelNames: dims.channelnames,
168
168
  multiscaleLevel: 0,
169
169
  multiscaleLevelDims: [{
@@ -194,41 +194,44 @@ class TiffLoader extends ThreadableVolumeLoader {
194
194
  const volumeSize = cimageinfo.volumeSize;
195
195
  const channelProms = [];
196
196
  // do each channel on a worker?
197
- for (let channel = 0; channel < imageInfo.combinedNumChannels; ++channel) {
198
- const thisChannelProm = new Promise((resolve, reject) => {
199
- const params = {
200
- channel: channel,
201
- // these are target xy sizes for the in-memory volume data
202
- // they may or may not be the same size as original xy sizes
203
- tilesizex: volumeSize.x,
204
- tilesizey: volumeSize.y,
205
- sizec: imageInfo.combinedNumChannels,
206
- sizez: volumeSize.z,
207
- dimensionOrder: dims.dimensionorder,
208
- bytesPerSample: getBytesPerSample(dims.pixeltype),
209
- url: this.url.length > 1 ? this.url[channel] : this.url[0] // if multiple urls, use the channel index to select the right one
210
- };
211
- const worker = new Worker(new URL("../workers/FetchTiffWorker", import.meta.url), {
212
- type: "module"
197
+ for (let source = 0; source < imageInfo.numChannelsPerSource.length; ++source) {
198
+ const numChannels = imageInfo.numChannelsPerSource[source];
199
+ for (let channel = 0; channel < numChannels; ++channel) {
200
+ const thisChannelProm = new Promise((resolve, reject) => {
201
+ const params = {
202
+ channel: channel,
203
+ // these are target xy sizes for the in-memory volume data
204
+ // they may or may not be the same size as original xy sizes
205
+ tilesizex: volumeSize.x,
206
+ tilesizey: volumeSize.y,
207
+ sizec: numChannels,
208
+ sizez: volumeSize.z,
209
+ dimensionOrder: dims.dimensionorder,
210
+ bytesPerSample: getBytesPerSample(dims.pixeltype),
211
+ url: this.url[source]
212
+ };
213
+ const worker = new Worker(new URL("../workers/FetchTiffWorker", import.meta.url), {
214
+ type: "module"
215
+ });
216
+ worker.onmessage = e => {
217
+ if (e.data.isError) {
218
+ reject(deserializeError(e.data.error));
219
+ return;
220
+ }
221
+ const {
222
+ data,
223
+ dtype,
224
+ channel,
225
+ range
226
+ } = e.data;
227
+ onData([channel], [dtype], [data], [range]);
228
+ worker.terminate();
229
+ resolve();
230
+ };
231
+ worker.postMessage(params);
213
232
  });
214
- worker.onmessage = e => {
215
- if (e.data.isError) {
216
- reject(deserializeError(e.data.error));
217
- return;
218
- }
219
- const {
220
- data,
221
- dtype,
222
- channel,
223
- range
224
- } = e.data;
225
- onData([channel], [dtype], [data], [range]);
226
- worker.terminate();
227
- resolve();
228
- };
229
- worker.postMessage(params);
230
- });
231
- channelProms.push(thisChannelProm);
233
+ channelProms.push(thisChannelProm);
234
+ }
232
235
  }
233
236
 
234
237
  // waiting for all channels to load allows errors to propagate to the caller via this promise
@@ -211,7 +211,8 @@ export function buildDefaultMetadata(rawImageInfo) {
211
211
  };
212
212
  metadata["Multiresolution levels"] = rawImageInfo.multiscaleLevelDims;
213
213
  // TODO decide???? combined or not?
214
- metadata["Channels"] = rawImageInfo.combinedNumChannels; //imageInfo.numChannels;
214
+ const totalChannels = imageInfo.numChannelsPerSource.reduce((a, b) => a + b, 0);
215
+ metadata["Channels"] = totalChannels;
215
216
  metadata["Time series frames"] = imageInfo.times || 1;
216
217
  // don't add User data if it's empty
217
218
  if (rawImageInfo.userData && !isEmpty(rawImageInfo.userData)) {
@@ -1,16 +1,51 @@
1
1
  import { VolumeLoadErrorType, VolumeLoadError } from "../VolumeLoadError.js";
2
+ /**
3
+ * Attempts to parse `color` as a 24-bit (6-digit) hexadecimal color with a possible leading `#`.
4
+ *
5
+ * Six-digit hex is the only allowable color representation in the OMERO metadata spec.
6
+ */
7
+ export function parseHexColor(color) {
8
+ if (color === undefined) {
9
+ return undefined;
10
+ }
11
+ const result = /^#?([a-fA-F\d]{2})([a-fA-F\d]{2})([a-fA-F\d]{2})$/i.exec(color);
12
+ if (result) {
13
+ return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
14
+ } else {
15
+ return undefined;
16
+ }
17
+ }
18
+
2
19
  /** Extracts channel names from a `ZarrSource`. Handles missing `omeroMetadata`. Does *not* resolve name collisions. */
3
- export function getSourceChannelNames(src) {
20
+ export function getSourceChannelMeta(src) {
4
21
  if (src.omeroMetadata?.channels) {
5
- return src.omeroMetadata.channels.map(({
6
- label
7
- }, idx) => label ?? `Channel ${idx + src.channelOffset}`);
22
+ const {
23
+ channels
24
+ } = src.omeroMetadata;
25
+ const names = [];
26
+ const colors = [];
27
+ for (let i = 0; i < channels.length; i++) {
28
+ const channel = channels[i];
29
+ names.push(channel.label ?? `Channel ${i + src.channelOffset}`);
30
+ colors.push(parseHexColor(channel.color));
31
+ }
32
+ return {
33
+ names,
34
+ colors
35
+ };
8
36
  }
9
37
  const cIdx = src.axesTCZYX[1];
10
38
  const length = cIdx < 0 ? 1 : src.scaleLevels[0].shape[cIdx];
11
- return Array.from({
39
+ const names = Array.from({
12
40
  length
13
41
  }, (_, idx) => `Channel ${idx + src.channelOffset}`);
42
+ const colors = Array.from({
43
+ length
44
+ }, () => undefined);
45
+ return {
46
+ names,
47
+ colors
48
+ };
14
49
  }
15
50
 
16
51
  /** Turns `axesTCZYX` into the number of dimensions in the array */
@@ -3,32 +3,29 @@ import { Vector3, Vector2 } from "three";
3
3
  export type ImageInfo = Readonly<{
4
4
  name: string;
5
5
  /**
6
- * XY dimensions of the texture atlas used by `RayMarchedAtlasVolume` and `Atlas2DSlice`, in number of z-slice
7
- * tiles (not pixels). Chosen by the loader to lay out the 3D volume in the squarest possible 2D texture atlas.
6
+ * XY dimensions of the texture atlas used by `RayMarchedAtlasVolume` and
7
+ * `Atlas2DSlice`, in number of z-slice tiles (not pixels). Chosen by the
8
+ * loader to lay out the 3D volume in the squarest possible 2D texture atlas.
8
9
  */
9
10
  atlasTileDims: [number, number];
10
11
  /** Size of the currently loaded subregion, in pixels, in XYZ order */
11
12
  subregionSize: [number, number, number];
12
13
  /** Offset of the loaded subregion into the total volume, in pixels, in XYZ order */
13
14
  subregionOffset: [number, number, number];
14
- /** Number of channels in the image, accounting for convergence of multiple sources.
15
- * Because of multiple sources, which is not accounted for in ImageInfo,
16
- * that this could be different than the number of channels in the multiscaleLevelDims.
17
- * NOTE Currently there is one ImageInfo per Volume, not per source.
18
- */
19
- combinedNumChannels: number;
15
+ /** The number of channels in each source, in source order. */
16
+ numChannelsPerSource: number[];
20
17
  /** The names of each channel */
21
18
  channelNames: string[];
22
19
  /** Optional overrides to default channel colors, in 0-255 range, RGB order */
23
- channelColors?: [number, number, number][];
20
+ channelColors?: ([number, number, number] | undefined)[];
24
21
  /** Dimensions of each scale level, at original size, from the first data source */
25
22
  multiscaleLevelDims: VolumeDims[];
26
23
  /** The scale level from which this image was loaded, between `0` and `numMultiscaleLevels-1` */
27
24
  multiscaleLevel: number;
28
25
  /**
29
- * An *optional* transform which may be supplied by image metadata. It is *not* applied by
30
- * default, but may be read and fed to `View3d` methods: `setVolumeTransform`,
31
- * `setVolumeRotation`, `setVolumeScale`.
26
+ * An *optional* transform which may be supplied by image metadata. It is
27
+ * *not* applied by default, but may be read and fed to `View3d` methods:
28
+ * `setVolumeTransform`, `setVolumeRotation`, `setVolumeScale`.
32
29
  */
33
30
  transform: {
34
31
  /** Translation of the volume from the center of space, in volume voxels in XYZ order */
@@ -48,6 +45,8 @@ export declare class CImageInfo {
48
45
  get currentLevelDims(): VolumeDims;
49
46
  /** Number of channels in the image */
50
47
  get numChannels(): number;
48
+ /** Number of channels per source, ordered by source index */
49
+ get numChannelsPerSource(): number[];
51
50
  /** XYZ size of the *original* (not downsampled) volume, in pixels */
52
51
  get originalSize(): Vector3;
53
52
  /** Size of the volume, in pixels */
@@ -67,7 +66,7 @@ export declare class CImageInfo {
67
66
  /** The names of each channel */
68
67
  get channelNames(): string[];
69
68
  /** Optional overrides to default channel colors, in 0-255 range */
70
- get channelColors(): [number, number, number][] | undefined;
69
+ get channelColors(): ([number, number, number] | undefined)[] | undefined;
71
70
  /** Size of the currently loaded subregion, in pixels */
72
71
  get subregionSize(): Vector3;
73
72
  /** Offset of the loaded subregion into the total volume, in pixels */
@@ -3,7 +3,7 @@ declare const _default: {
3
3
  angle: number;
4
4
  castShadow: false;
5
5
  color: 16777215;
6
- intensity: 0.4;
6
+ intensity: 15;
7
7
  position: {
8
8
  x: number;
9
9
  y: number;
@@ -12,12 +12,12 @@ declare const _default: {
12
12
  }>;
13
13
  ambientLightSettings: Readonly<{
14
14
  color: 16777215;
15
- intensity: 0.6;
15
+ intensity: 1.75;
16
16
  }>;
17
17
  reflectedLightSettings: Readonly<{
18
18
  castShadow: false;
19
19
  color: 16746666;
20
- intensity: 0.2;
20
+ intensity: 2;
21
21
  position: {
22
22
  x: number;
23
23
  y: number;
@@ -27,7 +27,7 @@ declare const _default: {
27
27
  fillLightSettings: Readonly<{
28
28
  castShadow: false;
29
29
  color: 15258025;
30
- intensity: 0.15;
30
+ intensity: 1.5;
31
31
  position: {
32
32
  x: number;
33
33
  y: number;
@@ -59,7 +59,7 @@ export type OmeroTransitionalMetadata = {
59
59
  channels: {
60
60
  active?: boolean;
61
61
  coefficient?: number;
62
- color: string;
62
+ color?: string;
63
63
  family?: string;
64
64
  inverted?: boolean;
65
65
  label?: string;
@@ -1,6 +1,15 @@
1
1
  import type { OMEAxis, OMEDataset, OMEMultiscale, TCZYX, ZarrSource } from "./types.js";
2
+ /**
3
+ * Attempts to parse `color` as a 24-bit (6-digit) hexadecimal color with a possible leading `#`.
4
+ *
5
+ * Six-digit hex is the only allowable color representation in the OMERO metadata spec.
6
+ */
7
+ export declare function parseHexColor(color: string | undefined): [number, number, number] | undefined;
2
8
  /** Extracts channel names from a `ZarrSource`. Handles missing `omeroMetadata`. Does *not* resolve name collisions. */
3
- export declare function getSourceChannelNames(src: ZarrSource): string[];
9
+ export declare function getSourceChannelMeta(src: ZarrSource): {
10
+ names: string[];
11
+ colors: ([number, number, number] | undefined)[];
12
+ };
4
13
  /** Turns `axesTCZYX` into the number of dimensions in the array */
5
14
  export declare const getDimensionCount: ([t, c, z]: TCZYX<number>) => number;
6
15
  export declare function remapAxesToTCZYX(axes: OMEAxis[]): TCZYX<number>;
@@ -96,28 +96,26 @@ export interface FuseChannel {
96
96
  }
97
97
  /** If `FuseChannel.rgbColor` is this value, it is disabled from fusion. */
98
98
  export declare const FUSE_DISABLED_RGB_COLOR = 0;
99
- /**
100
- * Provide options to control the visual appearance of a Volume
101
- * @typedef {Object} VolumeChannelDisplayOptions
102
- * @property {boolean} enabled array of boolean per channel
103
- * @property {Array.<number>} color array of rgb per channel
104
- * @property {Array.<number>} specularColor array of rgb per channel
105
- * @property {Array.<number>} emissiveColor array of rgb per channel
106
- * @property {number} glossiness array of float per channel
107
- * @property {boolean} isosurfaceEnabled array of boolean per channel
108
- * @property {number} isovalue array of number per channel
109
- * @property {number} isosurfaceOpacity array of number per channel
110
- * @example let options = {
111
- };
112
- */
113
99
  export interface VolumeChannelDisplayOptions {
100
+ /** Whether the channel's volume data should be rendered for this channel. */
114
101
  enabled?: boolean;
102
+ /** RGB color array, with values in the range of [0, 255]. */
115
103
  color?: [number, number, number];
104
+ /** RGB color array for specular (highlight) color, with values in the range of [0, 255]. */
116
105
  specularColor?: [number, number, number];
106
+ /** RGB color array for emissive (glow) color, with values in the range of [0, 255]. */
117
107
  emissiveColor?: [number, number, number];
108
+ /** Exponent factor controlling the glossiness ("shininess") of the material. 0 is default. */
118
109
  glossiness?: number;
110
+ /** Whether the isosurface mesh should be rendered for this channel. */
119
111
  isosurfaceEnabled?: boolean;
112
+ /**
113
+ * Isovalue used to calculate the isosurface mesh, in a [0, 255] range.
114
+ * Isosurface is found at the set of all boundaries between voxels whose
115
+ * intensities span across this isovalue.
116
+ */
120
117
  isovalue?: number;
118
+ /** Opacity of the isosurface, in a [0, 1] range. */
121
119
  isosurfaceOpacity?: number;
122
120
  }
123
121
  export declare enum RenderMode {
package/es/types.js CHANGED
@@ -18,22 +18,6 @@ export function isFloatTypeArray(array) {
18
18
  }
19
19
  /** If `FuseChannel.rgbColor` is this value, it is disabled from fusion. */
20
20
  export const FUSE_DISABLED_RGB_COLOR = 0;
21
-
22
- /**
23
- * Provide options to control the visual appearance of a Volume
24
- * @typedef {Object} VolumeChannelDisplayOptions
25
- * @property {boolean} enabled array of boolean per channel
26
- * @property {Array.<number>} color array of rgb per channel
27
- * @property {Array.<number>} specularColor array of rgb per channel
28
- * @property {Array.<number>} emissiveColor array of rgb per channel
29
- * @property {number} glossiness array of float per channel
30
- * @property {boolean} isosurfaceEnabled array of boolean per channel
31
- * @property {number} isovalue array of number per channel
32
- * @property {number} isosurfaceOpacity array of number per channel
33
- * @example let options = {
34
- };
35
- */
36
-
37
21
  export let RenderMode = /*#__PURE__*/function (RenderMode) {
38
22
  RenderMode[RenderMode["RAYMARCH"] = 0] = "RAYMARCH";
39
23
  RenderMode[RenderMode["PATHTRACE"] = 1] = "PATHTRACE";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aics/vole-core",
3
- "version": "4.1.0",
3
+ "version": "4.3.0",
4
4
  "description": "volume renderer for 3d, 4d, or 5d imaging data with OME-Zarr support",
5
5
  "main": "es/index.js",
6
6
  "type": "module",