Skip to content
Snippets Groups Projects
Commit ffeb391c authored by Lukas Pravda's avatar Lukas Pravda
Browse files

Merge branch 'dev'

parents 1265a35d 6b6a5fca
No related branches found
No related tags found
No related merge requests found
Pipeline #105124 passed with stages
in 1 minute and 18 seconds
.vscode
.DS_Store
.mypy_cache
node_modules
......
......@@ -12,4 +12,5 @@ pages:
paths:
- public
only:
- dev
- master
......@@ -40,6 +40,9 @@ A few files needs to be imported in the page before the component is attempted t
<!-- CSS style to be used for scene drawing (required for saving SVGs.) -->
<link rel="stylesheet" href="pdb-ligand-env-svg.css" />
<!-- CSS style to be used for the component UI -->
<link rel="stylesheet" href="pdb-ligand-env.css" />
<!-- UI icons -->
<link rel="stylesheet" href="https://ebi.emblstatic.net/web_guidelines/EBI-Icon-fonts/v1.3/fonts.css" />
......@@ -49,7 +52,7 @@ A few files needs to be imported in the page before the component is attempted t
charset="utf-8"></script>
<!--PDBe interactions component-->
<script type="module" src="pdb-ligand-env-component-0.2.0-min.js"></script>
<script type="module" src="pdb-ligand-env-component-0.3.0-min.js"></script>
```
#### A) Ligand interactions
......
......@@ -13,7 +13,7 @@
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"
charset="utf-8"></script>
<!--PDBe interactions component-->
<script type="module" src="pdb-ligand-env-component-0.2.0-min.js"></script>
<script type="module" src="pdb-ligand-env-component-0.3.0-min.js"></script>
</head>
......
......@@ -8,8 +8,8 @@
<!-- MOL* -->
<link rel="stylesheet" type="text/css"
href="https://wwwdev.ebi.ac.uk/pdbe/pdb-component-library/v1.0/css/molstar-light-0.0.1.css">
<script src="https://wwwdev.ebi.ac.uk/pdbe/pdb-component-library/v1.0/js/molstar-0.0.1.js"></script>
href="https://www.ebi.ac.uk/pdbe/pdb-component-library/css/pdbe-molstar-1.1.0.css">
<script src="https://www.ebi.ac.uk/pdbe/pdb-component-library/js/pdbe-molstar-plugin-1.1.0.js"></script>
<!-- Web component polyfill (only loads what it needs) -->
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs/webcomponents-lite.js" charset="utf-8">
......@@ -18,17 +18,17 @@
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"
charset="utf-8"></script>
<script type="module" src="pdb-ligand-env-component-0.2.0-min.js"></script>
<script type="module" src="pdb-ligand-env-component-0.3.0-min.js"></script>
<script>
var renderBmInteractions = function (id, bmId) {
var int =
`<pdb-ligand-env style="border: 1px solid black" pdb-id="${id}" bound-molecule-id="${bmId}"></pdb-ligand-env>`
`<pdb-ligand-env style="border: 1px solid black" pdb-id="${id}" bound-molecule-id="${bmId}" environment="dev"></pdb-ligand-env>`
document.getElementById('rt').innerHTML = int;
};
var renderLigandInteractions = function (id, chain, resId) {
var int =
`<pdb-ligand-env style="border: 1px solid black" pdb-id="${id}" pdb-chain-id="${chain}" pdb-res-id="${resId}" zoom-on></pdb-ligand-env>`
`<pdb-ligand-env style="border: 1px solid black" pdb-id="${id}" pdb-chain-id="${chain}" pdb-res-id="${resId}" zoom-on environment="dev"></pdb-ligand-env>`
document.getElementById('rt').innerHTML = int;
};
......@@ -42,9 +42,9 @@
if (result.bmid === undefined) {
result['query'] =
`https://wwwdev.ebi.ac.uk/pdbe/coordinates/${result.pdbid}/ligandInteraction?&authAsymId=$${result.chain}&authSeqNumber=${result.resid}&radius=5&dataSource=hydrogens`
`https://www.ebi.ac.uk/pdbe/model-server/v1/${result.pdbid}/residueInteraction?auth_asym_id=${result.chain}&auth_seq_id=${result.resid}&radius=5&data_source=pdb-h`
} else {
result['query'] = `https://wwwdev.ebi.ac.uk/pdbe/coordinates/${result.pdbid}?dataSource=hydrogens`
result['query'] = `https://www.ebi.ac.uk/pdbe/model-server/v1/${result.pdbid}/full?data_source=pdb-h`
}
if (result.chain !== undefined) result.chain = result.chain.toUpperCase();
......@@ -64,9 +64,9 @@
} else {
params = {
pdbid: '1cbs',
chain: 'A',
chain: 'A_1',
resid: 200,
query: "https://wwwdev.ebi.ac.uk/pdbe/coordinates/1cbs/ligandInteraction?&authAsymId=$A&authSeqNumber=$200&radius=5&dataSource=hydrogens"
query: "https://www.ebi.ac.uk/pdbe/model-server/v1/1cbs/residueInteraction?auth_asym_id=A_1&auth_seq_id=200&radius=5&data_source=pdb-h"
};
}
......@@ -78,32 +78,27 @@
var initParams = {
moleculeId: params.pdbid,
pdbeUrl: 'https://wwwdev.ebi.ac.uk/pdbe/',
loadMaps: true,
validationAnnotation: true,
domainAnnotation: true,
pdbeUrl: 'https://www.ebi.ac.uk/pdbe/',
loadMaps: false,
bgColor: {
r: 255,
g: 250,
b: 250
},
lowPrecisionCoords: true,
isExpanded: false,
hideControls: true,
showLogs: false,
subscribeEvents: true,
showPDBeLogo: false,
assemblyId: 'preferred', //'deposited'
// selectInteraction: false,
subscribeEvents: true,
ligandView: {
auth_asym_Id: params.chain,
auth_seq_id: params.resid,
auth_asym_id: params.chain,
hydrogens: true
},
backgroundColour: '0xFFFFFF',
//customColorList: [0xff0000, 0x0000ff],
//representationStyle: representationStyle//,
customData: {
url: `https://wwwdev.ebi.ac.uk/pdbe/coordinates/${params.pdbid}/ligandInteraction?&authAsymId=${params.chain}&authSeqNumber=${params.resid}&radius=5&dataSource=hydrogens`,
format: 'cif'
}
}
var pdbeMolstar = new MolStarPdbeWrapper();
var pdbeMolstar = new PDBeMolstarPlugin();
pdbeMolstar.render(document.getElementById('3dViewer'), initParams);
});
}());
......
source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"name": "pdb-ligand-env",
"version": "0.2.0",
"version": "0.3.0",
"description": "",
"main": "app.js",
"dependencies": {
"@types/d3": "^5.0.1",
"@types/d3": "^5.16.3",
"@types/d3-tip": "^3.5.5",
"d3": "^5.15.0",
"d3": "^5.16.0",
"d3-tip": "^0.9.1",
"d3scription": "^1.0.1",
"lit-element": "^2.2.1",
"typescript": "^3.7.4"
"lit-element": "^2.4.0",
"typescript": "^4.0.2"
},
"devDependencies": {
"@babel/core": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.8.3",
"@babel/preset-env": "^7.8.3",
"@babel/runtime": "^7.8.3",
"@webcomponents/webcomponentsjs": "^2.4.1",
"babel-loader": "^8.0.5",
"browser-sync": "^2.26.7",
"@babel/core": "^7.12.3",
"@babel/plugin-transform-runtime": "^7.12.1",
"@babel/preset-env": "^7.12.1",
"@babel/runtime": "^7.12.1",
"@webcomponents/webcomponentsjs": "^2.5.0",
"babel-loader": "^8.1.0",
"browser-sync": "^2.26.13",
"camelcase": "^5.0.0",
"clean-webpack-plugin": "^1.0.1",
"css-loader": "^2.1.0",
......@@ -32,12 +32,12 @@
"gulp-minify": "^3.1.0",
"live-server": "^1.2.1",
"npm-run-all": "^4.1.3",
"onchange": "^6.1.0",
"onchange": "^6.1.1",
"style-loader": "^0.23.1",
"ts-node": "^7.0.1",
"url-loader": "^1.1.2",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
"webpack": "^5.2.0",
"webpack-cli": "^3.3.12"
},
"scripts": {
"tscW": "tsc -w",
......
......@@ -38,10 +38,19 @@ class Depiction {
this.atoms = data.atoms.map(x => new Atom(x));
this.bonds = new Array<Bond>();
let bds = new Set<string>();
data.bonds.forEach(x => {
var atomA = this.atoms.find(e => e.name == x.bgn);
var atomB = this.atoms.find(e => e.name == x.end);
var bond = new Bond(atomA, atomB, x.coords, x.style);
let atomA = this.atoms.find(e => e.name == x.bgn);
let atomB = this.atoms.find(e => e.name == x.end);
let bond = new Bond(atomA, atomB, x.coords, x.style);
let bondFlag = [atomA.name, atomB.name].sort().join("_");
if (!bds.has(bondFlag)) {
bds.add(bondFlag);
atomA.connectivity++;
atomB.connectivity++;
}
this.bonds.push(bond);
});
......@@ -67,16 +76,9 @@ class Depiction {
}
// ideally we want to find an atom which is part just a single bond to get nice initial position.
// If there is no such atom any will do
let searchStruct = new Map<string, number>();
this.bonds.forEach(x => {
searchStruct.set(x.bgn.name, searchStruct.get(x.bgn.name) === undefined ? 1 : searchStruct.get(x.bgn.name) + 1);
searchStruct.set(x.end.name, searchStruct.get(x.end.name) === undefined ? 1 : searchStruct.get(x.end.name) + 1);
});
searchStruct = this.sortMap(searchStruct); // ascending order so we hit those with less partners sooner.
let thisAtomName = [...searchStruct.keys()].find(x => atomNames.findIndex(y => y === x) !== -1)
let thisAtom = this.atoms.find(x => x.name === thisAtomName);
let atoms = this.atoms.filter(x => atomNames.includes(x.name)).sort((x, y) => x.connectivity - y.connectivity);
let thisAtom = atoms[0];
let bond = this.bonds.find(x => x.containsAtom(thisAtom));
let otherAtom = bond.getOtherAtom(thisAtom);
......@@ -88,10 +90,9 @@ class Depiction {
return new Vector2D(x, y);
}
public draw(withClarityNodes: boolean = false) {
public draw() {
this.structure.selectAll("*").remove();
if (withClarityNodes) this.appendClarityNodes();
this.appendBondVisuals();
this.appendTexts();
}
......@@ -154,16 +155,24 @@ class Depiction {
* @memberof Depiction
*/
private appendTexts(): void {
let data = this.atoms
.filter(x => x.labels.length > 0)
?.map(x => x.labels)
?.reduce((a, b) => a.concat(b));
this.structure.selectAll()
.data(this.atoms.filter(x => Object.keys(x.labels).length !== 0).map(x => x.labels).reduce((a, b) => a.concat(b)))
.data(data)
.enter()
.append('text')
.attr('filter', "url(#solid-background)")
.attr('style', (x: any) => x.style)
.attr('x', (x: any) => x.x)
.attr('y', (x: any) => x.y)
.attr('dominant-baseline', (x: any) => x['dominant-baseline'])
.attr('text-anchor', (x: any) => x['text-anchor'])
.each(function (x: any) {
for (var i = 0; i < x.tspans.length; i++) {
d3.select(this)
.append('tspan')
......@@ -173,23 +182,6 @@ class Depiction {
});
}
/**
* Add small white circle on the background of atoms with label
* just to make the interaction lines pretty.
*
* @memberof Depiction
*/
private appendClarityNodes(): void {
this.structure.selectAll()
.data(this.atoms.filter(x => Object.keys(x.labels).length != 0))
.enter().append('circle')
.classed('pdb-lig-env-svg-shadow-node', true)
.attr('cx', (x: any) => x.position.x)
.attr('cy', (x: any) => x.position.y)
.attr('r', 15);
}
public getCenter(ids: string[]): Vector2D {
let coords = new Array<Vector2D>();
......@@ -241,10 +233,13 @@ class Atom {
name: string;
labels: any;
position: Vector2D;
connectivity: number
constructor(item: any) {
this.name = item.name;
this.labels = item.labels;
this.position = new Vector2D(item.x, item.y)
this.position = new Vector2D(item.x, item.y);
this.connectivity = 0;
}
/**
......@@ -347,7 +342,6 @@ class Bond {
coords: string;
style: string;
/**
*Creates an instance of the bond.
* @param {Atom} a
......@@ -358,7 +352,7 @@ class Bond {
this.bgn = a;
this.end = b;
this.coords = coords;
this.style = style.replace("stroke-width:2px", "stroke-width:4px");;
this.style = style.replace("stroke-width:2px", "stroke-width:4px");
}
......
class Visualization {
// component related
private parent: HTMLElement;
......@@ -21,12 +20,14 @@ class Visualization {
// #region data properties
private environment: Model.Environment;
private pdbId: string;
private bindingSite: Model.BindingSite;
private bindingSites: Model.BindingSite[];
private presentBindingSite: Model.BindingSite;
private depiction: Depiction;
private visualsMapper: VisualsMapper;
private interactionsData: any;
private selectedResidueHash: string;
private nodeDragged: boolean;
private rProvider: ResidueProvider;
......@@ -38,9 +39,11 @@ class Visualization {
this.environment = this.parseEnvironment(env);
this.parent.style.cssText += "display: block; height: 100%; width: 100%; position: relative;";
this.visualsMapper = new VisualsMapper();
this.rProvider = ResidueProvider.getInstance();
this.visualsMapper = new VisualsMapper(this.environment);
this.rProvider = ResidueProvider.getInstance(this.environment);
this.fullScreen = false;
this.bindingSites = new Array<Model.BindingSite>();
this.nodeDragged = false;
if (uiParameters === undefined) uiParameters = new Config.UIParameters();
......@@ -55,6 +58,8 @@ class Visualization {
.attr('width', '100%')
.attr('height', '100%');
this.addMarkers();
this.canvas = this.svg.append('g').attr('id', 'vis-root');
this.linksRoot = this.canvas.append('g').attr('id', 'links');
this.depictionRoot = this.canvas.append('g').attr('id', 'depiction');
......@@ -70,7 +75,7 @@ class Visualization {
this.addMarkers();
}
// #region even handlers
// #region event handlers
private getZoomHandler() {
return d3.zoom()
.scaleExtent([1 / 10, 10])
......@@ -85,6 +90,8 @@ class Visualization {
let hash = `${e.eventData.auth_asym_id}${e.eventData.auth_seq_id}${e.eventData.ins_code}`;
this.nodes?.each((node: Model.InteractionNode, index: number, group: any) => {
this.nodeDim(node, index, group);
if (node.id === hash) {
this.selectedResidueHash = hash;
this.nodeHighlight(node, index, group);
......@@ -94,7 +101,6 @@ class Visualization {
}
private nodeMouseLeaveEventHandler() {
if (this.fullScreen) return;
......@@ -104,24 +110,43 @@ class Visualization {
return;
}
});
this.links.attr('opacity', 1);
this.nodes.attr('opacity', 1);
}
private linkMouseClickEventHandler(x: Model.Link) {
this.fireExternalLinkEvent(x, Config.interactionClickEvent);
private linkMouseOverEventHandler(x: Model.Link, i: number, g: any) {
if (!this.nodeDragged) {
this.linkHighlight(x, i, g);
this.fireExternalLinkEvent(x, Config.interactionMouseoverEvent);
}
}
private linkMouseOverEventHandler(x: Model.Link, index: number, group: any) {
let parent = d3.select(group[index]).node().parentNode;
d3.select(parent).classed('pdb-lig-env-svg-bond-highlighted', true);
private linkMouseOutEventHandler(x: Model.Link, i: number, g: any) {
if (!this.nodeDragged) {
this.linkDim(x, i, g);
this.fireExternalNullEvent(Config.interactionMouseoutEvent);
}
}
this.fireExternalLinkEvent(x, Config.interactionMouseoverEvent);
//https://stackoverflow.com/questions/40722344/understanding-d3-with-an-example-mouseover-mouseup-with-multiple-arguments
private nodeMouseoverEventHandler(x: Model.InteractionNode, i: number, g: any) {
if (!this.nodeDragged) {
this.nodeHighlight(x, i, g);
this.fireExternalNodeEvent(x, Config.interactionMouseoverEvent);
}
}
private linkMouseOutEventHandler(index: number, group: any) {
let parent = d3.select(group[index]).node().parentNode;
d3.select(parent).classed('pdb-lig-env-svg-bond-highlighted', false);
private nodeMouseoutEventHandler(x: Model.InteractionNode, i: number, g: any) {
if (!this.nodeDragged) {
this.nodeDim(x, i, g);
this.fireExternalLinkLeaveEvent();
this.fireExternalNullEvent(Config.interactionMouseoutEvent);
}
this.links.attr('opacity', 1);
this.nodes.attr('opacity', 1);
}
private dragHandler = d3.drag()
......@@ -133,10 +158,14 @@ class Visualization {
x.fy = x.y;
})
.on('drag', (x: Model.InteractionNode) => {
this.nodeDragged = true;
x.fx = d3.event.x;
x.fy = d3.event.y;
})
.on('end', (x: Model.InteractionNode) => {
this.nodeDragged = false;
if (!d3.event.active) this.simulation.alphaTarget(0);
x.fx = d3.event.x;
x.fy = d3.event.y;
......@@ -226,7 +255,7 @@ class Visualization {
return d3.json(ligandUrl)
.catch(e => this.processError(e, `Component ${ligandId} was not found.`))
.then((d: any) => this.addDepiction(d, true))
.then((d: any) => this.addDepiction(d))
.then(() => this.centerScene());
}
......@@ -236,13 +265,11 @@ class Visualization {
*
* @param {*} depiction Content of annotation.json file generated by
* the PDBeChem process.
* @param {*} withClarityNodes Control if shadow nodes should be drawn
* in the background of nodes with labels
* @memberof Visualization
*/
public addDepiction(depiction: any, withClarityNodes: boolean) {
public addDepiction(depiction: any) {
this.depiction = new Depiction(this.depictionRoot, depiction);
this.depiction.draw(withClarityNodes);
this.depiction.draw();
}
......@@ -271,11 +298,7 @@ class Visualization {
public toogleZoom(active: boolean) {
if (active) {
this.zoomHandler = this.getZoomHandler();
} else {
this.zoomHandler = undefined;
}
this.zoomHandler = active ? this.getZoomHandler() : undefined;
}
......@@ -293,11 +316,13 @@ class Visualization {
if (this.depiction === undefined || this.depiction.ccdId !== body.ligand.chem_comp_id) {
this.initLigandDisplay(body.ligand.chem_comp_id).then(() => {
this.bindingSite = new Model.BindingSite().fromLigand(key, body, this.depiction);
this.presentBindingSite = new Model.BindingSite().fromLigand(key, body, this.depiction);
this.bindingSites.push(this.presentBindingSite);
this.setupLigandScene();
});
} else {
this.bindingSite = new Model.BindingSite().fromLigand(key, body, this.depiction);
this.presentBindingSite = new Model.BindingSite().fromLigand(key, body, this.depiction);
this.bindingSites.push(this.presentBindingSite);
this.setupLigandScene();
}
}
......@@ -314,10 +339,11 @@ class Visualization {
public addBoundMoleculeInteractions(data: any, bmId: string) {
let key = Object.keys(data)[0];
this.interactionsData = data;
this.presentBindingSite = new Model.BindingSite().fromBoundMolecule(key, data[key][0]);
this.bindingSite = new Model.BindingSite().fromBoundMolecule(key, data[key][0]);
this.bindingSite.bmId = bmId;
let ligands = this.bindingSite.residues.filter(x => x.isLigand);
this.bindingSites.push(this.presentBindingSite);
this.presentBindingSite.bmId = bmId;
let ligands = this.presentBindingSite.residues.filter(x => x.isLigand);
if (ligands.length === 1) this.initLigandInteractions(this.pdbId, ligands[0].authorResidueNumber, ligands[0].chainId);
else this.setupScene();
......@@ -326,7 +352,7 @@ class Visualization {
// #region menu functions
public saveSvg() {
d3.text(Resources.componentSvgCss)
d3.text(Resources.ligEnvCSSAPI(this.environment))
.then(x => {
let svgData = `
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" style="background-color: white;">
......@@ -360,7 +386,7 @@ class Visualization {
let dataBlob = new Blob([JSON.stringify(this.interactionsData, null, 4)], { type: 'application/json' });
downloadLink.href = URL.createObjectURL(dataBlob);
downloadLink.download = this.interactionsData === undefined ? 'no name.json' : `${this.pdbId}_${this.bindingSite.bmId}_interactions.json`;
downloadLink.download = this.interactionsData === undefined ? 'no name.json' : `${this.pdbId}_${this.presentBindingSite.bmId}_interactions.json`;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
......@@ -373,10 +399,27 @@ class Visualization {
* @memberof Visualization
*/
public reinitialize() {
if (this.depiction === undefined) this.setupScene();
else this.setupLigandScene();
this.hideLigandLabel();
if (this.bindingSites.length > 1 && this.depiction !== undefined) {
this.presentBindingSite = this.bindingSites[0];
this.bindingSites.pop();
this.depictionRoot.selectAll('*').remove();
this.depiction = undefined;
this.nullNodesPositions();
this.setupScene().then(() => this.centerScene());
}
else if (this.depiction === undefined) {
this.nullNodesPositions();
this.setupScene().then(() => this.centerScene());
}
else {
this.nullNodesPositions();
this.setupLigandScene().then(() => this.centerScene());
}
this.fireExternalNullEvent(Config.interactionHideLabelEvent);
}
......@@ -459,29 +502,23 @@ class Visualization {
}
private getSVGName(): string {
if (this.bindingSite !== undefined) return `${this.bindingSite.bmId}.svg`;
if (this.presentBindingSite !== undefined) return `${this.presentBindingSite.bmId}.svg`;
if (this.depiction !== undefined) return `${this.depiction.ccdId}.svg`;
return 'blank.svg';
}
// #region fire events
private nullNodesPositions() {
this.presentBindingSite.interactionNodes.forEach((x: Model.InteractionNode) => {
if (!x.static) {
x.fx = null;
x.fy = null;
}
});
//https://stackoverflow.com/questions/40722344/understanding-d3-with-an-example-mouseover-mouseup-with-multiple-arguments
private fireExternalNodeMouseEnterEvent(x: Model.InteractionNode, i: number, g: any) {
this.nodeHighlight(x, i, g);
this.fireExternalNodeEvent(x, Config.interactionMouseoverEvent);
}
private fireExternalNodeMouseLeaveEvent(x: Model.InteractionNode, i: number, g: any) {
this.nodeDim(x, i, g);
const e = new CustomEvent(Config.interactionMouseoutEvent, {
bubbles: true,
detail: {}
});
this.parent.dispatchEvent(e);
}
// #region fire events
private fireExternalLinkEvent(link: Model.Link, eventName: string) {
let atomsSource = [];
......@@ -517,7 +554,6 @@ class Visualization {
tooltip: link.toTooltip()
}
});
this.parent.dispatchEvent(e);
}
......@@ -538,8 +574,8 @@ class Visualization {
this.parent.dispatchEvent(e);
}
private fireExternalLinkLeaveEvent() {
const e = new CustomEvent(Config.interactionMouseoutEvent, {
private fireExternalNullEvent(eventName: string) {
const e = new CustomEvent(eventName, {
bubbles: true,
detail: {}
});
......@@ -559,22 +595,21 @@ class Visualization {
private setupLinks() {
this.links = this.linksRoot
.selectAll()
.data(this.bindingSite.links)
.data(this.presentBindingSite.links)
.enter().append('g');
this.links
.append('line')
.classed('pdb-lig-env-svg-shadow-bond', (x: Model.Link) => x.getLinkClass() !== 'hydrophobic')
.on('click', (x: Model.Link) => this.linkMouseClickEventHandler(x))
.on('mouseenter', (x: Model.Link, index: number, group: any) => this.linkMouseOverEventHandler(x, index, group))
.on('mouseleave', (_, index: number, group: any) => this.linkMouseOutEventHandler(index, group));
.on('mouseleave', (x: Model.Link, index: number, group: any) => this.linkMouseOutEventHandler(x, index, group));
this.links
.append('line')
.attr('class', (e: Model.Link) => `pdb-lig-env-svg-bond pdb-lig-env-svg-bond-${e.getLinkClass()}`)
.attr('marker-mid', (e: Model.Link) => e.hasClash() ? 'url(#clash)' : '')
.on('mouseenter', (x: Model.Link, y: any, z: any) => this.linkMouseOverEventHandler(x, y, z))
.on('mouseleave', (_, index: number, group: any) => this.linkMouseOutEventHandler(index, group));
.on('mouseleave', (x: Model.Link, index: number, group: any) => this.linkMouseOutEventHandler(x, index, group));
}
......@@ -585,11 +620,11 @@ class Visualization {
.style('text-anchor', 'middle')
.style('dominant-baseline', 'central')
.each(function (e: Model.InteractionNode) {
let labels = [e.residue.chemCompId, `${e.residue.chainId} ${e.residue.authorResidueNumber}`];
for (var i = 0; i < labels.length; i++) {
let labels = [e.residue.chemCompId, e.residue.authorResidueNumber];
for (let i = 0; i < labels.length; i++) {
d3.select(this)
.append('tspan')
.attr('dy', (i * 20) - 4)
.attr('dy', (i * 30) - 10)
.attr('x', 0)
.text(labels[i]);
}
......@@ -612,7 +647,8 @@ class Visualization {
if (!n.residue.isLigand) return;
this.fireExternalNodeMouseLeaveEvent(n, i, g);
this.nodeDim(n, i, g);
this.nodeMouseoutEventHandler(n, i, g);
this.showLigandLabel(n);
this.initLigandInteractions(this.pdbId, n.residue.authorResidueNumber, n.residue.chainId);
......@@ -654,37 +690,38 @@ class Visualization {
this.wipeOutVisuals();
this.setupLinks();
this.bindingSite.interactionNodes
this.presentBindingSite.interactionNodes
.filter((x: Model.InteractionNode) => !x.residue.isLigand)
.forEach((x: Model.InteractionNode) => {
let lnks = this.bindingSite.links
.filter((y: Model.LigandResidueLink) => y.containsInteractionNode(x))
let links = this.presentBindingSite.links.filter((y: Model.LigandResidueLink) => y.containsNode(x) && y.getLinkClass() !== 'hydrophobic');
links = links.length == 0 ? this.presentBindingSite.links.filter((y: Model.LigandResidueLink) => y.containsNode(x)) : links;
let atom_names = links
.map((y: Model.LigandResidueLink) => [].concat.apply([], y.interaction.map(z => z.sourceAtoms)));
let concated = [].concat.apply([], lnks);
let concated = [].concat.apply([], atom_names);
let position: Vector2D = this.depiction.getInitalNodePosition(concated);
x.x = position.x + Math.random() * 110;
x.y = position.y + Math.random() * 110;
x.x = position.x + Math.random() * 55;
x.y = position.y + Math.random() * 55;
});
// setup nodes; wait for resources to be ready
this.bindingSite.interactionNodes.forEach(x => this.rProvider.downloadAnnotation(x.residue));
this.presentBindingSite.interactionNodes.forEach(x => this.rProvider.downloadAnnotation(x.residue));
await Promise.all(this.rProvider.downloadPromises);
await Promise.all([this.visualsMapper.graphicsPromise, this.visualsMapper.mappingPromise]);
this.nodes = this.nodesRoot.append('g')
.selectAll()
.data(this.bindingSite.interactionNodes)
.data(this.presentBindingSite.interactionNodes)
.enter().append('g');
this.nodes.filter((x: Model.InteractionNode) => !x.residue.isLigand)
.on('click', (x: Model.InteractionNode) => this.fireExternalNodeEvent(x, Config.interactionClickEvent))
.attr('class', (x: Model.InteractionNode) => `pdb-lig-env-svg-node pdb-lig-env-svg-${x.residue.getResidueType()}-res`)
.on('mouseenter', (x: Model.InteractionNode, i: number, g: any) => this.fireExternalNodeMouseEnterEvent(x, i, g))
.on('mouseleave', (x: Model.InteractionNode, i: number, g: any) => this.fireExternalNodeMouseLeaveEvent(x, i, g));
.on('mouseenter', (x: Model.InteractionNode, i: number, g: any) => this.nodeMouseoverEventHandler(x, i, g))
.on('mouseleave', (x: Model.InteractionNode, i: number, g: any) => this.nodeMouseoutEventHandler(x, i, g));
this.nodes.filter((x: Model.InteractionNode) => !x.residue.isLigand && this.visualsMapper.glycanMapping.has(x.residue.chemCompId))
.html((e: Model.InteractionNode) => this.visualsMapper.getGlycanImage(e.residue.chemCompId));
......@@ -699,13 +736,12 @@ class Visualization {
let forceLink = d3.forceLink()
.links(this.links.filter((x: Model.LigandResidueLink) => x.getLinkClass() !== 'hydrophobic'))
.distance(70)
.strength(0.5);
.distance(5);
let charge = d3.forceManyBody().strength(-100).distanceMin(40).distanceMax(80);
let collision = d3.forceCollide().radius(45);
let charge = d3.forceManyBody().strength(-80).distanceMin(10).distanceMax(20);
let collision = d3.forceCollide(50).iterations(10).strength(0.5);
this.simulation = d3.forceSimulation(this.bindingSite.interactionNodes)
this.simulation = d3.forceSimulation(this.presentBindingSite.interactionNodes)
.force('link', forceLink)
.force('charge', charge) //strength
.force('collision', collision)
......@@ -729,18 +765,18 @@ class Visualization {
this.setupLinks();
// setup nodes; wait for resources to be ready
this.bindingSite.interactionNodes.forEach(x => ResidueProvider.getInstance().downloadAnnotation(x.residue));
this.presentBindingSite.interactionNodes.forEach(x => this.rProvider.downloadAnnotation(x.residue));
await Promise.all(this.rProvider.downloadPromises);
await Promise.all([this.visualsMapper.graphicsPromise, this.visualsMapper.mappingPromise]);
this.nodes = this.nodesRoot
.selectAll()
.data(this.bindingSite.interactionNodes)
.data(this.presentBindingSite.interactionNodes)
.enter().append('g')
.attr('class', (e: Model.InteractionNode) => `pdb-lig-env-svg-node pdb-lig-env-svg-${e.residue.getResidueType()}-res`)
.on('click', (x: Model.InteractionNode, i: number, g: any) => this.selectLigand(x, i, g))
.on('mouseover', (x: Model.InteractionNode, i: number, g: any) => this.fireExternalNodeMouseEnterEvent(x, i, g))
.on('mouseout', (x: Model.InteractionNode, i: number, g: any) => this.fireExternalNodeMouseLeaveEvent(x, i, g));
.on('mouseover', (x: Model.InteractionNode, i: number, g: any) => this.nodeMouseoverEventHandler(x, i, g))
.on('mouseout', (x: Model.InteractionNode, i: number, g: any) => this.nodeMouseoutEventHandler(x, i, g));
this.nodes.filter((n: Model.InteractionNode) =>
......@@ -754,7 +790,7 @@ class Visualization {
this.addNodeLabels(this.nodes);
let forceLink = d3.forceLink()
.links(this.bindingSite.links)
.links(this.presentBindingSite.links)
.distance((x: Model.Link) => (<Model.InteractionNode>x.source).residue.isLigand && (<Model.InteractionNode>x.target).residue.isLigand ? 55 : 150)
.strength(0.5);
......@@ -762,7 +798,7 @@ class Visualization {
let collision = d3.forceCollide(45);
let center = d3.forceCenter(this.parent.offsetWidth / 2, this.parent.offsetHeight / 2);
this.simulation = d3.forceSimulation(this.bindingSite.interactionNodes)
this.simulation = d3.forceSimulation(this.presentBindingSite.interactionNodes)
.force('link', forceLink)
.force('charge', charge) //strength
.force('collision', collision)
......@@ -795,21 +831,68 @@ class Visualization {
x.scale = 1.5;
if (x.residue.isLigand) d3.select(g[i]).style('cursor', 'pointer');
d3.select(g[i])
//.transition()
.attr('transform', () => `translate(${x.x},${x.y}) scale(${x.scale})`);
let otherNodes = [x];
this.links.filter((l: Model.Link) => l.containsNode(x)).each((lnk: Model.Link) => {
let otherNode = lnk.getOtherNode(x);
otherNodes.push(otherNode);
});
this.nodes
.filter((node: Model.InteractionNode) => !otherNodes.includes(node))
.attr('opacity', 0.4);
this.links
.filter((l: Model.Link) => !l.containsNode(x))
//.transition()
.attr('opacity', 0.4);
}
private nodeDim(x: Model.InteractionNode, i: number, g: any) {
x.scale = 1.0;
if (!x.residue.isLigand) x.scale = 1.0;
if (x.residue.isLigand) d3.select(g[i]).style('cursor', 'default');
d3.select(g[i])
//.transition()
.attr('transform', `translate(${x.x},${x.y}) scale(${x.scale})`);
}
private linkHighlight(x: Model.Link, i: number, g: any) {
let parent = d3.select(g[i]).node().parentNode;
d3.select(parent).classed('pdb-lig-env-svg-bond-highlighted', true);
this.links
.filter((l: Model.Link) => l !== x)
//.transition()
.attr('opacity', 0.4);
this.nodes
.filter((l: Model.InteractionNode) => !(l === x.source || l === x.target))
//.transition()
.attr('opacity', 0.4);
}
private linkDim(x: Model.Link, i: number, g: any) {
let parent = d3.select(g[i]).node().parentNode;
d3.select(parent).classed('pdb-lig-env-svg-bond-highlighted', false);
this.links
.filter((l: Model.Link) => l !== x)
//.transition()
.attr('opacity', 1);
this.nodes
.filter((l: Model.InteractionNode) => !(l === x.source || l === x.target))
//.transition()
.attr('opacity', 1);
}
private showLigandLabel(x: Model.InteractionNode) {
const e = new CustomEvent(Config.interactionShowLabelEvent, {
bubbles: true,
......@@ -821,27 +904,26 @@ class Visualization {
this.parent.dispatchEvent(e);
}
private addMarkers() {
let defs = this.svg.append('defs')
private hideLigandLabel() {
const e = new CustomEvent(Config.interactionHideLabelEvent, {
bubbles: true,
detail: {}
});
this.parent.dispatchEvent(e);
}
defs
.append('style')
.text("@import url('https://fonts.googleapis.com/css2?family=Exo+2:wght@200&display=swap');");
// proper background for depiction tspans
// gracefully copied from: https://stackoverflow.com/questions/15500894/background-color-of-text-in-svg
let filter = defs.append('filter')
.attr('x', 0)
.attr('y', 0)
.attr('width', 1)
.attr('height', 1)
.attr('id', 'solid-background')
private addMarkers() {
// let mapping = new Map<string, string>([
// // ["arrow-electrostatic", "#3F26BF"],
// // ["arrow-vdw", "#9B7653"],
// // ["arrow-metal", "#008080"],
// // ["arrow-aromatic", "#AD4379"],
// ]);
filter.append('feFlood').attr('flood-color', 'white')
filter.append('feComposite').attr('in', 'SourceGraphic')
this.svg
.append('defs')
defs
.append('marker')
.attr('id', 'clash')
.attr("markerWidth", 15)
......@@ -850,25 +932,9 @@ class Visualization {
.attr("refY", 3)
.attr("orient", "auto")
.attr('markerUnits', 'strokeWidth')
.append('path')
.attr('d', 'M22.245,4.015c0.313,0.313,0.313,0.826,0,1.139l-6.276,6.27c-0.313,0.312-0.313,0.826,0,1.14l6.273,6.272 c0.313,0.313,0.313,0.826,0,1.14l-2.285,2.277c-0.314,0.312-0.828,0.312-1.142,0l-6.271-6.271c-0.313-0.313-0.828-0.313-1.141,0 l-6.276,6.267c-0.313,0.313-0.828,0.313-1.141,0l-2.282-2.28c-0.313-0.313-0.313-0.826,0-1.14l6.278-6.269 c0.313-0.312,0.313-0.826,0-1.14L1.709,5.147c-0.314-0.313-0.314-0.827,0-1.14l2.284-2.278C4.308,1.417,4.821,1.417,5.135,1.73 L11.405,8c0.314,0.314,0.828,0.314,1.141,0.001l6.276-6.267c0.312-0.312,0.826-0.312,1.141,0L22.245,4.015z')
.attr('style', 'stroke:#FF5050;stroke-width:3px;');
// mapping.forEach((x, y) =>
// d3.select('defs').append('marker')
// .attr('id', y)
// .attr("markerWidth", 15)
// .attr("markerHeight", 15)
// .attr("refX", 7)
// .attr("refY", 3)
// .attr("orient", "auto")
// .attr('markerUnits', 'strokeWidth')
// .attr('viewBox', '0 0 20 20')
// .append('path')
// .attr('d', 'M0,0 L0,6 L9,3 z')
// .attr('fill', x)
// );
}
private parseEnvironment(env: string): Model.Environment {
......
......@@ -15,14 +15,14 @@ namespace Model {
GroupGroup
}
/**
* What environment should be used
*
* @export
* @enum {number}
*/
export enum Environment {
export enum Environment {
Production,
Development,
Internal
......@@ -149,7 +149,18 @@ namespace Model {
}
public toString(): string {
return `${this.residue.chemCompId} ${this.residue.chainId} ${this.residue.authorResidueNumber}${this.residue.authorInsertionCode}`;
let r = this.residue;
let splitted = r.chainId.split('_');
let chainStr = splitted[0];
let symString = "";
if (splitted.length > 1) {
symString = splitted[1] !== "1"? `[${splitted[1]}]` : "";
}
let str = `${r.chemCompId} | ${chainStr}${symString} | ${r.authorResidueNumber}${r.authorInsertionCode}`;
return str;
}
public toTooltip(): string {
......@@ -157,23 +168,49 @@ namespace Model {
}
}
export interface Link {
source: InteractionNode;
target: InteractionNode;
export abstract class Link {
public source: InteractionNode;
public target: InteractionNode;
constructor(source: InteractionNode, target: InteractionNode) {
this.source = source;
this.target = target;
}
public containsBothNodes(a: InteractionNode, b: InteractionNode): boolean {
let condA = this.source.equals(a) && this.target.equals(b);
let condB = this.source.equals(b) && this.target.equals(a);
return condA || condB;
}
public containsNode(node: InteractionNode) {
return this.source.equals(node) || this.target.equals(node);
}
getLinkClass(): string;
hasClash(): boolean;
toTooltip(): string;
public containsResidue(n: Residue): boolean {
return (this.target.residue.equals(n) || this.source.residue.equals(n));
}
public getOtherNode(node: InteractionNode) {
return this.source.equals(node) ? this.target : this.source;
}
abstract getLinkClass(): string;
abstract hasClash(): boolean;
abstract toTooltip(): string;
}
export class ResidueResidueLink implements Link {
export class ResidueResidueLink extends Link {
target: InteractionNode;
source: InteractionNode
interactions: Map<InteractionType, Array<string>>;
constructor(source: InteractionNode, target: InteractionNode, interactions: any) {
this.source = source;
this.target = target;
super(source, target);
this.interactions = new Map<InteractionType, Array<string>>();
Object.keys(interactions).forEach(x => {
......@@ -182,14 +219,6 @@ namespace Model {
});
}
/**
* Checks if the link contains a given node.
*/
public containsNode(n: InteractionNode): boolean {
return this.target.equals(n) || this.source.equals(n);
}
public hasClash(): boolean {
this.interactions.forEach(x => {
if (x.includes('clash')) {
......@@ -199,17 +228,6 @@ namespace Model {
return false;
}
/**
* Shorthand to check if the link contains particular underlying
* residue.
*/
public containsResidue(n: Residue): boolean {
return (this.target.residue.equals(n) || this.source.residue.equals(n));
}
public isBoundMoleculeLink(): boolean {
return this.source.residue.isLigand && this.target.residue.isLigand && this.interactions.get(InteractionType.AtomAtom).includes('covalent');
}
......@@ -245,30 +263,20 @@ namespace Model {
}
export class LigandResidueLink implements Link {
source: InteractionNode;
target: InteractionNode;
export class LigandResidueLink extends Link {
interaction: Array<Interaction>;
constructor(begin: InteractionNode, end: InteractionNode, beginAtoms: string[], endAtoms: string[],
interactionType: string, interactionDetails: string[], distance: number) {
this.source = begin;
this.target = end;
super(begin, end);
this.interaction = new Array<Interaction>();
this.interaction.push(
new Interaction(beginAtoms, endAtoms, InteractionTypeUtil.parse(interactionType.replace('-', '_')), interactionDetails, distance)
);
}
public containsBothNodes(a: InteractionNode, b: InteractionNode): boolean {
let condA = this.source.equals(a) && this.target.equals(b);
let condB = this.source.equals(b) && this.target.equals(a);
return condA || condB;
}
public addInteraction(beginAtoms: string[], endAtoms: string[], interactionType: string, interactionDetails: string[], distance: number): void {
this.interaction.push(
new Interaction(beginAtoms, endAtoms, InteractionTypeUtil.parse(interactionType.replace('-', '_')), interactionDetails, distance)
......@@ -293,10 +301,6 @@ namespace Model {
return 'other';
}
public containsInteractionNode(node: InteractionNode) {
return this.source.equals(node) || this.target.equals(node);
}
public hasClash(): boolean {
this.interaction.forEach(x => {
x.interactionsClases.forEach(y => {
......@@ -371,6 +375,19 @@ namespace Model {
this.links.push(link);
});
let searchableNodes = Array.from(this.tmpNodesSet);
data.composition.connections.forEach(pair => {
let bgn = searchableNodes.find(x => x.residue.id === pair[0]);
let end = searchableNodes.find(x => x.residue.id === pair[1]);
let result = this.links.find(x => x.containsBothNodes(bgn, end));
if (result === undefined) {
let link = new ResidueResidueLink(bgn, end, { 'atom_atom': ['covalent'] });
this.links.push(link);
}
});
this.interactionNodes = Array.from(this.tmpNodesSet.values());
return this;
......@@ -424,7 +441,7 @@ namespace Model {
return; // we do not want to show 'self interactions'
}
let tmpLink = tmpLinks.find(x => (<LigandResidueLink>x).containsBothNodes(bgnNode, endNode)) as LigandResidueLink;
let tmpLink = tmpLinks.find(x => x.containsBothNodes(bgnNode, endNode));
if (tmpLink !== undefined) {
tmpLink.addInteraction(x.ligand_atoms, x.end.atom_names, x.interaction_type, x.interaction_details, x.distance);
......
......@@ -5,6 +5,7 @@
*/
class ResidueProvider {
private static instance: ResidueProvider;
private environment: Model.Environment;
private mapping: Map<string, string>;
public downloadPromises: Array<Promise<any>>;
......@@ -14,7 +15,8 @@ class ResidueProvider {
* construction calls with the `new` operator.
* @memberof ResidueProvider
*/
private constructor() {
private constructor(env: Model.Environment) {
this.environment = env;
this.mapping = new Map<string, string>(Config.aaAbreviations);
this.downloadPromises = new Array<Promise<any>>();
}
......@@ -30,9 +32,9 @@ class ResidueProvider {
* @returns {ResidueProvider}
* @memberof ResidueProvider
*/
public static getInstance(): ResidueProvider {
public static getInstance(env: Model.Environment = Model.Environment.Production): ResidueProvider {
if (!ResidueProvider.instance) {
ResidueProvider.instance = new ResidueProvider();
ResidueProvider.instance = new ResidueProvider(env);
}
return ResidueProvider.instance;
......@@ -61,7 +63,7 @@ class ResidueProvider {
public downloadAnnotation(r: Model.Residue): void {
if (this.mapping.has(r.chemCompId) || r.isLigand) return;
let url = Resources.residueTypeAPI(r.chemCompId);
let url = Resources.residueTypeAPI(r.chemCompId, this.environment);
let res = d3.json(url).then(x => {
let code = x[r.chemCompId][0].one_letter_code;
this.mapping.set(r.chemCompId, code);
......
namespace Resources {
export const productionAPI: string = 'https://www.ebi.ac.uk/pdbe/graph-api';
export const devAPI: string = 'https://wwwdev.ebi.ac.uk/pdbe/graph-api';
export const intAPI: string = 'https://wwwint.ebi.ac.uk/pdbe/graph-api';
export const productionAPI: string = 'https://www.ebi.ac.uk/pdbe';
export const devAPI: string = 'https://wwwdev.ebi.ac.uk/pdbe';
export const intAPI: string = 'https://wwwint.ebi.ac.uk/pdbe';
// export const glycanSymbols: string = 'https://pdbe.gitdocs.ebi.ac.uk/web-components/ligand-env/pdb-snfg-visuals.xml';
// export const glycanMapping: string = 'https://pdbe.gitdocs.ebi.ac.uk/web-components/ligand-env/het_mapping.json';
// export const componentSvgCss: string = 'https://pdbe.gitdocs.ebi.ac.uk/web-components/ligand-env/pdb-ligand-env-svg.css';
export const boundMoleculeURL: string = 'graph-api/pdb/bound_molecule_interactions';
export const carbohydrateURL: string = 'graph-api/pdb/carbohydrate_polymer_interactions';
export const boundLigandURL: string = 'graph-api/pdb/bound_ligand_interactions';
export const compoundSummaryURL: string = 'api/pdb/compound/summary';
export const componentLibraryURL: string = 'pdb-component-library/data/ligand-env';
export const staticFilesURL: string = 'static/files/pdbechem_v2';
export const glycanSymbols: string = 'https://www.ebi.ac.uk/pdbe/pdb-component-library/data/ligand-env/pdb-snfg-visuals.xml';
export const glycanMapping: string = 'https://www.ebi.ac.uk/pdbe/pdb-component-library/data/het_mapping.json';
export const componentSvgCss: string = 'https://www.ebi.ac.uk/pdbe/pdb-component-library/data/pdb-ligand-env-svg.css';
export function glycanSymbolsAPI(env: Model.Environment) {
let url = '';
switch (env) {
case Model.Environment.Internal:
case Model.Environment.Development: {
url = `${devAPI}/${componentLibraryURL}/pdb-snfg-visuals.xml`;
break;
}
default: {
url = `${productionAPI}/${componentLibraryURL}/pdb-snfg-visuals.xml`;
break;
}
}
return url;
}
export const residueTypeURL: string = "https://www.ebi.ac.uk/pdbe/api/pdb/compound/summary/";
export function ligandAnnotationAPI(ligandName: string, env: Model.Environment): string {
export function hetMappingAPI(env: Model.Environment) {
let url = '';
switch (env) {
case Model.Environment.Production: {
url = `https://www.ebi.ac.uk/pdbe/static/files/pdbechem_v2/${ligandName}/annotation`;
break;
switch (env) {
case Model.Environment.Internal:
case Model.Environment.Development: {
url = `${devAPI}/${componentLibraryURL}/het_mapping.json`;
break;
}
case Model.Environment.Development:
default: {
url = `${productionAPI}/${componentLibraryURL}/het_mapping.json`;
break;
}
}
return url;
}
export function ligEnvCSSAPI(env: Model.Environment) {
let url = '';
switch (env) {
case Model.Environment.Internal:
url = `https://wwwdev.ebi.ac.uk/pdbe/static/files/pdbechem_v2/${ligandName}/annotation`;
case Model.Environment.Development: {
url = `${devAPI}/pdb-component-library/css/pdb-ligand-env-svg.css`;
break;
}
default: {
url = `${productionAPI}/pdb-component-library/css/pdb-ligand-env-svg.css`;
break;
}
}
return url;
}
export function boundMoleculeAPI(pdbId: string, bmId: string, env: Model.Environment): string {
export function ligandAnnotationAPI(ligandName: string, env: Model.Environment): string {
let url = '';
switch (env) {
case Model.Environment.Production: {
url = `${productionAPI}/pdb/bound_molecule_interactions/${pdbId}/${bmId}`;
url = `${productionAPI}/${staticFilesURL}/${ligandName}/annotation`;
break;
}
case Model.Environment.Development:
case Model.Environment.Internal:
url = `${devAPI}/${staticFilesURL}/${ligandName}/annotation`;
}
return url;
}
export function boundMoleculeAPI(pdbId: string, bmId: string, env: Model.Environment): string {
let url = '';
switch (env) {
case Model.Environment.Development: {
url = `${devAPI}/pdb/bound_molecule_interactions/${pdbId}/${bmId}`;
url = `${devAPI}/${boundMoleculeURL}/${pdbId}/${bmId}`;
break;
}
case Model.Environment.Internal: {
url = `${intAPI}/pdb/bound_molecule_interactions/${pdbId}/${bmId}`;
url = `${intAPI}/${boundMoleculeURL}/${pdbId}/${bmId}`;
break;
}
default:
url = `${productionAPI}/${boundMoleculeURL}/${pdbId}/${bmId}`;
break;
}
return url;
}
export function carbohydratePolymerAPI(pdbId: string, bmId: string, entityId: string, env: Model.Environment): string {
let url = '';
switch (env) {
case Model.Environment.Production: {
url = `${productionAPI}/pdb/carbohydrate_polymer_interactions/${pdbId}/${bmId}/${entityId}`;
break;
}
case Model.Environment.Development: {
url = `${devAPI}/pdb/carbohydrate_polymer_interactions/${pdbId}/${bmId}/${entityId}`;
url = `${devAPI}/${carbohydrateURL}/${pdbId}/${bmId}/${entityId}`;
break;
}
case Model.Environment.Internal: {
url = `${intAPI}/pdb/carbohydrate_polymer_interactions/${pdbId}/${bmId}/${entityId}`;
url = `${intAPI}/${carbohydrateURL}/${pdbId}/${bmId}/${entityId}`;
break;
}
default:
url = `${productionAPI}/${carbohydrateURL}/${pdbId}/${bmId}/${entityId}`;
break;
}
return url;
}
/**
* Retrieve ligand interactions URL given the environment
*
* @export
* @param {string} pdbId
* @param {string} chainId
* @param {number} resId
* @param {Model.Environment} env
* @returns
*/
export function ligandInteractionsAPI(pdbId: string, chainId: string, resId: number, env: Model.Environment) {
let url = '';
switch (env) {
case Model.Environment.Production: {
url = `${productionAPI}/pdb/bound_ligand_interactions/${pdbId}/${chainId}/${resId}`;
break;
}
case Model.Environment.Development: {
url = `${devAPI}/pdb/bound_ligand_interactions/${pdbId}/${chainId}/${resId}`;
url = `${devAPI}/${boundLigandURL}/${pdbId}/${chainId}/${resId}`;
break;
}
case Model.Environment.Internal: {
url = `${intAPI}/pdb/bound_ligand_interactions/${pdbId}/${chainId}/${resId}`;
url = `${intAPI}/${boundLigandURL}/${pdbId}/${chainId}/${resId}`;
break;
}
default:
url = `${productionAPI}/${boundLigandURL}/${pdbId}/${chainId}/${resId}`;
break;
}
return url;
}
export function residueTypeAPI(chemCompId: string): string {
return `${residueTypeURL}${chemCompId}`;
/**
* Return residue type URL API
*
* @export
* @param {string} chemCompId
* @param {Model.Environment} env
* @returns {string}
*/
export function residueTypeAPI(chemCompId: string, env: Model.Environment): string {
let url = '';
switch (env) {
case Model.Environment.Development: {
url = `${devAPI}/${compoundSummaryURL}/${chemCompId}`;
break;
}
case Model.Environment.Internal: {
url = `${intAPI}/${compoundSummaryURL}/${chemCompId}`;
break;
}
default:
url = `${productionAPI}/${compoundSummaryURL}/${chemCompId}`;
break;
}
return url;
}
}
......@@ -64,11 +64,11 @@ let helpBonds = `
<table class='pdb-lig-env-help-table' style="border-bottom: 0.5px solid black; padding-bottom: 10px;">
<tr>
<td>
<hr style="border: 0 none; border-top: 5px solid #AD4379; background: none; height: 0;" />
<hr style="border: 0 none; border-top: 5px dashed #AD4379; background: none; height: 0;" />
</td>
<td>aromatic</td>
<td>
<hr style="border: 0 none; border-top: 5px solid #FF5050; background: none; height: 0;" />
<hr style="border: 0 none; border-top: 5px dashed #FF5050; background: none; height: 0;" />
</td>
<td>clashes</td>
</tr>
......@@ -78,7 +78,7 @@ let helpBonds = `
</td>
<td>covalent</td>
<td>
<hr style="border: 0 none; border-top: 5px solid #3F26BF; background: none; height: 0;" />
<hr style="border: 0 none; border-top: 5px dashed #3F26BF; background: none; height: 0;" />
</td>
<td>electrostatic</td>
......@@ -94,7 +94,7 @@ let helpBonds = `
<tr>
<td>
<hr style="border: 0 none; border-top: 5px solid #9B7653; background: none; height: 0;" />
<hr style="border: 0 none; border-top: 5px dashed #9B7653; background: none; height: 0;" />
</td>
<td>vdw</td>
</tr>
......
......@@ -6,22 +6,25 @@
*
* @class VisualsMapper
*/
class VisualsMapper {
class VisualsMapper {
public glycanImages: Map<string, SVGElement>;
public glycanMapping: Map<string, string>;
public glycanMapping: Map<string, string>;
public graphicsPromise: Promise<void>;
public mappingPromise: Promise<void>;
constructor() {
constructor(env: Model.Environment) {
this.glycanMapping = new Map<string, string>();
this.glycanImages = new Map<string, SVGElement>();
this.graphicsPromise = this.parseSymbols(Resources.glycanSymbols);
this.mappingPromise = this.parseGlycanMapping(Resources.glycanMapping);
let symbolsUrl = Resources.glycanSymbolsAPI(env);
let mappingUrl = Resources.hetMappingAPI(env);
this.graphicsPromise = this.parseSymbols(symbolsUrl);
this.mappingPromise = this.parseGlycanMapping(mappingUrl);
}
/**
* Gets proper glycan in the SVG format based on teh glycan class.
*
......@@ -49,7 +52,7 @@ class VisualsMapper {
let parsedImages: any = x.documentElement.getElementsByTagName('glycans')[0].getElementsByTagName('g');
for (let img of parsedImages) {
this.glycanImages.set(img.getAttribute('name'), img.outerHTML);
}
}
});
}
......
.pdb-lig-env-svg-shadow-node {
stroke-width: 0;
fill: white;
}
.pdb-lig-env-svg-shadow-bond {
stroke-width: 15px;
stroke-width: 10px;
stroke: transparent;
}
......@@ -29,16 +25,19 @@
.pdb-lig-env-svg-bond-stacking {
stroke-width: 2px;
stroke-dasharray: 10 10;
stroke: green;
}
.pdb-lig-env-svg-bond-atom-pi {
stroke-width: 2px;
stroke-dasharray: 10 10;
stroke: #2E8B57;
}
.pdb-lig-env-svg-bond-amide {
stroke-width: 2px;
stroke-dasharray: 10 10;
stroke: green;
}
......@@ -65,16 +64,18 @@
.pdb-lig-env-svg-bond-clashes {
stroke-width: 2px;
stroke-dasharray: 10 10;
stroke: #FF5050;
}
.pdb-lig-env-svg-bond-covalent {
stroke-width: 2px;
stroke-width: 3px;
stroke: black;
}
.pdb-lig-env-svg-bond-other {
stroke-width: 1px;
stroke-dasharray: 10 10;
stroke: black;
}
......@@ -88,7 +89,7 @@
.pdb-lig-env-svg-node text {
cursor: inherit;
font-family: sans-serif;
font-family: 'Exo 2', sans-serif;
stroke: black !important;
fill: black !important;
}
......@@ -96,13 +97,13 @@
.pdb-lig-env-svg-node text tspan:first-child {
cursor: inherit;
font-weight: 100;
font-size: 1em;
font-size: 1.3em;
}
.pdb-lig-env-svg-node text tspan:nth-child(2) {
cursor: inherit;
font-weight: lighter;
font-size: 0.75em;
font-size: 1.3em;
}
.pdb-lig-env-svg-ligand-res {
......
......@@ -10,7 +10,7 @@
height: auto;
padding: 10px;
font: 12px sans-serif;
background: #ccd4e0;
background: #e9e6e0;
overflow: auto;
opacity: 0.8;
word-wrap: normal;
......@@ -34,7 +34,7 @@
}
.pdb-lig-env-menu-panel {
background: #ccd4e0;
background: #e9e6e0;
opacity: 0.9;
float: right;
height: 30px;
......@@ -58,7 +58,7 @@
}
.pdb-lig-env-menu-panel>i:hover {
color: #637ca0;
color: #ae5d04;
}
.pdb-lig-env-toolbar-container {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment