@aics/vole-core 4.2.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/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) {
@@ -711,7 +711,7 @@ export default class VolumeDrawable {
711
711
 
712
712
  // remove old 3d object from scene
713
713
  if (this.renderMode === RenderMode.SLICE || this.renderMode === RenderMode.RAYMARCH) {
714
- this.sceneRoot.remove(this.meshVolume.get3dObject());
714
+ this.childObjectsGroup.remove(this.meshVolume.get3dObject());
715
715
  }
716
716
  this.sceneRoot.remove(this.volumeRendering.get3dObject());
717
717
 
@@ -747,7 +747,7 @@ export default class VolumeDrawable {
747
747
  if (this.renderUpdateListener) {
748
748
  this.renderUpdateListener(0);
749
749
  }
750
- this.sceneRoot.add(this.meshVolume.get3dObject());
750
+ this.childObjectsGroup.add(this.meshVolume.get3dObject());
751
751
  }
752
752
 
753
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,
@@ -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";
@@ -281,11 +281,16 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
281
281
  // Channel names is the other place where we have to check every source
282
282
  // Track which channel names we've seen so far, so that we can rename them to avoid name collisions
283
283
  const channelNamesMap = new Map();
284
- const channelNames = this.sources.flatMap(src => {
285
- 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);
286
291
 
287
292
  // Resolve name collisions
288
- return sourceChannelNames.map(channelName => {
293
+ const resolvedNames = names.map(channelName => {
289
294
  const numMatchingChannels = channelNamesMap.get(channelName);
290
295
  if (numMatchingChannels !== undefined) {
291
296
  // If e.g. we've seen channel "Membrane" once before, rename this one to "Membrane (1)"
@@ -296,7 +301,9 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
296
301
  return channelName;
297
302
  }
298
303
  });
299
- });
304
+ channelNames.push(...resolvedNames);
305
+ channelColors.push(...colors);
306
+ }
300
307
  const alldims = source0.scaleLevels.map((level, i) => {
301
308
  const dims = {
302
309
  spaceUnit: spatialUnit,
@@ -314,6 +321,7 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
314
321
  subregionOffset: [0, 0, 0],
315
322
  numChannelsPerSource,
316
323
  channelNames,
324
+ channelColors,
317
325
  multiscaleLevel: levelToLoad,
318
326
  multiscaleLevelDims: alldims,
319
327
  transform: {
@@ -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 */
@@ -17,7 +17,7 @@ export type ImageInfo = Readonly<{
17
17
  /** The names of each channel */
18
18
  channelNames: string[];
19
19
  /** Optional overrides to default channel colors, in 0-255 range, RGB order */
20
- channelColors?: [number, number, number][];
20
+ channelColors?: ([number, number, number] | undefined)[];
21
21
  /** Dimensions of each scale level, at original size, from the first data source */
22
22
  multiscaleLevelDims: VolumeDims[];
23
23
  /** The scale level from which this image was loaded, between `0` and `numMultiscaleLevels-1` */
@@ -66,7 +66,7 @@ export declare class CImageInfo {
66
66
  /** The names of each channel */
67
67
  get channelNames(): string[];
68
68
  /** Optional overrides to default channel colors, in 0-255 range */
69
- get channelColors(): [number, number, number][] | undefined;
69
+ get channelColors(): ([number, number, number] | undefined)[] | undefined;
70
70
  /** Size of the currently loaded subregion, in pixels */
71
71
  get subregionSize(): Vector3;
72
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>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aics/vole-core",
3
- "version": "4.2.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",