Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VV - UI + Styles #6573

Merged
merged 3 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('Component > VolumetricViewer', function () {
subjectJSON: 'mock-subject-json'
}}
/>)
expect(screen.getByText('Suspense boundary')).to.exist()
expect(screen.getByText('Loading Volumetric Viewer...')).to.exist()
expect(await screen.findByTestId('subject-viewer-volumetric')).to.exist()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { lazy, Suspense } from 'react'
import { MobXProviderContext } from 'mobx-react'
import { useContext } from 'react'

const VolumetricViewer = lazy(() => import('@zooniverse/subject-viewers/VolumetricViewer'))
const DEFAULT_HANDLER = () => {}
const VolumetricViewer = lazy(() => import('@zooniverse/subject-viewers/VolumetricViewer'))

function VolumetricViewerWrapper({
loadingState = asyncStates.initialized,
Expand All @@ -21,14 +21,16 @@ function VolumetricViewerWrapper({
addAnnotation(activeStepTasks[0], annotations)
}

return <Suspense fallback={<p>Suspense boundary</p>}>
<VolumetricViewer
loadingState={loadingState}
onAnnotation={onAnnotationUpdate}
onError={onError}
onReady={onReady}
subject={subject}
/>
const config = {
loadingState,
onAnnotation: onAnnotationUpdate,
onError,
onReady,
subject,
}

return <Suspense fallback={<p>Loading Volumetric Viewer...</p>}>
<VolumetricViewer {...config} />
</Suspense>
}

Expand Down
2 changes: 2 additions & 0 deletions packages/lib-subject-viewers/src/VolumetricViewer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ This directory holds all the relevant code for rendering the VolumetricViewer. T

- `VolumetricViewerComponent` - a React component for the VolumetricViewer
- `VolumetricViewerData` - a function that returns the data with instantiated models along with the React Component

NOTE: OrbitControls are typically imported as `import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'` but that syntax throws an error due to FEM not being pure ESM, therefore we have inlined `OrbitControls.js` file so that it works without bundling issues. When updating the `three` dependency in `package.json` make sure to copy the file from `node_modules/three/examples/jsm/controls/OrbitControls.js` to `lib-subject-viewers/src/VolumetricViewer/helpers/OrbitControls.js`.
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,37 @@ export default function VolumetricViewer ({
subject
}) {
const { data, loading, error } = useVolumetricSubject({ onError, onReady, subject })

const [modelState] = useState({
annotations: ModelAnnotations({ onAnnotation }),
tool: ModelTool(),
viewer: ModelViewer()
})
const isLoading = loadingState === asyncStates.initialized
|| loadingState === asyncStates.loading
|| loading;
const isError = loadingState === asyncStates.error
|| error
|| data === null;

const isLoading = loadingState === asyncStates.initialized ||
loadingState === asyncStates.loading ||
loading
const isError = loadingState === asyncStates.error ||
error ||
data === null

// Specs should skip rendering the VolumetricViewer component
// WebGL/Canvas throws exceptions when running specs due to non-browser environment
return (data === 'mock-subject-json')
? <div data-testid="subject-viewer-volumetric"></div>
? <div data-testid='subject-viewer-volumetric' />
: (isLoading)
? <p>Loading...</p>
: (isError)
? <p>Error</p>
: <ComponentViewer
data-testid="subject-viewer-volumetric"
config={{}}
data={data}
models={modelState}
/>
? <p>Loading...</p>
: (isError)
? <p>Error</p>
: <ComponentViewer
data-testid='subject-viewer-volumetric'
config={{}}
data={data}
models={modelState}
/>
}

export const VolumetricViewerData = ({
export const VolumetricViewerData = ({
onAnnotation = DEFAULT_HANDLER,
subjectData = '',
subjectUrl = ''
Expand All @@ -69,6 +69,7 @@ export const VolumetricViewerData = ({

VolumetricViewer.propTypes = {
loadingState: string,
onAnnotation: func,
onError: func,
onReady: func,
subject: object
Expand Down
Original file line number Diff line number Diff line change
@@ -1,60 +1,145 @@
import { object, string } from 'prop-types'
import { AlgorithmAStar } from './../helpers/AlgorithmAStar.js'
import { Box } from 'grommet'
import { Cube } from './Cube.js'
import { Histogram } from './Histogram.js'
import { Orientation } from './Orientation.js'
import { object, string } from 'prop-types'
import { Plane } from './Plane.js'
import { Box } from 'grommet'
import styled, { css } from 'styled-components'

const StyledBox = styled(Box)`
${props => props.theme.dark
? css`
background-color: none;
color: #FFFFFF;
`
: css`
background-color: #FFFFFF;
border: 1px solid #E2E5E9;
color: #000000;
`
}

border-bottom-left-radius: 16px;
border-top-left-radius: 16px;
width: 100%;

.volume-container {
background-color: #000000;
position: relative;

.volume-cube {
width: 100%;
}
}

@media (width > 1325px) {
max-width: 975px;

.planes-container {
margin: 20px;
}

.volume-container {
height: 590px;
min-width: 330px;
padding: 20px;

.volume-cube {
max-width: 460px;
}

.volume-controls {
margin-top: 20px;
max-height: 60px;
width: 100%;
}
}
}

@media (width <= 1325px) {
background: none;
border: none;
flex-direction: column-reverse;
max-width: 410px;

.planes-container {
margin: 20px 0 20px 20px;
}

.volume-container {
border-radius: 16px;
border-top-right-radius: 0;
margin: 0 0 0 20px;
width: 390px;

.volume-cube {
max-width: 325px;
width: 100%;
}

.volume-controls {
margin: 20px 0;
width: 90%;
}
}
}
`

export const ComponentViewer = ({
data,
models
}) => {
// Initialize Annotations
if (models.annotations) {
models.annotations.initialize({
algorithm: AlgorithmAStar,
data: [], // will come from Caesar if they exist
viewer: models.viewer
})
}
models.annotations.initialize({
algorithm: AlgorithmAStar,
data: [],
viewer: models.viewer
})

// Initialize Tool
if (models.tool) {
models.tool.initialize({
annotations: models.annotations
})
}
models.tool.initialize({
annotations: models.annotations
})

// Initialize Viewer
if (models.viewer) {
models.viewer.initialize({
annotations: models.annotations,
data,
tool: models.tool
})
}
models.viewer.initialize({
annotations: models.annotations,
data,
tool: models.tool
})

return (
<Box direction='row' style={{ maxWidth: '800px', padding: '20px' }}>
<Box flex>
{models.viewer.dimensions.map((dimensionName, dimension) => {
return (
<Plane
<Box flex align='end'>
<StyledBox direction='row'>
<Box className='planes-container'>
{models.viewer.dimensions.map((dimensionName, dimension) => {
return (
<Plane
annotations={models.annotations}
dimension={dimension}
key={`dimension-${dimensionName}`}
tool={models.tool}
viewer={models.viewer}
/>
)
})}
</Box>
<Box className='volume-container' flex align='center' justify='between'>
<Box className='volume-cube'>
<Cube
annotations={models.annotations}
tool={models.tool}
viewer={models.viewer}
/>
</Box>
<Box className='volume-controls' flex direction='row' justify='between'>
<Orientation />
<Histogram
annotations={models.annotations}
dimension={dimension}
key={`dimension-${dimensionName}`}
tool={models.tool}
viewer={models.viewer}
/>
)
})}
</Box>
<Box flex>
<Cube
annotations={models.annotations}
tool={models.tool}
viewer={models.viewer}
/>
</Box>
</Box>
</Box>
</StyledBox>
</Box>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { object } from 'prop-types'
import { useEffect, useState } from 'react'
import { AnnotationView } from './AnnotationView.js'
import { InputRangeDual } from './InputRangeDual.js'
import { object } from 'prop-types'
import { useEffect, useState } from 'react'

export const Config = ({
annotations,
Expand Down
Loading
Loading