Skip to content

Commit

Permalink
Fix #721
Browse files Browse the repository at this point in the history
Object intersection testing was completly broken for viewer grids.
It is less so now.
  • Loading branch information
dkoes committed Sep 20, 2023
1 parent 145412b commit 4d26447
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 40 deletions.
4 changes: 2 additions & 2 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 py3Dmol/py3Dmol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class view(object):
the exception that the functions all return None.
http://3dmol.org/doc/GLViewer.html
'''
def __init__(self,query='',width=640,height=480,viewergrid=None,data=None,style=None,linked=True,options=dict(),js='https://cdnjs.cloudflare.com/ajax/libs/3Dmol/2.0.3/3Dmol-min.js'):
def __init__(self,query='',width=640,height=480,viewergrid=None,data=None,style=None,linked=True,options=dict(),js='https://cdnjs.cloudflare.com/ajax/libs/3Dmol/2.0.4/3Dmol-min.js'):
'''Create a 3Dmol.js view.
width -- width in pixels of container
height -- height in pixels of container
Expand Down
2 changes: 1 addition & 1 deletion py3Dmol/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
# the version across setup.py and the project code, see
# https://packaging.python.org/en/latest/single_source_version.html
# Keep version in synce with 3Dmol.js version. Use "postX" suffix if needed
version='2.0.3',
version='2.0.4',

