Skip to content

Commit 268f2d4

Browse files
committed
fix(MultiViewRoot): consolidate renders on resize
MultiViewRoot manages the resize listener for descendants, in order to batch render calls efficiently.
1 parent e2d5ec2 commit 268f2d4

3 files changed

Lines changed: 75 additions & 53 deletions

File tree

src/core/MultiViewRoot.js

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export default class MultiViewRoot extends Component {
3636
constructor(props) {
3737
super(props);
3838
this.containerRef = React.createRef();
39+
this.containerToRenderer = new Map();
3940

4041
// Create vtk.js view
4142
this.renderWindow = vtkRenderWindow.newInstance();
@@ -45,7 +46,7 @@ export default class MultiViewRoot extends Component {
4546
this.renderWindow.addView(this.renderWindowView);
4647

4748
this.resizeObserver = new ResizeObserver((entries) => {
48-
this.onResize();
49+
this.onResize(entries);
4950
});
5051

5152
this.interactor = vtkRenderWindowInteractor.newInstance();
@@ -65,7 +66,7 @@ export default class MultiViewRoot extends Component {
6566
this.interactor.initialize();
6667

6768
this.resizeObserver.observe(container);
68-
this.onResize();
69+
this.resizeRootContainer();
6970

7071
this.initialized = true;
7172

@@ -129,18 +130,58 @@ export default class MultiViewRoot extends Component {
129130
}
130131
}
131132

132-
onResize() {
133-
const container = this.containerRef.current;
134-
if (container) {
133+
observeRendererResize(container, renderer) {
134+
if (!this.containerToRenderer.has(container)) {
135+
this.containerToRenderer.set(container, renderer);
136+
this.resizeObserver.observe(container);
137+
}
138+
}
139+
140+
unobserveRendererResize(container) {
141+
this.containerToRenderer.delete(container);
142+
this.resizeObserver.unobserve(container);
143+
}
144+
145+
resizeRootContainer() {
146+
const rootContainer = this.containerRef.current;
147+
// resize the render window
148+
if (rootContainer) {
135149
const devicePixelRatio = window.devicePixelRatio || 1;
136-
const { width, height } = container.getBoundingClientRect();
150+
const { width, height } = rootContainer.getBoundingClientRect();
137151
const w = Math.floor(width * devicePixelRatio);
138152
const h = Math.floor(height * devicePixelRatio);
139153
this.renderWindowView.setSize(Math.max(w, 10), Math.max(h, 10));
140-
this.renderWindow.render();
141154
}
142155
}
143156

157+
onResize(entries) {
158+
entries.forEach((entry) => {
159+
const rootContainer = this.containerRef.current;
160+
if (entry.target === rootContainer) {
161+
this.resizeRootContainer();
162+
} else if (this.containerToRenderer.has(entry.target)) {
163+
// update that renderer's viewport
164+
const renderer = this.containerToRenderer.get(entry.target);
165+
const containerBox = entry.target.getBoundingClientRect();
166+
const canvasBox = this.renderWindowView
167+
.getCanvas()
168+
.getBoundingClientRect();
169+
170+
// relative to canvas
171+
const top = containerBox.top - canvasBox.top;
172+
const left = containerBox.left - canvasBox.left;
173+
174+
const xmin = left / canvasBox.width;
175+
const xmax = (left + containerBox.width) / canvasBox.width;
176+
const ymin = 1 - (top + containerBox.height) / canvasBox.height;
177+
const ymax = 1 - top / canvasBox.height;
178+
179+
renderer.setViewport(xmin, ymin, xmax, ymax);
180+
}
181+
});
182+
this.renderWindow.render();
183+
}
184+
144185
update(props, previous) {
145186
const { triggerRender } = props;
146187
// Allow to trigger method call from property change

src/core/View.js

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,6 @@ export default class View extends Component {
160160
);
161161
this.selector.attach(this.openglRenderWindow, this.renderer);
162162

163-
// Resize handling
164-
this.resizeObserver = new ResizeObserver(() => this.onResize());
165-
166163
// expose helper methods
167164
this.renderView = () => {
168165
this.updateCubeBounds();
@@ -351,10 +348,6 @@ export default class View extends Component {
351348
return position;
352349
}
353350

354-
onResize() {
355-
this.props.onResize();
356-
}
357-
358351
render() {
359352
const { id, children, style, className } = this.props;
360353

@@ -378,8 +371,6 @@ export default class View extends Component {
378371

379372
componentDidMount() {
380373
const container = this.containerRef.current;
381-
this.onResize();
382-
this.resizeObserver.observe(container);
383374
document.addEventListener('keyup', this.handleKey);
384375

385376
// Assign the mouseDown event, we can't use the React event system
@@ -412,9 +403,6 @@ export default class View extends Component {
412403
container.removeEventListener('mousedown', this.onMouseDown);
413404

414405
document.removeEventListener('keyup', this.handleKey);
415-
// Stop size listening
416-
this.resizeObserver.disconnect();
417-
this.resizeObserver = null;
418406

419407
this.selector.delete();
420408
this.orientationWidget.delete();

src/core/ViewContainer.js

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class ViewController extends Component {
1414

1515
this.renderer = vtkRenderer.newInstance();
1616
this.viewRef = React.createRef();
17+
this.resizeObserver = new ResizeObserver(() => this.onResize());
1718

1819
if (props.root) {
1920
this.renderWindow = props.root.renderWindow;
@@ -41,15 +42,21 @@ class ViewController extends Component {
4142
this.renderWindow.addRenderer(this.renderer);
4243

4344
const view = this.viewRef.current;
44-
const container = view.containerRef.current;
45-
container.addEventListener('pointerenter', this.onEnter);
46-
47-
if (!this.props.root) {
48-
this.openglRenderWindow.setContainer(container);
49-
if (this.props.interactive) {
50-
this.interactor.bindEvents(container);
45+
const container = view?.containerRef.current;
46+
if (view && container) {
47+
container.addEventListener('pointerenter', this.onEnter);
48+
if (this.props.root) {
49+
this.props.root.observeRendererResize(container, this.renderer);
50+
} else {
51+
this.resizeObserver.observe(container);
52+
this.openglRenderWindow.setContainer(container);
53+
if (this.props.interactive) {
54+
this.interactor.bindEvents(container);
55+
}
56+
this.interactor.setInteractorStyle(view.style);
57+
// initial resize
58+
this.onResize();
5159
}
52-
this.interactor.setInteractorStyle(view.style);
5360
}
5461
}
5562

@@ -58,6 +65,11 @@ class ViewController extends Component {
5865
const container = view.containerRef.current;
5966
container.removeEventListener('pointerenter', this.onEnter);
6067

68+
this.resizeObserver.disconnect();
69+
if (this.props.root) {
70+
this.props.root.unobserveRendererResize(container);
71+
}
72+
6173
// MultiViewRoot parent may delete the render window first in WillUnmount.
6274
if (!this.renderWindow.isDeleted()) {
6375
this.renderWindow.removeRenderer(this.renderer);
@@ -99,7 +111,6 @@ class ViewController extends Component {
99111
renderer={this.renderer}
100112
interactor={this.interactor}
101113
ref={this.viewRef}
102-
onResize={this.onResize}
103114
{...filteredProps}
104115
/>
105116
);
@@ -129,31 +140,13 @@ class ViewController extends Component {
129140

130141
onResize() {
131142
const container = this.viewRef.current?.containerRef.current;
132-
if (container) {
133-
if (this.props.root) {
134-
const containerBox = container.getBoundingClientRect();
135-
const canvasBox = this.openglRenderWindow
136-
.getCanvas()
137-
.getBoundingClientRect();
138-
139-
// relative to canvas
140-
const top = containerBox.top - canvasBox.top;
141-
const left = containerBox.left - canvasBox.left;
142-
143-
const xmin = left / canvasBox.width;
144-
const xmax = (left + containerBox.width) / canvasBox.width;
145-
const ymin = 1 - (top + containerBox.height) / canvasBox.height;
146-
const ymax = 1 - top / canvasBox.height;
147-
148-
this.renderer.setViewport(xmin, ymin, xmax, ymax);
149-
} else {
150-
const devicePixelRatio = window.devicePixelRatio || 1;
151-
const { width, height } = container.getBoundingClientRect();
152-
const w = Math.floor(width * devicePixelRatio);
153-
const h = Math.floor(height * devicePixelRatio);
154-
this.openglRenderWindow.setSize(Math.max(w, 10), Math.max(h, 10));
155-
this.renderWindow.render();
156-
}
143+
if (container && !this.props.root) {
144+
const devicePixelRatio = window.devicePixelRatio || 1;
145+
const { width, height } = container.getBoundingClientRect();
146+
const w = Math.floor(width * devicePixelRatio);
147+
const h = Math.floor(height * devicePixelRatio);
148+
this.openglRenderWindow.setSize(Math.max(w, 10), Math.max(h, 10));
149+
this.renderWindow.render();
157150
}
158151
}
159152
}

0 commit comments

Comments
 (0)