Skip to content

Commit

Permalink
Various improvements
Browse files Browse the repository at this point in the history
py3dmol.view takes format argument
can pass selection for volumetricrender/isosurfaces
gradient documentation
onesided surface option
custom linear builtin
  • Loading branch information
dkoes committed Mar 14, 2024
1 parent cf59124 commit dbf2efd
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 29 deletions.
13 changes: 9 additions & 4 deletions py3Dmol/py3Dmol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,14 @@ 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.1.0/3Dmol-min.js'):
def __init__(self,query='',width=640,height=480,viewergrid=None,data=None,style=None,linked=True,options=dict(),format=None,js='https://cdnjs.cloudflare.com/ajax/libs/3Dmol/2.1.0/3Dmol-min.js'):
'''Create a 3Dmol.js view.
width -- width in pixels of container
height -- height in pixels of container
query -- optional argument to provide to $3Dmol.download
viewergrid -- optional tuple (rows,columns) to define grid
data -- molecular data to provide to addModel, wit viewer grid can be indexed (r,c)
data -- molecular data to provide to addModel, with viewer grid can be indexed (r,c)
format -- format of provided data
style -- style to apply, with viewer grid can be indexed (r,c)
options -- optional options to provide to $3Dmol.download
js -- url for 3Dmol.js'''
Expand Down Expand Up @@ -148,7 +149,11 @@ def __init__(self,query='',width=640,height=480,viewergrid=None,data=None,style=
d = data[r][c]
except:
d = data
self.startjs += "viewergrid_UNIQUEID[%d][%d].addModel(%s);\n"%(r,c,json.dumps(d))
try:
f = format[r][c]
except:
f = format
self.startjs += f"viewergrid_UNIQUEID[{r}][{c}].addModel({json.dumps(d)}{','+json.dumps(f) if f else ''});\n"
if style:
try:
s = style[r][c]
Expand All @@ -160,7 +165,7 @@ def __init__(self,query='',width=640,height=480,viewergrid=None,data=None,style=
else:
cmds = ''
if data:
cmds = "viewer_UNIQUEID.addModel(%s);\n"%json.dumps(data)
cmds = f"viewer_UNIQUEID.addModel({json.dumps(data)}{','+json.dumps(format) if format else ''});\n"
if style:
cmds += "viewer_UNIQUEID.setStyle(%s);\n"%json.dumps(style)
self.startjs += cmds + "viewer_UNIQUEID.zoomTo();\n"
Expand Down
15 changes: 13 additions & 2 deletions src/GLShape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { CAP, GLDraw } from "./GLDraw"
import { subdivide_spline } from "./glcartoon";
import { adjustVolumeStyle, extend, Func, makeFunction } from "./utilities";
import { GradientType } from "./Gradient";
import { AtomSelectionSpec } from "specs";
import { GLViewer } from "GLViewer";


/**
Expand Down Expand Up @@ -1215,7 +1217,7 @@ export class GLShape {
};

/**
* Create isosurface from voluemetric data.
* Create isosurface from volumetric data.
* @param {VolumeData} data - volumetric input data
* @param {IsoSurfaceSpec} isoSpec - volumetric data shape specification
* @example //the user can specify a selected region for the isosurface
Expand All @@ -1241,7 +1243,7 @@ export class GLShape {
viewer.render();
});
*/
addIsosurface(data, volSpec:IsoSurfaceSpec, callback?) {//may want to cache the arrays geneerated when selectedRegion ==true
addIsosurface(data, volSpec:IsoSurfaceSpec, callback?, viewer?: GLViewer) {//may want to cache the arrays generated when selectedRegion ==true

var isoval = (volSpec.isoval !== undefined && typeof (volSpec.isoval) === "number") ? volSpec.isoval
: 0.0;
Expand Down Expand Up @@ -1290,6 +1292,13 @@ export class GLShape {
if (volSpec.selectedRegion && volSpec.coords === undefined) {
volSpec.coords = volSpec.selectedRegion; //backwards compat for incorrectly documented feature
}
if (volSpec.coords === undefined && volSpec.selection !== undefined) {
if(!viewer) {
console.log("addIsosurface needs viewer is selection provided.");
} else {
volSpec.coords = viewer.selectedAtoms(volSpec.selection) as XYZ[];
}
}
if (volSpec.coords !== undefined) {

var xmax = volSpec.coords[0].x,
Expand Down Expand Up @@ -1593,6 +1602,8 @@ export interface IsoSurfaceSpec extends ShapeSpec {
smoothness?: number;
/** coordinates around which to include data; use viewer.selectedAtoms() to convert an AtomSelectionSpec to coordinates */
coords?: XYZ[];
/** selection around which to include data (alternative to coords) */
selection?: AtomSelectionSpec;
/** distance around coords to include data [default = 2.0] */
seldist?: number;
/** volumetric data for vertex coloring, can be VolumeData object or raw data if volformat is specified */
Expand Down
15 changes: 11 additions & 4 deletions src/GLViewer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//a molecular viewer based on GLMol

import { Geometry, Renderer, Camera, Raycaster, Projector, Light, Fog, Scene, Coloring, FrontSide, Material } from "./WebGL";
import { Geometry, Renderer, Camera, Raycaster, Projector, Light, Fog, Scene, Coloring, FrontSide, Material, MeshDoubleLambertMaterial } from "./WebGL";
import { Vector3, Matrix4, Matrix3, Quaternion, XYZ } from "./WebGL/math";
import { MeshLambertMaterial, Object3D, Mesh, LineBasicMaterial, Line } from "./WebGL";
import { elementColors, CC, ColorSpec, ColorschemeSpec } from "./colors";
Expand Down Expand Up @@ -3212,7 +3212,7 @@ export class GLViewer {
public addIsosurface(data, spec: IsoSurfaceSpec = {}, callback?) {
var s = new GLShape(spec);
s.shapePosition = this.shapes.length;
s.addIsosurface(data, spec, callback);
s.addIsosurface(data, spec, callback, this);
this.shapes.push(s);
return s;
};
Expand All @@ -3227,7 +3227,7 @@ export class GLViewer {
*/
public addVolumetricRender(data, spec: VolumetricRendererSpec) {
spec = spec || {};
var s = new GLVolumetricRender(data, spec);
var s = new GLVolumetricRender(data, spec, this);
s.shapePosition = this.shapes.length;
this.shapes.push(s);
return s;
Expand Down Expand Up @@ -4123,7 +4123,12 @@ export class GLViewer {
* @return {MeshLambertMaterial}
*/
private static getMatWithStyle(style: SurfaceStyleSpec) {
var mat = new MeshLambertMaterial();
let mat = null;
if(style.onesided) {
mat = new MeshLambertMaterial();
} else {
mat = new MeshDoubleLambertMaterial();
}
mat.vertexColors = Coloring.VertexColors;

for (var prop in style) {
Expand Down Expand Up @@ -4978,6 +4983,8 @@ export interface ViewerGridSpec {
* });
*/
export interface SurfaceStyleSpec {
/** one sided material - back is transparent */
onesided?: boolean;
/** sets the transparency: 0 to hide, 1 for fully opaque */
opacity?: number;
/** element based coloring */
Expand Down
51 changes: 43 additions & 8 deletions src/Gradient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CC, Color } from "./colors";
import { CC, Color, ColorSpec } from "./colors";

export abstract class GradientType {
gradient?: string;
Expand All @@ -24,13 +24,34 @@ export function normalizeValue(
}
}

/**
* Gradient specification.
* @see builtinGradients
*/
export type GradientSpec = {
/** Kind of gradient. E.g. RWB, ROYGB, sinebow. Can also specify linear[_color]* as a
* shorthand for CustomLinear and passing a colors array. */
gradient?: string;
/** Lower range of gradient */
min?: number;
/** Upper range of gradient */
max?: number;
/** {AtomSpec} property to use for gradient calculation. E.g., 'b' for temperature factors of a PDB. */
prop?: string;
/** mid point value for gradient (for rwb) */
mid?: number;
/** Custom colors for gradient (for {@link CustomLinear}) */
colors?: Array<ColorSpec>;
/** map of a certain {@link AtomSpec} property to a color of the form `{'prop': 'elem', map:elementColors.greenCarbon}` Allows the user to provide a mapping of elements to colors to the colorscheme. This can be done with any properties, and not just 'elem'.
*/
map?: Record<string, unknown>
};

//return a Gradient object, even if what is specified is descriptive
export function getGradient(grad: any): GradientType {
export function getGradient(grad: GradientSpec|GradientType): GradientType {
if (grad instanceof GradientType) {
return grad;
} else if (
grad.gradient !== undefined &&
builtinGradients[grad.gradient]
} else if (grad.gradient !== undefined && builtinGradients[grad.gradient]
) {
let min = grad.min === undefined ? -1 : grad.min;
let max = grad.max === undefined ? 1 : grad.max;
Expand All @@ -43,8 +64,14 @@ export function getGradient(grad: any): GradientType {
} else {
return new builtinGradients[grad.gradient](min, max, grad.mid);
}
} else if(typeof(grad.gradient) == "string" && grad.gradient.startsWith('linear_')) {
let colors = grad.gradient.split('_');
colors.shift();
let min = grad.min === undefined ? -1 : grad.min;
let max = grad.max === undefined ? 1 : grad.max;
return new CustomLinear(min,max,colors);
}
return grad;
return grad as GradientType;
}

/**
Expand Down Expand Up @@ -381,8 +408,16 @@ export class CustomLinear extends GradientType {
}
}

//map from names to gradient constructors
export const builtinGradients = {
/**
* built in gradient schemes
* The user can pass these strings directly as the gradient
* @prop rwb - red/white/blue, supports setting a mid point for white
* @prop roygb - rainbow
* @prop sinebow - rainbow with better saturation properties
* @prop linear - linearly maps between provided colors
*
*/
export const builtinGradients = {
"rwb": RWB,
"RWB": RWB,
"roygb": ROYGB,
Expand Down
19 changes: 16 additions & 3 deletions src/VolumetricRender.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Sphere } from "./WebGL/shapes";
import { Vector3, Matrix4 } from "./WebGL/math";
import { Vector3, Matrix4, XYZ } from "./WebGL/math";
import { VolumetricMaterial, Mesh, Texture, Object3D, Material } from "./WebGL";
import { CC } from "./colors";
import { GLShape } from "./GLShape";
import { AtomSelectionSpec } from "specs";
import { GLViewer } from "GLViewer";



Expand All @@ -15,7 +17,9 @@ export interface VolumetricRendererSpec {
/** number of times to sample each voxel approximately (default 5) */
subsamples?: number;
/** coordinates around which to include data; use viewer.selectedAtoms() to convert an AtomSelectionSpec to coordinates */
coords?: Array<Vector3>;
coords?: XYZ[];
/** selection around which to include data */
selection?: AtomSelectionSpec;
/** distance around coords to include data [default = 2.0] */
seldist?: number;
};
Expand Down Expand Up @@ -65,7 +69,9 @@ export class GLVolumetricRender {
texmatrix: any;
minunit: any;

constructor(data: { matrix: { elements: any; }; size: { x: number; y: number; z: number; }; unit: { x: number; y: number; z: number; }; origin: { x: number; y: number; z: number; }; data: number[]; getIndex: (arg0: number, arg1: number, arg2: number) => number; }, spec: VolumetricRendererSpec) {
constructor(data: { matrix: { elements: any; }; size: XYZ;
unit: XYZ; origin: XYZ; data: number[]; getIndex: (arg0: number, arg1: number, arg2: number) => number; },
spec: VolumetricRendererSpec, viewer?: GLViewer) {
spec = spec || {};
var transferfn = Object.assign([], spec.transferfn);
this.subsamples = spec.subsamples || 5.0;
Expand Down Expand Up @@ -186,6 +192,13 @@ export class GLVolumetricRender {
);
this.boundingSphere.radius = this.maxdepth / 2;

if (spec.coords === undefined && spec.selection !== undefined) {
if(viewer) {
spec.coords = viewer.selectedAtoms(spec.selection) as XYZ[];
} else {
console.log('Need to provide viewer to volumetric renderer if selection specified.');
}
}
// volume selectivity based on given coords and distance
if (spec.coords !== undefined && spec.seldist !== undefined) {
let mask = new Uint8Array(data.data.length);
Expand Down
11 changes: 3 additions & 8 deletions src/utilities.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//a collection of miscellaneous utility functions

import { builtinGradients, Gradient } from "./Gradient";
import { getGradient, Gradient, GradientType } from "./Gradient";
import { VolumeData } from "./VolumeData";
import { builtinColorSchemes, CC, elementColors, htmlColors, Color } from "./colors";
import { IsoSurfaceSpec } from "GLShape";
Expand Down Expand Up @@ -567,14 +567,9 @@ export function getColorFromStyle(atom, style): Color {
//apply a property mapping
prop = scheme.prop;
var grad = scheme.gradient; //redefining scheme
if (typeof builtinGradients[grad] != "undefined") {
grad = new builtinGradients[grad](
scheme.min,
scheme.max,
scheme.mid ? scheme.mid : scheme.colors
);
if(!(grad instanceof GradientType)) {
grad = getGradient(scheme);
}

let range = grad.range() || [-1, 1]; //sensible default
val = getAtomProperty(atom, prop);
if (val != null) {
Expand Down
7 changes: 7 additions & 0 deletions tests/auto/tests/customlinear.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
$3Dmol.get('../test_structs/af.pdb', function(data){
viewer.addModel(data);
viewer.setStyle({cartoon:{colorscheme:{prop: 'b',
gradient:'linear_red_orange_yellow_white', min: 70, max: 100}}});
viewer.zoomTo();
viewer.render();
});
9 changes: 9 additions & 0 deletions tests/auto/tests/onesidedsurf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
$3Dmol.download('pdb:3erk', viewer, {}, function() {
viewer.addSurface('MS',{'colorscheme':'whiteCarbon','onesided':true},
{'byres':true, 'hetflag':false, 'within':{'distance':5,'sel':{'resn':'SB4'}}},
{'hetflag':false});
viewer.setStyle({'resn':'SB4'},{'stick':{'colorscheme':'chartreuseCarbon'}});
viewer.setStyle({'bonds':0},{'sphere':{'radius':0.35}});
viewer.zoomTo({'resn':'SB4'});
viewer.render();
});
26 changes: 26 additions & 0 deletions tests/auto/tests/test_ccp4_volsel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@


$.get('data/1lo6.pdb', function(data) {
viewer.addModel(data,'pdb');
viewer.setStyle({cartoon:{},stick:{}});

//can't use jquery with binary data
$3Dmol.getbin('data/1lo6_2FOFC.ccp4', function(data) {
var voldata = new $3Dmol.VolumeData(data, 'ccp4');
viewer.addVolumetricRender(voldata, { transferfn:[
{ color: "#ffffff", opacity: 0.0, value: 0 },
{ color: "#00ffff", opacity: .1, value: .5 }
],
selection: {resi: 18},
seldist: 4.0
});
viewer.addIsosurface(voldata, {isoval: 0.3,
color: "blue",
wireframe: true,
selection: {resi: 18},
seldist: 3.0
});
viewer.zoomTo({resi: 18});
viewer.render();
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 dbf2efd

Please sign in to comment.