description='An IPython interface for embedding 3Dmol.js views in Jupyter notebooks',
long_description=long_description,
Expand Down
101 changes: 65 additions & 36 deletions src/GLViewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ export class GLViewer {

//for grid viewers, return true if point is in this viewer
private isInViewer(x: number, y: number) {
if (this.viewers != undefined && !this.control_all) {
if (this.viewers != undefined) {
var width = this.WIDTH / this.cols;
var height = this.HEIGHT / this.rows;
var offset = this.canvasOffset();
Expand Down Expand Up @@ -590,8 +590,8 @@ export class GLViewer {

this.setupRenderer();

this.row = this.config.row;
this.col = this.config.col;
this.row = this.config.row == undefined ? 0 : this.config.row;
this.col = this.config.col == undefined ? 0 : this.config.col;
this.cols = this.config.cols;
this.rows = this.config.rows;
this.viewers = this.config.viewers;
Expand Down Expand Up @@ -670,14 +670,23 @@ export class GLViewer {
returnsingle = true;
}

let ratioX = this.renderer.getXRatio();
let ratioY = this.renderer.getYRatio();

let col = this.col;
let row = this.row;
let viewxoff = col*(this.WIDTH/ratioX);
//row is from bottom
let viewyoff = (ratioY-row-1)*(this.HEIGHT/ratioY);

let results = [];
let offset = this.canvasOffset();
coords.forEach(coord => {
let t = new Vector3(coord.x, coord.y, coord.z);
t.applyMatrix4(this.modelGroup.matrixWorld);
this.projector.projectVector(t, this.camera);
let screenX = this.WIDTH * (t.x + 1) / 2.0 + offset.left;
let screenY = -this.HEIGHT * (t.y - 1) / 2.0 + offset.top;
let screenX = (this.WIDTH/ratioX) * (t.x + 1) / 2.0 + offset.left + viewxoff;
let screenY = -(this.HEIGHT/ratioY) * (t.y - 1) / 2.0 + offset.top + viewyoff;
results.push({ x: screenX, y: screenY });
});
if (returnsingle) results = results[0];
Expand Down Expand Up @@ -902,11 +911,9 @@ export class GLViewer {
if (this.isDragging && this.scene) { //saw mousedown, haven't moved
var x = this.getX(ev);
var y = this.getY(ev);
if (this.closeEnoughForClick(ev)) {
var offset = this.canvasOffset();
var mouseX = ((x - offset.left) / this.WIDTH) * 2 - 1;
var mouseY = -((y - offset.top) / this.HEIGHT) * 2 + 1;
this.handleClickSelection(mouseX, mouseY, ev);
if (this.closeEnoughForClick(ev) && this.isInViewer(x,y)) {
let mouse = this.mouseXY(x,y);
this.handleClickSelection(mouse.x, mouse.y, ev);
}
}

Expand All @@ -922,7 +929,7 @@ export class GLViewer {
var y = this.getY(ev);
if (x === undefined)
return;
if (!this.isInViewer(x, y)) {
if (!this.control_all && !this.isInViewer(x, y)) {
return;
}

Expand Down Expand Up @@ -1026,43 +1033,64 @@ export class GLViewer {
this.hoverDuration = duration;
};

private mouseXY(x,y) {
//convert to -1..1 coordinates
let offset = this.canvasOffset();
let ratioX = this.renderer.getXRatio();
let ratioY = this.renderer.getYRatio();

let col = this.col;
let row = this.row;
let viewxoff = col*(this.WIDTH/ratioX);
//row is from bottom
let viewyoff = (ratioY-row-1)*(this.HEIGHT/ratioY);

let mouseX = ((x - offset.left-viewxoff) / (this.WIDTH/ratioX)) * 2 - 1;
let mouseY = -((y - offset.top - viewyoff) / (this.HEIGHT/ratioY)) * 2 + 1;

return {x: mouseX, y: mouseY};
}

public _handleMouseMove(ev) { // touchmove

clearTimeout(this.hoverTimeout);
var offset = this.canvasOffset();
var mouseX = ((this.getX(ev) - offset.left) / this.WIDTH) * 2 - 1;
var mouseY = -((this.getY(ev) - offset.top) / this.HEIGHT) * 2 + 1;
ev.preventDefault();


let x = this.getX(ev);
let y = this.getY(ev);
if (x === undefined)
return;

let ratioX = this.renderer.getXRatio();
let ratioY = this.renderer.getYRatio();

let mouse = this.mouseXY(x,y);

let self = this;
// hover timeout
if (this.current_hover !== null) {
this.handleHoverContinue(mouseX, mouseY);
this.handleHoverContinue(mouse.x, mouse.y);
}

var mode = 0;
if (!this.control_all && !this.isInViewer(x, y)) {
return;
}

if (!this.scene)
return;

if (this.hoverables.length > 0) {
this.hoverTimeout = setTimeout(
function () {
self.handleHoverSelection(mouseX, mouseY, ev);
self.handleHoverSelection(mouse.x, mouse.y, ev);
},
this.hoverDuration);
}

ev.preventDefault();
if (!this.scene)
return;
if (!this.isDragging)
return;
var mode = 0;

var x = this.getX(ev);
var y = this.getY(ev);
if (x === undefined)
return;

if (!this.isInViewer(x, y)) {
return;
}


var dx = (x - this.mouseStartX) / this.WIDTH;
var dy = (y - this.mouseStartY) / this.HEIGHT;
// check for pinch
Expand All @@ -1078,8 +1106,7 @@ export class GLViewer {
// translate
mode = 1;
}
var ratioX = this.renderer.getXRatio();
var ratioY = this.renderer.getYRatio();

dx *= ratioX;
dy *= ratioY;
var r = Math.hypot(dx, dy);
Expand Down Expand Up @@ -1127,8 +1154,10 @@ export class GLViewer {
var x = this.mouseStartX;
var y = this.mouseStartY;
var offset = this.canvasOffset();
var mouseX = ((x - offset.left) / this.WIDTH) * 2 - 1;
var mouseY = -((y - offset.top) / this.HEIGHT) * 2 + 1;
let mouse = this.mouseXY(x,y);
let mouseX = mouse.x;
let mouseY = mouse.y;

let intersects = this.targetedObjects(mouseX, mouseY, this.contextMenuEnabledAtoms);
var selected = null;
if (intersects.length) {
Expand Down Expand Up @@ -4715,7 +4744,7 @@ export function createViewerGrid(element, config:ViewerGridSpec={}, viewer_confi
viewer_config.canvas = canvas;
viewer_config.viewers = viewers;
viewer_config.control_all = config.control_all;
var viewer = createViewer(element, viewer_config);
var viewer = createViewer(element, extend({},viewer_config));
row.push(viewer);
}
viewers.unshift(row); //compensate for weird ordering in renderer
Expand Down
95 changes: 95 additions & 0 deletions tests/auto/tests/testgridlabel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
var benz=`
RDKit 3D
6 6 0 0 0 0 0 0 0 0999 V2000
-0.9517 0.7811 -0.6622 C 0 0 0 0 0 0 0 0 0 0 0 0
0.2847 1.3329 -0.3121 C 0 0 0 0 0 0 0 0 0 0 0 0
1.2365 0.5518 0.3512 C 0 0 0 0 0 0 0 0 0 0 0 0
0.9517 -0.7811 0.6644 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.2847 -1.3329 0.3144 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.2365 -0.5518 -0.3489 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 2 0
2 3 1 0
3 4 2 0
4 5 1 0
5 6 2 0
6 1 1 0
M END
$$$$
`

var viewers = $3Dmol.createViewerGrid(
'gldiv', //id of div to create canvas in
{
rows: 2,
cols: 2,
linked: true,
control_all: true //mouse controls all viewers
}
);

var view0 = viewers[0][0];
var view1 = viewers[1][1];

view0.addModel(benz,'sdf')
view0.setStyle({'stick':{}})
view1.addModel(benz,'sdf')
view1.setStyle({'stick':{"colorscheme": "cyanCarbon"}})
view0.setHoverable(
{},
true,
function(atom,viewer,event,container) {
console.log();
if(!atom.label) {
atom.label = viewer.addLabel(atom.atom,{position: atom, backgroundColor: 'mintcream', fontColor:'black'});
viewer.render();
}},
function(atom,viewer) {
if(atom.label) {
viewer.removeLabel(atom.label);
delete atom.label;
}
}
)

view1.setHoverable(
{},
true,
function(atom,viewer,event,container) {
console.log();
if(!atom.label) {
atom.label = viewer.addLabel(atom.atom,{position: atom, backgroundColor: 'cyan', fontColor:'black'});
viewer.render();
}},
function(atom,viewer) {
if(atom.label) {
viewer.removeLabel(atom.label);
delete atom.label;
}
}
)

view0.setClickable({},true,function(atom,viewer) {
viewer.removeAllShapes();
viewer.addSphere({center:atom,radius:1.0,color:'red',alpha:0.4});
viewer.render();
});

view1.setClickable({},true,function(atom,viewer) {
viewer.removeAllShapes();
viewer.addSphere({center:atom,radius:1.0,color:'purple',alpha:0.4});
viewer.render( );
});

view0.zoomTo();
view1.zoomTo();
view0.render( );
view1.render( );

let xy = view0.modelToScreen(view0.models[0].atoms[1])
view0._handleMouseDown({pageX: xy.x, pageY: xy.y, preventDefault: function(){}});
view0._handleMouseUp({pageX: xy.x, pageY: xy.y, preventDefault: function(){}});

xy = view1.modelToScreen(view1.models[0].atoms[0])
view1._handleMouseDown({pageX: xy.x, pageY: xy.y, preventDefault: function(){}});
view1._handleMouseUp({pageX: xy.x, pageY: xy.y, preventDefault: function(){}});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 4d26447

Please sign in to comment.