Skip to content

Commit

Permalink
Merge pull request #604 from PaulHax/loading-image-indicator
Browse files Browse the repository at this point in the history
feat(scaleSelector): spinner shows when loading image
  • Loading branch information
thewtex authored Oct 18, 2022
2 parents 13ba5a1 + e778f14 commit 7c88e1e
Show file tree
Hide file tree
Showing 21 changed files with 261 additions and 125 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"itk-image-io": "^1.0.0-b.15",
"itk-mesh-io": "^1.0.0-b.15",
"itk-viewer-color-maps": "^1.0.3",
"itk-viewer-icons": "^11.12.0",
"itk-viewer-icons": "^11.13.0",
"itk-viewer-transfer-function-editor": "^1.1.4",
"itk-wasm": "^1.0.0-b.17",
"mobx": "^5.15.7",
Expand Down
38 changes: 35 additions & 3 deletions src/Rendering/Images/createImageRenderingActor.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,20 @@ const sendRenderedImageAssigned = (
})
}

const sendStartDataUpdate = context => {
context.service.send({
type: 'START_DATA_UPDATE',
name: context.actorName,
})
}

const sendFinishDataUpdate = context => {
context.service.send({
type: 'FINISH_DATA_UPDATE',
name: context.actorName,
})
}

const eventResponses = {
IMAGE_ASSIGNED: {
target: 'updatingImage',
Expand Down Expand Up @@ -273,12 +287,27 @@ const createUpdatingImageMachine = options => {
states: {
checkingUpdateNeeded: {
always: [
{ cond: 'isImageUpdateNeeded', target: 'loadingImage' },
{ cond: 'isImageUpdateNeeded', target: 'preLoadingImage' },
{ target: '#updatingImageMachine.loadedImage' },
],
exit: assign({ isUpdateForced: false }),
},

preLoadingImage: {
entry: sendStartDataUpdate,
invoke: {
id: 'preLoadingImage',
src: async () => {
// Give spinner chance to start. Waiting 2 frames works better in cached image case =|
await new Promise(requestAnimationFrame)
await new Promise(requestAnimationFrame)
},
onDone: {
target: 'loadingImage',
},
},
},

loadingImage: {
invoke: {
id: 'updateRenderedImage',
Expand All @@ -302,6 +331,7 @@ const createUpdatingImageMachine = options => {
},

loadedImage: {
entry: sendFinishDataUpdate,
always: [
{
cond: 'isFramerateScalePickingOn',
Expand Down Expand Up @@ -349,10 +379,11 @@ const createUpdatingImageMachine = options => {
}

const createImageRenderingActor = (options, context, name) => {
const machineContext = { ...context, actorName: name }
return createMachine(
{
id: 'imageRendering',
context: { ...context, actorName: name },
context: machineContext,
type: 'parallel',
states: {
imageLoader: {
Expand All @@ -378,11 +409,12 @@ const createImageRenderingActor = (options, context, name) => {
UPDATE_IMAGE_HISTOGRAM: {},
RENDERED_BOUNDS_CHANGED: {},
},
entry: 'assignVisualizedComponents',
invoke: {
id: 'updatingImageMachine',
src: createUpdatingImageMachine(options),
data: {
...context,
...machineContext,
hasScaledCoarser: false,
targetScale: ({ images }, event) => {
if (event.type === 'SET_IMAGE_SCALE')
Expand Down
8 changes: 4 additions & 4 deletions src/Rendering/Images/createImagesRenderingMachine.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ function spawnImageRenderingActor(options) {
})
}

const sendEventToAllActors = () =>
actions.pure(({ images: { imageRenderingActors } }, event) =>
const sendEventToAllActors = actions.pure(
({ images: { imageRenderingActors } }, event) =>
Array.from(imageRenderingActors.values()).map(actor =>
send(event, {
to: actor,
})
)
)
)

function createImagesRenderingMachine(options, context) {
const { imageRenderingActor } = options
Expand Down Expand Up @@ -196,7 +196,7 @@ function createImagesRenderingMachine(options, context) {
},
...makeTransitions(
['CROPPING_PLANES_CHANGED_BY_USER', 'CAMERA_MODIFIED'],
{ actions: sendEventToAllActors() }
{ actions: sendEventToAllActors }
),
},
},
Expand Down
3 changes: 2 additions & 1 deletion src/Rendering/VTKJS/Images/applyComponentVisibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function applyComponentVisibility(context, event) {
const weight = visibility ? 1.0 : 0.0

if (visibility && visualizedComponents.indexOf(index) < 0) {
// add component to visualizedComponents
visualizedComponents.push(index)
for (let i = 0; i < visualizedComponents.length; i++) {
if (!componentVisibilities[visualizedComponents[i]]) {
Expand All @@ -40,7 +41,7 @@ function applyComponentVisibility(context, event) {

volumeActors.forEach(volume => {
const volumeProperty = volume.getProperty()
if (!!context.images.labelImage || !!context.images.editorLabelImage) {
if (context.images.labelImage || context.images.editorLabelImage) {
let componentsVisible = false
for (let i = 0; i < visualizedComponents.length - 1; i++) {
componentsVisible = componentsVisible[i] ? true : componentsVisible
Expand Down
56 changes: 56 additions & 0 deletions src/Rendering/VTKJS/Images/assignVisualizedComponents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { assign } from 'xstate'

const assignVisualizedComponents = assign({
images: context => {
const name = context.actorName
const actorContext = context.images.actorContext.get(name)
const image = actorContext.image
const labelImage = actorContext.labelImage
const editorLabelImage = actorContext.editorLabelImage
if (image) {
const imageComponents = image.imageType.components
actorContext.visualizedComponents = Array(image.imageType.components)
.fill(0)
.map((_, idx) => idx)
.filter(i => actorContext.componentVisibilities[i])

actorContext.maxIntensityComponents = 4
if (labelImage) {
actorContext.maxIntensityComponents -= 1
}
if (editorLabelImage) {
actorContext.maxIntensityComponents -= 1
}

const numVizComps = Math.min(
imageComponents,
actorContext.maxIntensityComponents
)
if (actorContext.visualizedComponents.length > numVizComps) {
// turn off unrenderable components
actorContext.visualizedComponents = actorContext.visualizedComponents.slice(
0,
numVizComps
)
const offComps = [...Array(imageComponents).keys()].filter(
comp => !actorContext.visualizedComponents.includes(comp)
)
offComps.forEach(comp =>
context.service.send({
type: 'IMAGE_COMPONENT_VISIBILITY_CHANGED',
data: { name, component: comp, visibility: false },
})
)
}
}
if (labelImage) {
actorContext.visualizedComponents =
actorContext.visualizedComponents ?? []
actorContext.visualizedComponents.push(-1)
}

return context.images
},
})

export default assignVisualizedComponents
2 changes: 2 additions & 0 deletions src/Rendering/VTKJS/Images/imagesRenderingMachineOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import mapToPiecewiseFunctionNodes from './mapToPiecewiseFunctionNodes'
import { getBoundsOfFullImage } from '../Main/croppingPlanes'
import { computeRenderedBounds } from '../Main/computeRenderedBounds'
import { applyCinematicChanged } from './applyCinematicChanged'
import assignVisualizedComponents from './assignVisualizedComponents'

const EPSILON = 0.000001

Expand Down Expand Up @@ -69,6 +70,7 @@ const imagesRenderingMachineOptions = {
actions: {
applyRenderedImage,
assignRenderedImage,
assignVisualizedComponents,

toggleLayerVisibility,

Expand Down
3 changes: 0 additions & 3 deletions src/Rendering/VTKJS/Images/updateRenderedImage.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import vtkITKHelper from 'vtk.js/Sources/Common/DataModel/ITKHelper'
import { mat4 } from 'gl-matrix'

import updateVisualizedComponents from './updateVisualizedComponents'
import { fuseImages } from './fuseImages'
import { computeRenderedBounds } from '../Main/computeRenderedBounds'
import { worldBoundsToIndexBounds } from '../../../IO/MultiscaleSpatialImage'
Expand Down Expand Up @@ -47,8 +46,6 @@ async function updateRenderedImage(context) {
const name = context.images.updateRenderedName
const actorContext = context.images.actorContext.get(name)

updateVisualizedComponents(context, name)

const {
image,
labelImage,
Expand Down
48 changes: 0 additions & 48 deletions src/Rendering/VTKJS/Images/updateVisualizedComponents.js

This file was deleted.

7 changes: 5 additions & 2 deletions src/Rendering/VTKJS/createRenderer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import vtkProxyManager from 'vtk.js/Sources/Proxy/Core/ProxyManager'
import proxyConfiguration from './proxyManagerConfiguration'
// import vtkProxyManager from 'vtk.js/Sources/Proxy/Core/ProxyManager'
// import proxyConfiguration from './proxyManagerConfiguration'
import createMainRenderer from './Main/createMainRenderer'

// Load the rendering pieces we want to use (for both WebGL and WebGPU)
Expand All @@ -19,6 +19,9 @@ function createRenderer(context) {
context.itkVtkView.setXyLowerLeft(context.xyLowerLeft)

createMainRenderer(context)

const interactor = context.itkVtkView.getInteractor()
interactor.onRenderEvent(() => context.service.send('POST_RENDER'))
}

export default createRenderer
6 changes: 2 additions & 4 deletions src/UI/Images/createImagesUIMachine.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@ const assignComponentVisibility = assign({
// A component was made visible, and it was not already in the list
// of visualized components
const currentNumVisualized = componentVisibilities.reduce(
(a, c) => c + a,
(count, isVisible) => count + isVisible,
0
)
if (currentNumVisualized + 1 > actorContext.maxIntensityComponents) {
// Find the index in the visualized components list of the last touched
// component. We need to replace it with this component the user just
// turned on.
// Replace last touched component with turned on component
componentVisibilities[
actorContext.lastComponentVisibilityChanged
] = false
Expand Down
34 changes: 19 additions & 15 deletions src/UI/Layers/createLayerUIActor.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,15 @@ const assignLayerVisibility = assign({
},
})

const createLayerUIActor = (options, context) => {
const createLayerUIActor = (options, context, actorContext) => {
return Machine(
{
id: 'layerUI',
initial: 'idle',
context,
initial: 'active',
context: { actorContext, ...context },
states: {
idle: {
always: {
target: 'active',
actions: 'createLayerInterface',
},
},
active: {
entry: 'createLayerInterface',
on: {
SELECT_LAYER: {
actions: 'selectLayer',
Expand All @@ -33,12 +28,21 @@ const createLayerUIActor = (options, context) => {
actions: [assignLayerVisibility, 'toggleLayerVisibility'],
},
},
},
finished: {
type: 'final',
},
onDone: {
//actions: 'cleanup'
initial: 'idle',
states: {
idle: {
entry: 'finishDataUpdate',
on: { START_DATA_UPDATE: 'dataUpdating' },
},
dataUpdating: {
entry: 'startDataUpdate',
on: { FINISH_DATA_UPDATE: 'dataLoading' },
},
dataLoading: {
// wait until data is loaded on GPU
on: { POST_RENDER: 'idle' },
},
},
},
},
},
Expand Down
Loading

0 comments on commit 7c88e1e

Please sign in to comment.