Skip to content

Commit

Permalink
Regenerate lost contexts
Browse files Browse the repository at this point in the history
Issue #795.  When a context is lost, go through all visible contexts
(up to the first 16) and regenerate them if needed.

If a browser supports fewer than 16 contexts and too many contexts are
on the screen there will be an infinite loop of context regeneration,
but at least on the desktop 16 seems like a safe lower bound.
  • Loading branch information
dkoes committed Jul 25, 2024
1 parent c77554e commit 55ee641
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 40 deletions.
76 changes: 54 additions & 22 deletions src/GLViewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { GLVolumetricRender, VolumetricRendererSpec } from "./VolumetricRender";
import { AtomSelectionSpec, AtomSpec } from "./specs";
import { decode, toRGBA8, encode } from 'upng-js'

export const CONTEXTS_PER_VIEWPORT = 16;

/**
* WebGL-based 3Dmol.js viewer
Expand Down Expand Up @@ -117,8 +118,8 @@ export class GLViewer {
private getWidth() {
let div = this.container;
//offsetwidth accounts for scaling
let w = div.offsetWidth;
if(w == 0 && div.style.display === 'none') {
let w = div.offsetWidth;
if (w == 0 && div.style.display === 'none') {
let oldpos = div.style.position;
let oldvis = div.style.visibility;
div.style.display = 'block';
Expand All @@ -127,15 +128,15 @@ export class GLViewer {
w = div.offsetWidth;
div.style.display = 'none';
div.style.visibility = oldvis;
div.style.position = oldpos;
div.style.position = oldpos;
}
return w;
};

private getHeight() {
let div = this.container;
let h = div.offsetHeight;
if(h == 0 && div.style.display === 'none') {
let h = div.offsetHeight;
if (h == 0 && div.style.display === 'none') {
let oldpos = div.style.position;
let oldvis = div.style.visibility;
div.style.display = 'block';
Expand All @@ -144,7 +145,7 @@ export class GLViewer {
h = div.offsetHeight;
div.style.display = 'none';
div.style.visibility = oldvis;
div.style.position = oldpos;
div.style.position = oldpos;
}
return h;
};
Expand Down Expand Up @@ -195,6 +196,34 @@ export class GLViewer {
this.scene.add(directionalLight);
};

private _handleLostContext(event) {

//when contexts go missing, try to regenerate any that are visible on screen
//but no more than CONTEXTS_PER_VIEWPORT (if this is set higher than the
//browser limit there will be an infinity loop of refreshing contexts of
//too many are on screen)
const isVisible = function (cont) {
const rect = cont.getBoundingClientRect();
return !(
rect.right < 0 ||
rect.bottom < 0 ||
rect.top > (window.innerHeight || document.documentElement.clientHeight) ||
rect.left > (window.innerWidth || document.documentElement.clientWidth)
);
};

if (isVisible(this.container)) {
let restored = 0;
for(let c of document.getElementsByTagName('canvas')) {
if( isVisible(c) && (c as any)._3dmol_viewer != undefined) {
(c as any)._3dmol_viewer.resize();
restored += 1;
if(restored >= CONTEXTS_PER_VIEWPORT) break;
}
}
}
}

private initContainer(element) {
this.container = element;
this.WIDTH = this.getWidth();
Expand All @@ -204,6 +233,9 @@ export class GLViewer {
this.container.append(this.renderer.domElement);
this.glDOM = this.renderer.domElement;

(this.glDOM as any)._3dmol_viewer = this;
this.glDOM.addEventListener("webglcontextlost", this._handleLostContext.bind(this));

if (!this.nomouse) {
// user can request that the mouse handlers not be installed
this.glDOM.addEventListener('mousedown', this._handleMouseDown.bind(this), { passive: false });
Expand Down Expand Up @@ -656,15 +688,15 @@ export class GLViewer {
//make sure a viewer that is becoming visible is alive
let intcallback = (entries, observer) => {
entries.forEach((entry) => {
if(entry.isIntersecting) {
if (entry.isIntersecting) {
this.resize();
}
});
};
this.intwatcher = new window.IntersectionObserver(intcallback);
this.intwatcher.observe(this.container);
}

try {
if (typeof (this.callback) === "function")
this.callback(this);
Expand Down Expand Up @@ -1363,12 +1395,12 @@ export class GLViewer {
//create new context
let resetcanvas = false;
let currentcanvas = this.container.querySelector('canvas');
if(currentcanvas && currentcanvas != this.renderer.getCanvas()) {
if (currentcanvas && currentcanvas != this.renderer.getCanvas()) {
//canvas has been replaced, use new one
this.config.canvas = currentcanvas;
} else {
currentcanvas.remove(); //remove existing
if(this.config && this.config.canvas != undefined) {
if (this.config && this.config.canvas != undefined) {
delete this.config.canvas;
resetcanvas = true;
}
Expand All @@ -1378,7 +1410,7 @@ export class GLViewer {
this.renderer.setClearColorHex(this.bgColor, this.config.backgroundAlpha);

regen = true;
if(resetcanvas) {
if (resetcanvas) {
this.config.canvas = this.renderer.getCanvas();
}
}
Expand All @@ -1392,10 +1424,10 @@ export class GLViewer {
if (regen) { //restored rendere, need to regenerate scene
let options = this.renderer.supportedExtensions();
options.regen = true;
if(this.viewers) {
for(let i = 0, n = this.viewers.length; i < n; i++) {
for(let j = 0, m = this.viewers[i].length; j < m; j++) {
this.viewers[i][j].render(null, options);
if (this.viewers) {
for (let i = 0, n = this.viewers.length; i < n; i++) {
for (let j = 0, m = this.viewers[i].length; j < m; j++) {
this.viewers[i][j].render(null, options);
}
}
}
Expand Down Expand Up @@ -1748,7 +1780,7 @@ export class GLViewer {
}

for (i = 0; i < this.labels.length; i++) {
if(exts.regen) {
if (exts.regen) {
this.labels[i].dispose();
this.modelGroup.remove(this.labels[i].sprite);
this.labels[i].setContext();
Expand Down Expand Up @@ -2230,7 +2262,7 @@ export class GLViewer {
$3Dmol.get('data/1fas.pqr', function(data){
viewer.addModel(data, "pqr");
viewer.zoomTo();
viewer.zoomTo();
$3Dmol.get("data/1fas.cube",function(volumedata){
viewer.addSurface($3Dmol.SurfaceType.VDW, {
opacity:0.85,
Expand Down Expand Up @@ -3077,9 +3109,9 @@ export class GLViewer {

let newatoms = [];
for (let a = 0; a < atoms.length; a++) {
let newx = atoms[a].x+offset.x,
newy = atoms[a].y+offset.y,
newz = atoms[a].z+offset.z;
let newx = atoms[a].x + offset.x,
newy = atoms[a].y + offset.y,
newz = atoms[a].z + offset.z;
if (omitPosition(newx, newy, newz)) {
continue;
}
Expand Down Expand Up @@ -3368,7 +3400,7 @@ export class GLViewer {
if (options.interval) {
interval = options.interval;
}
if (options.loop) {``
if (options.loop) {
loop = options.loop;
}
if (options.reps) {
Expand Down Expand Up @@ -4166,7 +4198,7 @@ export class GLViewer {
*/
private static getMatWithStyle(style: SurfaceStyleSpec) {
let mat = null;
if(style.onesided) {
if (style.onesided) {
mat = new MeshLambertMaterial();
} else {
mat = new MeshDoubleLambertMaterial();
Expand Down
36 changes: 18 additions & 18 deletions tests/webpages/multi.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,41 @@
<body>
<script src="../../build/3Dmol.js"></script>
<div class='viewer_3Dmoljs' id="1" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-cid='2519'
data-style='stick' data-spin='axis:z;speed:100'></div><br>
data-style='stick' data-spin='axis:z;speed:100'></div>
<div class='viewer_3Dmoljs' id="2" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-cid='2518'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="3" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-cid='1000'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="4" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-cid='1001'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="5" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-cid='2515'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="6" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='1ycr'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="7" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='1ycr'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="8" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='1ycr'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="9" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='1ycr'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="10" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='2i2j'
data-style='cartoon'></div><br>
data-style='cartoon'></div>
<div class='viewer_3Dmoljs' id="11" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='2n4f'
data-style='cartoon'></div><br>
data-style='cartoon'></div>
<div class='viewer_3Dmoljs' id="12" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='4knk'
data-style='cartoon'></div><br>
data-style='cartoon'></div>
<div class='viewer_3Dmoljs' id="13" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='6c94'
data-style='cartoon'></div><br>
data-style='cartoon'></div>
<div class='viewer_3Dmoljs' id="14" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='2por'
data-style='cartoon'></div><br>
data-style='cartoon'></div>
<div class='viewer_3Dmoljs' id="15" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='1pfn'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="16" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='1ubq'
data-style='stick'></div> <br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="17" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='5tup'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="18" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='1ycr'
data-style='stick'></div><br>
data-style='stick'></div>
<div class='viewer_3Dmoljs' id="19" style="height: 300px; width: 300px; display: inline-block; position: relative;" data-pdb='3erk'
data-style='stick'></div>
</body>
Expand Down

0 comments on commit 55ee641

Please sign in to comment.