Skip to content

Commit a6b5440

Browse files
committed
Pull request #691: Enabled to drag and drop to open mmCIF, PDB, PNG, and BCF files.
Merge in STRUC/icn3d from release3.44.2 to master * commit '9a3f467218606d8d4c61350b3ff02b9b3b7f2b2e': Enabled to drag and drop to open mmCIF, PDB, PNG, and BCF files.
2 parents 6c05867 + 9a3f467 commit a6b5440

24 files changed

+607
-46
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
## Change Log
2+
[icn3d-3.44.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.44.2.zip) was release on June 30, 2025. Enabled to drag and drop to open mmCIF, PDB, PNG, and BCF files.
3+
24
[icn3d-3.44.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.44.1.zip) was release on June 26, 2025. Output BCF viewpoint file via the menu "File > Save File > BCF Viewpoint".
35

46
[icn3d-3.44.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.44.0.zip) was release on June 11, 2025. Upgraded three.js to version 177 and compiled three.js directly into iCn3D library. No need to include three.js as a separate library.

build/icn3d.js

Lines changed: 116 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57390,6 +57390,10 @@ void main() {
5739057390
me.htmlCls.dialogCls.openDlg('dl_state', 'Please input the state file');
5739157391
});
5739257392

57393+
me.myEventCls.onIds("#" + me.pre + "mn1_bcfviewpoint", "click", function(e) { me.icn3d; //e.preventDefault();
57394+
me.htmlCls.dialogCls.openDlg('dl_bcfviewpoint', 'Please input the BCF viewpoint file');
57395+
});
57396+
5739357397
me.myEventCls.onIds("#" + me.pre + "mn1_selection", "click", function(e) { me.icn3d; //e.preventDefault();
5739457398
me.htmlCls.dialogCls.openDlg('dl_selection', 'Please input the selection file');
5739557399
});
@@ -60303,6 +60307,7 @@ void main() {
6030360307
html += this.getLink('mn1_fixedversion', 'Share Link in Archived Ver. ' + me.htmlCls.wifiStr, undefined, 2);
6030460308
html += this.getLink('mn1_selection', 'Selection File', undefined, 2);
6030560309
html += this.getLink("mn1_collection", "Collection File", undefined, 2);
60310+
html += this.getLink('mn1_bcfviewpoint', 'BCF Viewpoint File', undefined, 2);
6030660311

6030760312
html += this.getMenuSep();
6030860313

@@ -62791,6 +62796,12 @@ void main() {
6279162796
html += me.htmlCls.buttonStr + "reload_state' style='margin-top: 6px;'>Load</button>";
6279262797
html += "</div>";
6279362798

62799+
html += me.htmlCls.divStr + "dl_bcfviewpoint' class='" + dialogClass + "'>";
62800+
html += this.addNotebookTitle('dl_bcfviewpoint', 'Please input a BCF viewpoint file');
62801+
html += "BCF viewpoint file: " + me.htmlCls.inputFileStr + "id='" + me.pre + "bcfviewpoint'><br/>";
62802+
html += me.htmlCls.buttonStr + "reload_bcfviewpoint' style='margin-top: 6px;'>Load</button>";
62803+
html += "</div>";
62804+
6279462805
html += me.htmlCls.divStr + "dl_video' class='" + dialogClass + "'>";
6279562806
html += this.addNotebookTitle('dl_video', 'Save canvas changes in a video');
6279662807
html += "State file: " + me.htmlCls.inputFileStr + "id='" + me.pre + "state'><br/>";
@@ -64285,6 +64296,67 @@ void main() {
6428564296
}
6428664297
}
6428764298

64299+
async openBcf(file) { let me = this.icn3dui, ic = me.icn3d;
64300+
let url = './script/jszip.min.js';
64301+
await me.getAjaxPromise(url, 'script');
64302+
64303+
let jszip = new JSZip();
64304+
64305+
me.htmlCls.setHtmlCls.fileSupport();
64306+
64307+
jszip.loadAsync(file).then(function(zip) {
64308+
zip.forEach(function (relativePath, zipEntry) {
64309+
if (zipEntry.dir) {
64310+
// Handle directory creation
64311+
let folder = jszip.folder(relativePath);
64312+
folder.forEach(function (filename, zipEntry2) {
64313+
if(filename.substr(0, 9) == 'viewpoint') {
64314+
zipEntry2.async('string') // or 'blob', 'arraybuffer'
64315+
.then(function(fileData) {
64316+
let parser = new DOMParser();
64317+
let xmlDoc = parser.parseFromString(fileData, "text/xml");
64318+
64319+
// Accessing elements
64320+
//const author = xmlDoc.getElementsByTagName("author")[0].textContent;
64321+
//const author = xmlDoc.querySelector("author").textContent;
64322+
let viewpoint = xmlDoc.querySelector("CameraViewPoint");
64323+
let direction = xmlDoc.querySelector("CameraDirection");
64324+
let upvector = xmlDoc.querySelector("CameraUpVector");
64325+
let fov = xmlDoc.querySelector("FieldOfView").textContent;
64326+
xmlDoc.querySelector("AspectRatio").textContent;
64327+
64328+
let childNodes, viewpointArray = [], directionArray = [], upvectorArray = [];
64329+
64330+
childNodes = viewpoint.children;
64331+
viewpointArray = [childNodes[0].textContent, childNodes[1].textContent, childNodes[2].textContent];
64332+
childNodes = direction.children;
64333+
directionArray = [childNodes[0].textContent, childNodes[1].textContent, childNodes[2].textContent];
64334+
childNodes = upvector.children;
64335+
upvectorArray = [childNodes[0].textContent, childNodes[1].textContent, childNodes[2].textContent];
64336+
64337+
ic.cam.position.set(viewpointArray[0], viewpointArray[1], viewpointArray[2]);
64338+
ic.cam.quaternion.setFromUnitVectors(new Vector3$1(0, 0, -1), new Vector3$1(directionArray[0], directionArray[1], directionArray[2]));
64339+
ic.cam.up.set(upvectorArray[0], upvectorArray[1], upvectorArray[2]);
64340+
ic.cam.fov = fov;
64341+
//ic.container.whratio = aspect;
64342+
64343+
ic.drawCls.applyTransformation(ic._zoomFactor, ic.mouseChange, ic.quaternion);
64344+
ic.drawCls.render();
64345+
});
64346+
}
64347+
});
64348+
}
64349+
// else {
64350+
// // Handle file extraction
64351+
// zipEntry.async("string").then(function (content) {
64352+
// });
64353+
// }
64354+
});
64355+
}, function (e) {
64356+
console.error("Error loading BCF viewpoint file:", e);
64357+
});
64358+
}
64359+
6428864360
//Hold all functions related to click events.
6428964361
allEventFunctions() { let me = this.icn3dui, ic = me.icn3d;
6429064362
let thisClass = this;
@@ -64711,7 +64783,7 @@ void main() {
6471164783
let v2Y = $("#" + me.pre + "v2Y").val();
6471264784
let v2Z = $("#" + me.pre + "v2Z").val();
6471364785

64714-
let angleRad = new THREE.Vector3(parseFloat(v1X), parseFloat(v1Y), parseFloat(v1Z)).angleTo(new THREE.Vector3(parseFloat(v2X), parseFloat(v2Y), parseFloat(v2Z)));
64786+
let angleRad = new Vector3$1(parseFloat(v1X), parseFloat(v1Y), parseFloat(v1Z)).angleTo(new Vector3$1(parseFloat(v2X), parseFloat(v2Y), parseFloat(v2Z)));
6471564787
let angle = angleRad / 3.1416 * 180;
6471664788
angle = Math.abs(angle).toFixed(0);
6471764789
if(angle > 180) angle -= 180;
@@ -65377,6 +65449,19 @@ void main() {
6537765449
}
6537865450
});
6537965451

65452+
me.myEventCls.onIds("#" + me.pre + "reload_bcfviewpoint", "click", async function(e) { me.icn3d;
65453+
e.preventDefault();
65454+
if(!me.cfg.notebook) dialog.dialog( "close" );
65455+
65456+
let file = $("#" + me.pre + "bcfviewpoint")[0].files[0];
65457+
if(!file) {
65458+
alert("Please select a file before clicking 'Load'");
65459+
}
65460+
else {
65461+
await thisClass.openBcf(file);
65462+
}
65463+
});
65464+
6538065465
me.myEventCls.onIds("#" + me.pre + "reload_selectionfile", "click", function(e) { let ic = me.icn3d;
6538165466
e.preventDefault();
6538265467
if(!me.cfg.notebook) dialog.dialog( "close" );
@@ -66812,8 +66897,8 @@ void main() {
6681266897
let posArray1 = ic.contactCls.getExtent(atomSet1);
6681366898
let posArray2 = ic.contactCls.getExtent(atomSet2);
6681466899

66815-
let pos1 = new THREE.Vector3(posArray1[2][0], posArray1[2][1], posArray1[2][2]);
66816-
let pos2 = new THREE.Vector3(posArray2[2][0], posArray2[2][1], posArray2[2][2]);
66900+
let pos1 = new Vector3$1(posArray1[2][0], posArray1[2][1], posArray1[2][2]);
66901+
let pos2 = new Vector3$1(posArray2[2][0], posArray2[2][1], posArray2[2][2]);
6681766902

6681866903
let radius = $("#" + me.pre + "linebtwsets_radius").val();
6681966904
let color = $("#" + me.pre + "linebtwsets_customcolor").val();
@@ -66837,7 +66922,7 @@ void main() {
6683766922
let nameArray = $("#" + me.pre + "cartoonshape").val();
6683866923
let atomSet1 = ic.definedSetsCls.getAtomsFromNameArray(nameArray);
6683966924
let posArray1 = ic.contactCls.getExtent(atomSet1);
66840-
let pos1 = new THREE.Vector3(posArray1[2][0], posArray1[2][1], posArray1[2][2]);
66925+
let pos1 = new Vector3$1(posArray1[2][0], posArray1[2][1], posArray1[2][2]);
6684166926

6684266927
let shape = $("#" + me.pre + "cartoonshape_shape").val(); // Sphere or Cube
6684366928
let radius = $("#" + me.pre + "cartoonshape_radius").val();
@@ -67298,6 +67383,32 @@ void main() {
6729867383
thisClass.setLogCmd("set theme black", true);
6729967384
});
6730067385

67386+
// dragover and drop
67387+
me.myEventCls.onIds("#" + me.pre + "viewer", "dragover", function(e) { me.icn3d;
67388+
e.preventDefault();
67389+
$("#" + me.pre + "viewer")[0].style.border = "5px solid blue";
67390+
});
67391+
me.myEventCls.onIds("#" + me.pre + "viewer", "drop", async function(e) { me.icn3d;
67392+
e.preventDefault();
67393+
67394+
let files = e.dataTransfer.files;
67395+
67396+
for(let i = 0, il = e.dataTransfer.files.length; i < il; ++i) {
67397+
let file = e.dataTransfer.files[i];
67398+
let fileName = file.name;
67399+
67400+
let fileType = fileName.substr(fileName.lastIndexOf('.') + 1).toLowerCase();
67401+
if(fileType == 'pdb' || fileType == 'mmcif' || fileType == 'png') {
67402+
await me.htmlCls.eventsCls.readFile(true, files, i, '', (fileType == 'mmcif'), (fileType == 'png'));
67403+
}
67404+
else if(fileType == 'bcf') {
67405+
await thisClass.openBcf(file);
67406+
}
67407+
}
67408+
67409+
$("#" + me.pre + "viewer")[0].style.border = "0px solid black";
67410+
});
67411+
6730167412
$(document).on("click", "." + me.pre + "snpin3d", async function(e) { let ic = me.icn3d;
6730267413
e.stopImmediatePropagation();
6730367414

@@ -133464,7 +133575,7 @@ void main() {
133464133575
//even when multiple iCn3D viewers are shown together.
133465133576
this.pre = this.cfg.divid + "_";
133466133577

133467-
this.REVISION = '3.44.1';
133578+
this.REVISION = '3.44.2';
133468133579

133469133580
// In nodejs, iCn3D defines "window = {navigator: {}}"
133470133581
this.bNode = (Object.keys(window).length < 2) ? true : false;

build/icn3d.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/icn3d.module.js

Lines changed: 116 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57387,6 +57387,10 @@ class ClickMenu {
5738757387
me.htmlCls.dialogCls.openDlg('dl_state', 'Please input the state file');
5738857388
});
5738957389

57390+
me.myEventCls.onIds("#" + me.pre + "mn1_bcfviewpoint", "click", function(e) { me.icn3d; //e.preventDefault();
57391+
me.htmlCls.dialogCls.openDlg('dl_bcfviewpoint', 'Please input the BCF viewpoint file');
57392+
});
57393+
5739057394
me.myEventCls.onIds("#" + me.pre + "mn1_selection", "click", function(e) { me.icn3d; //e.preventDefault();
5739157395
me.htmlCls.dialogCls.openDlg('dl_selection', 'Please input the selection file');
5739257396
});
@@ -60300,6 +60304,7 @@ class SetMenu {
6030060304
html += this.getLink('mn1_fixedversion', 'Share Link in Archived Ver. ' + me.htmlCls.wifiStr, undefined, 2);
6030160305
html += this.getLink('mn1_selection', 'Selection File', undefined, 2);
6030260306
html += this.getLink("mn1_collection", "Collection File", undefined, 2);
60307+
html += this.getLink('mn1_bcfviewpoint', 'BCF Viewpoint File', undefined, 2);
6030360308

6030460309
html += this.getMenuSep();
6030560310

@@ -62788,6 +62793,12 @@ class SetDialog {
6278862793
html += me.htmlCls.buttonStr + "reload_state' style='margin-top: 6px;'>Load</button>";
6278962794
html += "</div>";
6279062795

62796+
html += me.htmlCls.divStr + "dl_bcfviewpoint' class='" + dialogClass + "'>";
62797+
html += this.addNotebookTitle('dl_bcfviewpoint', 'Please input a BCF viewpoint file');
62798+
html += "BCF viewpoint file: " + me.htmlCls.inputFileStr + "id='" + me.pre + "bcfviewpoint'><br/>";
62799+
html += me.htmlCls.buttonStr + "reload_bcfviewpoint' style='margin-top: 6px;'>Load</button>";
62800+
html += "</div>";
62801+
6279162802
html += me.htmlCls.divStr + "dl_video' class='" + dialogClass + "'>";
6279262803
html += this.addNotebookTitle('dl_video', 'Save canvas changes in a video');
6279362804
html += "State file: " + me.htmlCls.inputFileStr + "id='" + me.pre + "state'><br/>";
@@ -64282,6 +64293,67 @@ class Events {
6428264293
}
6428364294
}
6428464295

64296+
async openBcf(file) { let me = this.icn3dui, ic = me.icn3d;
64297+
let url = './script/jszip.min.js';
64298+
await me.getAjaxPromise(url, 'script');
64299+
64300+
let jszip = new JSZip();
64301+
64302+
me.htmlCls.setHtmlCls.fileSupport();
64303+
64304+
jszip.loadAsync(file).then(function(zip) {
64305+
zip.forEach(function (relativePath, zipEntry) {
64306+
if (zipEntry.dir) {
64307+
// Handle directory creation
64308+
let folder = jszip.folder(relativePath);
64309+
folder.forEach(function (filename, zipEntry2) {
64310+
if(filename.substr(0, 9) == 'viewpoint') {
64311+
zipEntry2.async('string') // or 'blob', 'arraybuffer'
64312+
.then(function(fileData) {
64313+
let parser = new DOMParser();
64314+
let xmlDoc = parser.parseFromString(fileData, "text/xml");
64315+
64316+
// Accessing elements
64317+
//const author = xmlDoc.getElementsByTagName("author")[0].textContent;
64318+
//const author = xmlDoc.querySelector("author").textContent;
64319+
let viewpoint = xmlDoc.querySelector("CameraViewPoint");
64320+
let direction = xmlDoc.querySelector("CameraDirection");
64321+
let upvector = xmlDoc.querySelector("CameraUpVector");
64322+
let fov = xmlDoc.querySelector("FieldOfView").textContent;
64323+
xmlDoc.querySelector("AspectRatio").textContent;
64324+
64325+
let childNodes, viewpointArray = [], directionArray = [], upvectorArray = [];
64326+
64327+
childNodes = viewpoint.children;
64328+
viewpointArray = [childNodes[0].textContent, childNodes[1].textContent, childNodes[2].textContent];
64329+
childNodes = direction.children;
64330+
directionArray = [childNodes[0].textContent, childNodes[1].textContent, childNodes[2].textContent];
64331+
childNodes = upvector.children;
64332+
upvectorArray = [childNodes[0].textContent, childNodes[1].textContent, childNodes[2].textContent];
64333+
64334+
ic.cam.position.set(viewpointArray[0], viewpointArray[1], viewpointArray[2]);
64335+
ic.cam.quaternion.setFromUnitVectors(new Vector3$1(0, 0, -1), new Vector3$1(directionArray[0], directionArray[1], directionArray[2]));
64336+
ic.cam.up.set(upvectorArray[0], upvectorArray[1], upvectorArray[2]);
64337+
ic.cam.fov = fov;
64338+
//ic.container.whratio = aspect;
64339+
64340+
ic.drawCls.applyTransformation(ic._zoomFactor, ic.mouseChange, ic.quaternion);
64341+
ic.drawCls.render();
64342+
});
64343+
}
64344+
});
64345+
}
64346+
// else {
64347+
// // Handle file extraction
64348+
// zipEntry.async("string").then(function (content) {
64349+
// });
64350+
// }
64351+
});
64352+
}, function (e) {
64353+
console.error("Error loading BCF viewpoint file:", e);
64354+
});
64355+
}
64356+
6428564357
//Hold all functions related to click events.
6428664358
allEventFunctions() { let me = this.icn3dui, ic = me.icn3d;
6428764359
let thisClass = this;
@@ -64708,7 +64780,7 @@ class Events {
6470864780
let v2Y = $("#" + me.pre + "v2Y").val();
6470964781
let v2Z = $("#" + me.pre + "v2Z").val();
6471064782

64711-
let angleRad = new THREE.Vector3(parseFloat(v1X), parseFloat(v1Y), parseFloat(v1Z)).angleTo(new THREE.Vector3(parseFloat(v2X), parseFloat(v2Y), parseFloat(v2Z)));
64783+
let angleRad = new Vector3$1(parseFloat(v1X), parseFloat(v1Y), parseFloat(v1Z)).angleTo(new Vector3$1(parseFloat(v2X), parseFloat(v2Y), parseFloat(v2Z)));
6471264784
let angle = angleRad / 3.1416 * 180;
6471364785
angle = Math.abs(angle).toFixed(0);
6471464786
if(angle > 180) angle -= 180;
@@ -65374,6 +65446,19 @@ class Events {
6537465446
}
6537565447
});
6537665448

65449+
me.myEventCls.onIds("#" + me.pre + "reload_bcfviewpoint", "click", async function(e) { me.icn3d;
65450+
e.preventDefault();
65451+
if(!me.cfg.notebook) dialog.dialog( "close" );
65452+
65453+
let file = $("#" + me.pre + "bcfviewpoint")[0].files[0];
65454+
if(!file) {
65455+
var aaa = 1; //alert("Please select a file before clicking 'Load'");
65456+
}
65457+
else {
65458+
await thisClass.openBcf(file);
65459+
}
65460+
});
65461+
6537765462
me.myEventCls.onIds("#" + me.pre + "reload_selectionfile", "click", function(e) { let ic = me.icn3d;
6537865463
e.preventDefault();
6537965464
if(!me.cfg.notebook) dialog.dialog( "close" );
@@ -66809,8 +66894,8 @@ class Events {
6680966894
let posArray1 = ic.contactCls.getExtent(atomSet1);
6681066895
let posArray2 = ic.contactCls.getExtent(atomSet2);
6681166896

66812-
let pos1 = new THREE.Vector3(posArray1[2][0], posArray1[2][1], posArray1[2][2]);
66813-
let pos2 = new THREE.Vector3(posArray2[2][0], posArray2[2][1], posArray2[2][2]);
66897+
let pos1 = new Vector3$1(posArray1[2][0], posArray1[2][1], posArray1[2][2]);
66898+
let pos2 = new Vector3$1(posArray2[2][0], posArray2[2][1], posArray2[2][2]);
6681466899

6681566900
let radius = $("#" + me.pre + "linebtwsets_radius").val();
6681666901
let color = $("#" + me.pre + "linebtwsets_customcolor").val();
@@ -66834,7 +66919,7 @@ class Events {
6683466919
let nameArray = $("#" + me.pre + "cartoonshape").val();
6683566920
let atomSet1 = ic.definedSetsCls.getAtomsFromNameArray(nameArray);
6683666921
let posArray1 = ic.contactCls.getExtent(atomSet1);
66837-
let pos1 = new THREE.Vector3(posArray1[2][0], posArray1[2][1], posArray1[2][2]);
66922+
let pos1 = new Vector3$1(posArray1[2][0], posArray1[2][1], posArray1[2][2]);
6683866923

6683966924
let shape = $("#" + me.pre + "cartoonshape_shape").val(); // Sphere or Cube
6684066925
let radius = $("#" + me.pre + "cartoonshape_radius").val();
@@ -67295,6 +67380,32 @@ class Events {
6729567380
thisClass.setLogCmd("set theme black", true);
6729667381
});
6729767382

67383+
// dragover and drop
67384+
me.myEventCls.onIds("#" + me.pre + "viewer", "dragover", function(e) { me.icn3d;
67385+
e.preventDefault();
67386+
$("#" + me.pre + "viewer")[0].style.border = "5px solid blue";
67387+
});
67388+
me.myEventCls.onIds("#" + me.pre + "viewer", "drop", async function(e) { me.icn3d;
67389+
e.preventDefault();
67390+
67391+
let files = e.dataTransfer.files;
67392+
67393+
for(let i = 0, il = e.dataTransfer.files.length; i < il; ++i) {
67394+
let file = e.dataTransfer.files[i];
67395+
let fileName = file.name;
67396+
67397+
let fileType = fileName.substr(fileName.lastIndexOf('.') + 1).toLowerCase();
67398+
if(fileType == 'pdb' || fileType == 'mmcif' || fileType == 'png') {
67399+
await me.htmlCls.eventsCls.readFile(true, files, i, '', (fileType == 'mmcif'), (fileType == 'png'));
67400+
}
67401+
else if(fileType == 'bcf') {
67402+
await thisClass.openBcf(file);
67403+
}
67404+
}
67405+
67406+
$("#" + me.pre + "viewer")[0].style.border = "0px solid black";
67407+
});
67408+
6729867409
$(document).on("click", "." + me.pre + "snpin3d", async function(e) { let ic = me.icn3d;
6729967410
e.stopImmediatePropagation();
6730067411

@@ -133461,7 +133572,7 @@ class iCn3DUI {
133461133572
//even when multiple iCn3D viewers are shown together.
133462133573
this.pre = this.cfg.divid + "_";
133463133574

133464-
this.REVISION = '3.44.1';
133575+
this.REVISION = '3.44.2';
133465133576

133466133577
// In nodejs, iCn3D defines "window = {navigator: {}}"
133467133578
this.bNode = (Object.keys(window).length < 2) ? true : false;

dist/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
## Change Log
2+
[icn3d-3.44.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.44.2.zip) was release on June 30, 2025. Enabled to drag and drop to open mmCIF, PDB, PNG, and BCF files.
3+
24
[icn3d-3.44.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.44.1.zip) was release on June 26, 2025. Output BCF viewpoint file via the menu "File > Save File > BCF Viewpoint".
35

46
[icn3d-3.44.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.44.0.zip) was release on June 11, 2025. Upgraded three.js to version 177 and compiled three.js directly into iCn3D library. No need to include three.js as a separate library.

0 commit comments

Comments
 (0)