Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
2dab001
use volumeloadercontext
frasercl Oct 8, 2024
5e14066
add volume object w/ observer connection to drawable
frasercl Oct 8, 2024
84ac658
(try to) enable volume models to set timesteps
frasercl Oct 8, 2024
416e92f
try to load volume data
frasercl Oct 9, 2024
432be1c
show full volume object, not just box; remove box hack
frasercl Oct 9, 2024
3711c9e
lots of new methods, code to try n get these dang volumes to show up
frasercl Oct 10, 2024
1c0d2c6
using more of those methods in geometrystore
frasercl Oct 10, 2024
18118c9
doing the stuff that needs doing in `visGeometry`
frasercl Oct 10, 2024
5528961
use volume subpoints
frasercl Oct 10, 2024
3923a42
set resolution and a few other settings properly
frasercl Oct 10, 2024
b7dce62
switch back from max project (fix on volume-viewer side)
frasercl Oct 10, 2024
1a9d435
move ball geometry into volume
frasercl Oct 11, 2024
1bbcfd9
trying to get ortho camera to work
frasercl Oct 11, 2024
d2b8e72
remove orthoscale context prop
frasercl Oct 11, 2024
0033592
rename: `tempVolumeGroup` -> `volumeGroup`
frasercl Oct 11, 2024
b9f30b7
update volume-viewer interface; try (fail) to get ortho volumes to work
frasercl Oct 21, 2024
c497d69
Merge branch 'feature/volume-rendering' of https://github.com/simular…
frasercl Oct 21, 2024
547f8c0
experiment with how to get `orthoScale` right
frasercl Oct 21, 2024
f21b65b
FIX orthographic volumes AT LAST
frasercl Oct 23, 2024
7fa5e28
make camera type testing nicer
frasercl Oct 23, 2024
d8515ca
Merge branch 'volume-agents' into feature/volume-rendering
frasercl Oct 28, 2024
9b507cf
replace volume-viewer branch dependency with proper npm dep
frasercl Nov 7, 2024
f465c10
Merge branch 'volume-agents' into feature/volume-rendering
frasercl Dec 10, 2024
018d89c
Merge branch 'volume-agents' into feature/volume-rendering
frasercl Jan 17, 2025
ff87ac3
bump @types/three
frasercl Jan 18, 2025
8b2edd2
fix volume simulator import
frasercl Jan 18, 2025
d3f590b
Bump vite from 5.4.14 to 5.4.17
dependabot[bot] Apr 8, 2025
7148198
Merge branch 'volume-agents' into feature/volume-rendering
frasercl Apr 23, 2025
9aeedfd
update vole-core dependency
frasercl Apr 23, 2025
f42e53c
Merge pull request #487 from simularium/dependabot/npm_and_yarn/vite-…
toloudis Apr 25, 2025
55699e5
Bump http-proxy-middleware from 2.0.7 to 2.0.9 in /examples
dependabot[bot] Apr 25, 2025
b4df812
Bump vite from 5.4.17 to 5.4.18
dependabot[bot] Apr 25, 2025
a69bb87
Bump vite from 6.2.5 to 6.3.4 in /examples
dependabot[bot] Apr 30, 2025
6cbd7a1
Bug Fix: Blood Plasma Simularium Example (#493)
ascibisz May 8, 2025
cbea575
Merge pull request #490 from simularium/dependabot/npm_and_yarn/examp…
toloudis Jun 9, 2025
bff5385
Merge pull request #491 from simularium/dependabot/npm_and_yarn/vite-…
toloudis Jun 9, 2025
7ab44d1
Merge pull request #492 from simularium/dependabot/npm_and_yarn/examp…
toloudis Jun 9, 2025
5de80fe
Bump webpack-dev-server from 5.1.0 to 5.2.1 in /examples
dependabot[bot] Jun 9, 2025
0f12260
3.10.0
ascibisz Jun 10, 2025
b0af148
Merge pull request #494 from simularium/dependabot/npm_and_yarn/examp…
frasercl Jun 11, 2025
d6edd91
Bump vite from 5.4.18 to 5.4.19
dependabot[bot] Jun 11, 2025
086fe94
Merge pull request #495 from simularium/dependabot/npm_and_yarn/vite-…
toloudis Jun 13, 2025
0561f62
Bump on-headers and compression in /examples
dependabot[bot] Jul 18, 2025
78c51cc
Merge pull request #497 from simularium/dependabot/npm_and_yarn/examp…
toloudis Jul 18, 2025
2a0159a
Bump form-data from 4.0.1 to 4.0.4
dependabot[bot] Jul 22, 2025
b064cb1
Merge pull request #498 from simularium/dependabot/npm_and_yarn/form-…
toloudis Jul 23, 2025
121596c
add export field to package.json (#496)
interim17 Jul 31, 2025
80d0546
clear available agents (#500)
meganrm Aug 1, 2025
baec486
3.10.1
meganrm Aug 1, 2025
746b4b7
need to include types in export (#501)
meganrm Aug 2, 2025
b59b2fb
3.10.2
meganrm Aug 2, 2025
4527126
read frameNumber from intView when parsing binary frame (#502)
ascibisz Aug 15, 2025
6dc215d
3.10.3
ascibisz Aug 15, 2025
9b9e1cd
trying to revive this example
toloudis Sep 24, 2025
3e51de0
Merge branch 'main' into feature/volume-rendering
toloudis Sep 24, 2025
ac0f5fe
viewer version
toloudis Sep 24, 2025
a3aea7e
fix worker loading!
toloudis Sep 24, 2025
50a0a30
WIP working toward fixing time series volume playback
toloudis Sep 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions examples/src/simulators/VolumeSimulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,19 @@ export default class VolumeSim implements IClientSimulatorImpl {
0, // ry
0, // rz
10.0, // collision radius
0, // subpoints
4, // subpoints
0,
0,
1,
2,

// AGENT 2 (sphere, to test volume-mesh intersection)
VisTypes.ID_VIS_TYPE_DEFAULT, // vis type
1, // instance id
1, // type
0, // x
0, // y
6, // z
3, // z
0, // rx
0, // ry
0, // rz
Expand Down
23 changes: 19 additions & 4 deletions src/visGeometry/GeometryStore.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { forEach } from "lodash";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import jsLogger, { ILogger, ILogLevel } from "js-logger";
import { createVolumeLoader, LoadSpec } from "@aics/volume-viewer";
import { LoadSpec, VolumeLoaderContext } from "@aics/volume-viewer";
import {
BufferGeometry,
Object3D,
Expand Down Expand Up @@ -50,6 +50,7 @@ class GeometryStore {
private _geoLoadAttempted: Map<string, boolean>;
private _cachedAssets: Map<string, string>;
private _registry: Registry;
private volumeLoaderContext?: VolumeLoaderContext;
public mlogger: ILogger;
public static sphereGeometry: SphereGeometry = new SphereGeometry(
1,
Expand Down Expand Up @@ -384,15 +385,29 @@ class GeometryStore {
});
}

/** Don't start a volume load worker until we know we need it */
private async getVolumeLoaderContext(): Promise<VolumeLoaderContext> {
if (!this.volumeLoaderContext) {
// TODO this is missing optional config properties:
// `maxCacheSize`, `maxActiveRequests`, `maxLowPriorityRequests`.
// Do we want to set our own values for these?
this.volumeLoaderContext = new VolumeLoaderContext();
await this.volumeLoaderContext.onOpen();
}
return this.volumeLoaderContext;
}

private async fetchVolume(url: string): Promise<VolumeModel> {
// TODO should this be in a worker? Are we already in a worker here?
// Should this class get a `VolumeLoaderContext` going?
const model = new VolumeModel();
this.setGeometryInRegistry(url, model, GeometryDisplayType.VOLUME);
const loader = await createVolumeLoader(url);
// TODO onChannelLoaded callback?
const volume = await loader.createVolume(new LoadSpec());
const context = await this.getVolumeLoaderContext();
const loader = await context.createLoader(url);
const loadCallback = model.onChannelLoaded.bind(model);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to bind the callback here? :0

const volume = await loader.createVolume(new LoadSpec(), loadCallback);
model.setImage(volume);
model.loadInitialData();
return model;
}

Expand Down
132 changes: 114 additions & 18 deletions src/visGeometry/VolumeModel.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,136 @@
import { Box3Helper, Euler, Object3D, Vector3 } from "three";
import { Euler, Object3D, Vector3 } from "three";

import { Volume, VolumeDrawable } from "@aics/volume-viewer";
import {
HasThreeJsContext,
Lut,
Volume,
VolumeDrawable,
} from "@aics/volume-viewer";

import { AgentData } from "../simularium/types";

// TEMPORARY HACK to make things typecheck. TODO remove!
import { VolumeRenderImpl } from "@aics/volume-viewer/es/types/VolumeRenderImpl";
interface TempRayMarchedVolume extends VolumeRenderImpl {
boxHelper: Box3Helper;
}

Comment on lines -7 to -12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

export default class VolumeModel {
// TODO what to do with this `cancelled` property? Type check fails without it.
// When should it be set, if ever; what should it be used for, if anything?
public cancelled = false;
private image?: VolumeDrawable;
private drawable?: VolumeDrawable;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I recall, I think the "cancelled" property mentioned above is for when the trajectory has been changed but there is still geometry download requests in-flight. We can use cancelled to just drop the arriving stuff.

private volume?: Volume;
private channelsEnabled: boolean[] = [];

private setEnabledChannels(channels: number[]): void {
if (!this.volume || !this.drawable) {
this.channelsEnabled = [];
return;
}
const { numChannels } = this.volume.imageInfo;
this.channelsEnabled = new Array(numChannels).fill(false);

for (const channel of channels) {
if (channel < numChannels) {
this.channelsEnabled[channel] = true;
}
}

for (const [channelIndex, enabled] of this.channelsEnabled.entries()) {
this.drawable.setVolumeChannelEnabled(channelIndex, enabled);
}
}

public setImage(volumeObject: Volume): void {
this.image = new VolumeDrawable(volumeObject, {});
this.volume = volumeObject;
this.drawable = new VolumeDrawable(this.volume, {});
this.volume.addVolumeDataObserver(this);
this.drawable.setBrightness(0.7);
this.drawable.setGamma(0.15, 0.9, 1.0);
this.drawable.setDensity(0.7);
}

public setAgentData(data: AgentData): void {
if (this.image) {
this.image.setTranslation(new Vector3(data.x, data.y, data.z));
this.image.setRotation(new Euler(data.xrot, data.yrot, data.zrot));
if (this.drawable) {
this.drawable.setTranslation(new Vector3(data.x, data.y, data.z));
this.drawable.setRotation(
new Euler(data.xrot, data.yrot, data.zrot)
);
const r = data.cr * 2;
this.image.setScale(new Vector3(r, r, r));
this.drawable.setScale(new Vector3(r, r, r));
// Always defined if `drawable` is, but ts doesn't know that.
if (this.volume) {
// Volume agent data may use subpoint 0 as time
const numPoints = data.subpoints.length;
const time = numPoints > 0 ? data.subpoints[0] : 0;
if (this.volume.loadSpec.time !== time) {
this.volume.updateRequiredData({ time });
}
// If there are more subpoints, they are enabled channel idxes.
// Otherwise, just channel 0 is enabled.
const channels = numPoints > 1 ? data.subpoints.slice(1) : [0];
this.setEnabledChannels(channels);
}
}
}

public loadInitialData(): void {
this.volume?.loader?.loadVolumeData(this.volume);
}

public getObject3D(): Object3D | undefined {
return this.image?.sceneRoot;
return this.drawable?.sceneRoot;
}

public onChannelLoaded(vol: Volume, channelIndex: number): void {
if (this.drawable) {
const isEnabled = this.channelsEnabled[channelIndex];
this.drawable.setVolumeChannelEnabled(channelIndex, isEnabled);
this.drawable.updateScale();
this.drawable.onChannelLoaded([channelIndex]);
if (this.volume) {
const histo = this.volume.getHistogram(channelIndex);
const min = histo.findBinOfPercentile(0.5);
const max = histo.findBinOfPercentile(0.983);
Comment on lines +94 to +95
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw the note you had in the PR description, maybe tag all the hardcoded values with a TODO so they're easier to track down? Totally optional

const lut = new Lut().createFromMinMax(min, max);
this.volume.setLut(channelIndex, lut);
}
this.drawable.updateLuts();
this.drawable.fuse();
}
}

public onBeforeRender(
context: HasThreeJsContext,
width: number,
height: number,
orthoScale: number | undefined
): void {
if (this.drawable) {
const isOrtho = orthoScale !== undefined;
this.drawable.setIsOrtho(isOrtho);
if (isOrtho) {
this.drawable.setOrthoScale(orthoScale);
}
this.drawable.setResolution(width, height);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does setting the resolution here do?

this.drawable.onAnimate(context);
}
}

public setSize(width: number, height: number): void {
this.drawable?.setResolution(width, height);
}

// METHODS FROM `VolumeDataObserver` in volume-viewer

public onVolumeData(_volume: Volume, channels: number[]): void {
this.drawable?.updateScale();
this.drawable?.onChannelLoaded(channels);
}

public onVolumeChannelAdded(
_volume: Volume,
newChannelIndex: number
): void {
this.drawable?.onChannelAdded(newChannelIndex);
}

public tempGetBoundingBoxObject(): Box3Helper | undefined {
this.image?.setShowBoundingBox(true);
return (this.image?.volumeRendering as TempRayMarchedVolume)?.boxHelper;
public onVolumeLoadError(_volume: Volume, error: unknown): void {
console.error("Volume load error", error);
}
}
44 changes: 33 additions & 11 deletions src/visGeometry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import {
} from "./types";
import { checkAndSanitizePath } from "../util";
import ColorHandler from "./ColorHandler";
import { HasThreeJsContext } from "@aics/volume-viewer";

const MAX_PATH_LEN = 32;
const MAX_MESHES = 100000;
Expand Down Expand Up @@ -152,7 +153,7 @@ class VisGeometry {
public lightsGroup: Group;
public agentPathGroup: Group;
public instancedMeshGroup: Group;
public tempVolumeGroup: Group; // TODO remove
public volumeGroup: Group; // TODO remove
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the TODO still needed?

private supportsWebGL2Rendering: boolean;
private lodBias: number;
private lodDistanceStops: number[];
Expand Down Expand Up @@ -219,9 +220,9 @@ class VisGeometry {
this.instancedMeshGroup = new Group();
this.instancedMeshGroup.name = "instanced meshes for agents";
this.scene.add(this.instancedMeshGroup);
this.tempVolumeGroup = new Group();
this.tempVolumeGroup.name = "volumes";
this.scene.add(this.tempVolumeGroup);
this.volumeGroup = new Group();
this.volumeGroup.name = "volumes";
this.scene.add(this.volumeGroup);

this.resetBounds(DEFAULT_VOLUME_DIMENSIONS);

Expand Down Expand Up @@ -995,10 +996,20 @@ class VisGeometry {
for (let i = this.instancedMeshGroup.children.length - 1; i >= 0; i--) {
this.instancedMeshGroup.remove(this.instancedMeshGroup.children[i]);
}
for (let i = this.tempVolumeGroup.children.length - 1; i >= 0; i--) {
this.tempVolumeGroup.remove(this.tempVolumeGroup.children[i]);
for (let i = this.volumeGroup.children.length - 1; i >= 0; i--) {
this.volumeGroup.remove(this.volumeGroup.children[i]);
}

const volRenderContext: HasThreeJsContext = {
camera: this.camera,
// if not for `renderer` already being used for
// `SimulariumRenderer`, we could use `this` below.
renderer: this.threejsrenderer,
};

const canvasWidth = this.threejsrenderer.domElement.width;
const canvasHeight = this.threejsrenderer.domElement.height;

// re-add fibers immediately
this.instancedMeshGroup.add(this.fibers.getGroup());

Expand All @@ -1025,9 +1036,20 @@ class VisGeometry {
}
}
} else if (displayType === GeometryDisplayType.VOLUME) {
const volObject = entry.geometry.tempGetBoundingBoxObject();
if (volObject) {
this.tempVolumeGroup.add(volObject);
const volObj = entry.geometry.getObject3D();
if (volObj) {
const isOrtho = (this.camera as OrthographicCamera)
.isOrthographicCamera;
const orthoScale = isOrtho
? 1 / this.camera.zoom
: undefined;
entry.geometry.onBeforeRender(
volRenderContext,
canvasWidth,
canvasHeight,
orthoScale
);
this.volumeGroup.add(volObj);
}
} else {
const meshEntry = entry as MeshGeometry;
Expand Down Expand Up @@ -1060,7 +1082,7 @@ class VisGeometry {
this.boundingBoxMesh.visible = false;
this.tickMarksMesh.visible = false;
this.agentPathGroup.visible = false;
this.tempVolumeGroup.visible = false;
this.volumeGroup.visible = false;
this.renderer.render(
this.threejsrenderer,
this.scene,
Expand All @@ -1072,7 +1094,7 @@ class VisGeometry {
this.boundingBoxMesh.visible = true;
this.tickMarksMesh.visible = true;
this.agentPathGroup.visible = true;
this.tempVolumeGroup.visible = true;
this.volumeGroup.visible = true;

this.threejsrenderer.autoClear = false;
// hide everything except the wireframe and paths, and render with the standard renderer
Expand Down