diff --git a/Sources/MainView.js b/Sources/MainView.js
index bee75251..e63f276e 100644
--- a/Sources/MainView.js
+++ b/Sources/MainView.js
@@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
import { Layout, Menu, Progress } from 'antd';
import Layouts from './layouts';
+import LayoutConfig from './config/glanceLayoutConfig';
import style from './pv-explorer.mcss';
import icons from './icons';
@@ -13,7 +14,9 @@ import Controls from './controls';
const { Header, Sider, Content } = Layout;
-const layouts = ['Layout2D', 'Layout3D', 'LayoutSplit', 'LayoutQuad'];
+const { LayoutGrid } = Layouts;
+
+const layouts = ['2D', '3D', 'Split', 'Quad'];
const WIDTH = 300;
@@ -21,7 +24,7 @@ export default class MainView extends React.Component {
constructor(props) {
super(props);
this.state = {
- layout: 'Layout3D',
+ layout: '3D',
overlayOpacity: 100,
collapsed: false,
tab: 'files',
@@ -36,10 +39,7 @@ export default class MainView extends React.Component {
}
onLayoutChange({ item, key, selectedKeys }) {
- this.setState({ layout: key }, () => {
- this.forceUpdate();
- this.props.proxyManager.createRepresentationInAllViews();
- });
+ this.setState({ layout: key });
}
onToggleControl() {
@@ -50,7 +50,6 @@ export default class MainView extends React.Component {
}
render() {
- const Renderer = Layouts[this.state.layout];
let progress = null;
if (this.state.showProgress) {
@@ -80,7 +79,7 @@ export default class MainView extends React.Component {
))}
@@ -105,9 +104,11 @@ export default class MainView extends React.Component {
-
diff --git a/Sources/config/glanceLayoutConfig.js b/Sources/config/glanceLayoutConfig.js
new file mode 100644
index 00000000..35b726bf
--- /dev/null
+++ b/Sources/config/glanceLayoutConfig.js
@@ -0,0 +1,98 @@
+import Layout2D from '../layouts/Layout2D';
+import Layout3D from '../layouts/Layout3D';
+
+export default {
+ layouts: {
+ '2D': {
+ grid: [1, 1],
+ views: {
+ // stacked
+ sliceX: { cell: [1, 1] },
+ sliceY: { cell: [1, 1], defaultVisible: true, defaultActive: true },
+ sliceZ: { cell: [1, 1] },
+ },
+ },
+ '3D': {
+ grid: [1, 1],
+ views: {
+ volume: { cell: [1, 1], defaultActive: true },
+ },
+ },
+ Split: {
+ grid: [1, 2],
+ views: {
+ // stacked
+ sliceX: { cell: [1, 1] },
+ sliceY: { cell: [1, 1], defaultVisible: true },
+ sliceZ: { cell: [1, 1] },
+
+ volume: { cell: [1, 2], defaultActive: true },
+ },
+ },
+ Quad: {
+ grid: [2, 2],
+ views: {
+ sliceX: {
+ cell: [2, 1],
+ propOverrides: {
+ viewType: 'View2D_X',
+ title: '-Z+Y',
+ orientations: [],
+ },
+ },
+ sliceY: {
+ cell: [1, 1],
+ propOverrides: {
+ viewType: 'View2D_Y',
+ title: '-Z+X',
+ orientations: [],
+ },
+ },
+ sliceZ: {
+ cell: [2, 2],
+ propOverrides: {
+ viewType: 'View2D_Z',
+ title: '+X+Y',
+ orientations: [],
+ },
+ },
+ volume: { cell: [1, 2], defaultActive: true },
+ },
+ },
+ },
+ views: {
+ sliceX: {
+ component: Layout2D,
+ props: {
+ axis: 0,
+ orientation: 1,
+ viewUp: [0, 0, 1],
+ onAxisChange: (axis) => ['sliceX', 'sliceY', 'sliceZ'][axis],
+ },
+ stackChangeFunc: 'onAxisChange',
+ },
+ sliceY: {
+ component: Layout2D,
+ props: {
+ axis: 1,
+ orientation: 1,
+ viewUp: [0, -1, 0],
+ onAxisChange: (axis) => ['sliceX', 'sliceY', 'sliceZ'][axis],
+ },
+ stackChangeFunc: 'onAxisChange',
+ },
+ sliceZ: {
+ component: Layout2D,
+ props: {
+ axis: 2,
+ orientation: 1,
+ viewUp: [0, -1, 0],
+ onAxisChange: (axis) => ['sliceX', 'sliceY', 'sliceZ'][axis],
+ },
+ stackChangeFunc: 'onAxisChange',
+ },
+ volume: {
+ component: Layout3D,
+ },
+ },
+};
diff --git a/Sources/layouts/Layout2D.js b/Sources/layouts/Layout2D.js
index 991806cc..2e730c4d 100644
--- a/Sources/layouts/Layout2D.js
+++ b/Sources/layouts/Layout2D.js
@@ -37,7 +37,6 @@ export default class Layout2D extends React.Component {
this.onActiveSourceChange = this.onActiveSourceChange.bind(this);
this.rotate = this.rotate.bind(this);
this.toggleOrientationMarker = this.toggleOrientationMarker.bind(this);
- this.updateOrientation = this.updateOrientation.bind(this);
this.activateView = this.activateView.bind(this);
this.flush = () => this.forceUpdate();
@@ -158,26 +157,9 @@ export default class Layout2D extends React.Component {
}
}
- updateOrientation(e) {
- const state = this.props.orientations[Number(e.target.dataset.index)];
- this.view.updateOrientation(state.axis, state.orientation, state.viewUp);
- const reps = this.view.getRepresentations();
- for (let i = 0; i < reps.length; i++) {
- if (reps[i].setSlicingMode) {
- reps[i].setSlicingMode('XYZ'[state.axis]);
- }
- }
- this.props.proxyManager.modified();
- this.view.resetCamera();
- this.view.renderLater();
- this.onActiveSourceChange();
- this.updateSlider();
-
- // Update control panel
- this.props.proxyManager.modified();
-
- // Update slider
- this.forceUpdate();
+ resize() {
+ this.view.resize();
+ this.slider.resize();
}
rotate() {
@@ -217,7 +199,7 @@ export default class Layout2D extends React.Component {
key={o.label}
className={style.button}
data-index={i}
- onClick={this.updateOrientation}
+ onClick={() => this.props.onAxisChange(o.axis)}
>
{o.label}
@@ -269,6 +251,7 @@ Layout2D.propTypes = {
orientations: PropTypes.array,
activateOnMount: PropTypes.bool,
viewType: PropTypes.string,
+ onAxisChange: PropTypes.func,
};
Layout2D.defaultProps = {
@@ -285,4 +268,5 @@ Layout2D.defaultProps = {
{ label: 'Z', axis: 2, orientation: 1, viewUp: [0, -1, 0] },
],
activateOnMount: false,
+ onAxisChange: () => {},
};
diff --git a/Sources/layouts/Layout3D.js b/Sources/layouts/Layout3D.js
index 41bb817d..be1836ad 100644
--- a/Sources/layouts/Layout3D.js
+++ b/Sources/layouts/Layout3D.js
@@ -59,6 +59,10 @@ export default class Layout3D extends React.Component {
this.props.proxyManager.setActiveView(this.view);
}
+ resize() {
+ this.view.resize();
+ }
+
render() {
return (
{
+ const layout = this.config.layouts[layoutName];
+
+ // set initial active view, if any
+ const viewName = Object.keys(layout.views).find(
+ (name) => !!layout.views[name].defaultActive
+ );
+ if (viewName) {
+ this.layoutInitActiveView[layoutName] = viewName;
+ }
+
+ // process stacks
+ const gridY = layout.grid[1];
+ const tmpStacks = {};
+ Object.keys(layout.views).forEach((name) => {
+ const view = layout.views[name];
+ const [x, y] = view.cell;
+ const stackId = x * gridY + y;
+ tmpStacks[stackId] = tmpStacks[stackId] || [];
+ tmpStacks[stackId].push(name);
+ });
+
+ this.layoutStacks[layoutName] = {};
+ Object.keys(tmpStacks).forEach((stackId) => {
+ const stack = tmpStacks[stackId];
+ if (stack.length > 1) {
+ const id = `StackActive_${layoutName}_${stackId}`;
+ stack.forEach((view) => {
+ // set stack id for this view in this layout
+ this.layoutStacks[layoutName][view] = id;
+ // set view as visible in stack if possible
+ if (layout.views[view].defaultVisible || !this.state[id]) {
+ this.state[id] = view;
+ }
+ });
+ }
+ });
+ });
+ }
+
+ componentDidUpdate() {
+ // resize all so views fill their containing space
+ Object.values(this.viewRefs)
+ .filter((v) => !!v)
+ .forEach((view) => view.resize());
+
+ // activate initial view, if specified
+ if (this.props.layout in this.layoutInitActiveView) {
+ let viewName = this.layoutInitActiveView[this.props.layout];
+
+ // if initial view is shadowed by another view in same stack,
+ // then activate the other view.
+ const stackId = this.layoutStacks[this.props.layout][viewName];
+ if (stackId) {
+ // if active view in stack is current view, then this is a no-op.
+ viewName = this.state[stackId];
+ }
+
+ this.viewRefs[viewName].activateView();
+ }
+ }
+
+ setStackView(newView) {
+ const stackId = this.layoutStacks[this.props.layout][newView];
+ if (stackId in this.state) {
+ this.setState({ [stackId]: newView }, () => {
+ this.viewRefs[newView].activateView();
+ });
+ }
+ }
+
+ setViewRef(name, ref) {
+ this.viewRefs[name] = ref;
+ }
+
+ render() {
+ const layout = this.config.layouts[this.props.layout];
+
+ const [xRows, yRows] = layout.grid;
+ const rootStyles = {
+ gridTemplate: `repeat(${xRows}, 1fr) / repeat(${yRows}, 1fr)`,
+ };
+
+ const views = Object.keys(this.config.views).map((viewName) => {
+ const viewSpec = this.config.views[viewName];
+ const props = Object.assign({}, viewSpec.props);
+ const cellStyle = {};
+
+ let visible = viewName in layout.views;
+ if (visible) {
+ const layoutSpec = layout.views[viewName];
+
+ if (viewName in this.layoutStacks[this.props.layout]) {
+ const stackId = this.layoutStacks[this.props.layout][viewName];
+ visible = this.state[stackId] === viewName;
+ }
+
+ const [xCell, yCell] = layoutSpec.cell;
+ Object.assign(cellStyle, {
+ gridArea: `${xCell} / ${yCell} / ${xCell + 1} / ${yCell + 1}`,
+ });
+
+ Object.assign(props, layoutSpec.propOverrides);
+
+ // set our custom stack changing function
+ if (viewSpec.stackChangeFunc && props[viewSpec.stackChangeFunc]) {
+ const func = props[viewSpec.stackChangeFunc];
+ props[viewSpec.stackChangeFunc] = (...args) => {
+ this.setStackView(func(...args));
+ };
+ }
+ }
+
+ return (
+
+ this.setViewRef(viewName, r)}
+ proxyManager={this.props.proxyManager}
+ {...props}
+ />
+
+ );
+ });
+
+ return (
+
+ {views}
+
+ );
+ }
+}
+
+LayoutGrid.propTypes = {
+ initialConfig: PropTypes.object.isRequired,
+ layout: PropTypes.string.isRequired,
+ proxyManager: PropTypes.object,
+ className: PropTypes.string,
+};
+
+LayoutGrid.defaultProps = {
+ proxyManager: null,
+ className: '',
+};
diff --git a/Sources/layouts/LayoutQuad.js b/Sources/layouts/LayoutQuad.js
deleted file mode 100644
index 4c4efb63..00000000
--- a/Sources/layouts/LayoutQuad.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import Layout2D from './Layout2D';
-import Layout3D from './Layout3D';
-import style from './vtk-layout.mcss';
-
-export default function LayoutQuad(props) {
- return (
-
- );
-}
-
-LayoutQuad.propTypes = {
- proxyManager: PropTypes.object,
- className: PropTypes.string,
-};
-
-LayoutQuad.defaultProps = {
- proxyManager: null,
- className: '',
-};
diff --git a/Sources/layouts/LayoutSplit.js b/Sources/layouts/LayoutSplit.js
deleted file mode 100644
index 4d71b077..00000000
--- a/Sources/layouts/LayoutSplit.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import Layout2D from './Layout2D';
-import Layout3D from './Layout3D';
-import style from './vtk-layout.mcss';
-
-export default function LayoutSplit(props) {
- return (
-
- );
-}
-
-LayoutSplit.propTypes = {
- proxyManager: PropTypes.object,
- className: PropTypes.string,
-};
-
-LayoutSplit.defaultProps = {
- proxyManager: null,
- className: '',
-};
diff --git a/Sources/layouts/index.js b/Sources/layouts/index.js
index 48060428..a6a80d65 100644
--- a/Sources/layouts/index.js
+++ b/Sources/layouts/index.js
@@ -1,11 +1,9 @@
import Layout2D from './Layout2D';
import Layout3D from './Layout3D';
-import LayoutSplit from './LayoutSplit';
-import LayoutQuad from './LayoutQuad';
+import LayoutGrid from './LayoutGrid';
export default {
Layout2D,
Layout3D,
- LayoutSplit,
- LayoutQuad,
+ LayoutGrid,
};
diff --git a/Sources/layouts/vtk-layout.mcss b/Sources/layouts/vtk-layout.mcss
index 928fccda..32244fbc 100644
--- a/Sources/layouts/vtk-layout.mcss
+++ b/Sources/layouts/vtk-layout.mcss
@@ -1,12 +1,11 @@
-.quadRoot {
+.layoutRoot {
position: relative;
- display: flex;
- flex-direction: column;
- align-items: stretch;
height: 100%;
+ display: grid;
}
.splitRow {
+ min-height: 0;
position: relative;
flex: 1;
display: flex;
@@ -14,6 +13,16 @@
align-items: stretch;
}
+.viewContainer {
+ min-width: 0;
+ min-height: 0;
+ display: flex;
+}
+
+.hiddenViewContainer {
+ display: none;
+}
+
.view {
flex: 1;
min-width: 10px;
@@ -22,7 +31,6 @@
.renderWindowContainer {
flex: 1;
- height: 100%;
display: flex;
flex-direction: column;
align-items: stretch;
diff --git a/Sources/pv-explorer.mcss b/Sources/pv-explorer.mcss
index 08475733..4427b23d 100644
--- a/Sources/pv-explorer.mcss
+++ b/Sources/pv-explorer.mcss
@@ -89,6 +89,7 @@
.workspace {
overflow: hidden;
height: calc(100vh - 64px);
+ max-height: calc(100vh - 64px);
}
.progressContainer {