speck-renderer
Version:
Browser-based WebGL molecule renderer with the goal of producing figures that are as attractive as they are practical.
411 lines (360 loc) • 14.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _dat = require('dat.gui/build/dat.gui');
var _dat2 = _interopRequireDefault(_dat);
var _renderer = require('./renderer');
var _renderer2 = _interopRequireDefault(_renderer);
var _view = require('./view');
var _view2 = _interopRequireDefault(_view);
var _system = require('./system');
var _system2 = _interopRequireDefault(_system);
var _xyz = require('./xyz');
var _xyz2 = _interopRequireDefault(_xyz);
var _datGui = require('./config/dat-gui');
var _datGui2 = _interopRequireDefault(_datGui);
var _caffeine = require('./molecules/caffeine');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Speck = function () {
function Speck(_ref) {
var canvasContainerID = _ref.canvasContainerID,
canvasID = _ref.canvasID,
options = _ref.options;
_classCallCheck(this, Speck);
this.canvasContainer = document.getElementById(canvasContainerID);
this.canvas = document.getElementById(canvasID);
this.system = _system2.default.new();
this.view = _view2.default.new();
var defaultOptions = {
zoomRatio: 1
};
this.options = _extends({}, defaultOptions, options);
this.gui = new _dat2.default.GUI({ load: _datGui2.default });
this.renderer = new Speck.Renderer({ canvas: this.canvas, canvasContainer: this.canvasContainer, system: this.system, view: this.view, options: this.options });
this._init();
this.loadStructure(_caffeine.caffeine);
this.renderer.init();
}
_createClass(Speck, [{
key: 'setOptions',
value: function setOptions(options) {
var newOptions = _extends({}, this.options, options);
this.options = newOptions;
this.renderer.options = newOptions;
}
}, {
key: 'getOptions',
value: function getOptions() {
return this.options;
}
}, {
key: '_init',
value: function _init() {
var _this = this;
var speck = this.renderer;
this.gui.remember(speck);
var controllers = [];
var atomsFolder = this.gui.addFolder('Atoms');
controllers.push(atomsFolder.add(speck, 'atomRadius', 0, 100));
controllers.push(atomsFolder.add(speck, 'relativeAtomRadius', 0, 100));
controllers.push(atomsFolder.add(speck, 'atomShade', 0, 100));
var bondsFolder = this.gui.addFolder('Bonds');
controllers.push(bondsFolder.add(speck, 'displayBonds'));
controllers.push(bondsFolder.add(speck, 'bondRadius', 0, 100));
controllers.push(bondsFolder.add(speck, 'bondThreshold').min(0));
controllers.push(bondsFolder.add(speck, 'bondShade', 0, 100));
var shadingFolder = this.gui.addFolder('Shading');
controllers.push(shadingFolder.add(speck, 'ambientOcclusion', 0, 100));
controllers.push(shadingFolder.add(speck, 'brightness', 0, 100));
controllers.push(shadingFolder.add(speck, 'AOResolution', [16, 64, 128, 256, 512, 1024, 2048]));
controllers.push(shadingFolder.add(speck, 'samplesPerFrame', [0, 1, 2, 4, 8, 16, 32, 64, 128]));
var depthOfFieldFolder = this.gui.addFolder('Depth of Field');
controllers.push(depthOfFieldFolder.add(speck, 'depthOfFieldStrength', 0, 100));
controllers.push(depthOfFieldFolder.add(speck, 'depthOfFieldPosition', 0, 100));
var detailFolder = this.gui.addFolder('Detail');
controllers.push(detailFolder.add(speck, 'outlineStrength', 0, 100));
controllers.push(detailFolder.add(speck, 'antialiasingPasses').min(1).step(1));
controllers.push(detailFolder.add(speck, 'resolution', [64, 128, 256, 512, 1024, 2048]));
this.gui.add(speck, 'center');
this.gui.add(speck, 'downloadImage');
var _loop = function _loop(i, len) {
var controller = controllers[i];
controller.onChange(function (value) {
switch (controller.property) {
case "atomRadius":
_this.renderer.view.atomScale = value / 100;
break;
case "relativeAtomRadius":
_this.renderer.view.relativeAtomScale = value / 100;
break;
case "atomShade":
_this.renderer.view.atomShade = value / 100;
break;
case "displayBonds":
_this.renderer.view.bonds = value;
_this.renderer.renderer.setSystem(_this.renderer.system, _this.renderer.view);
break;
case "bondRadius":
_this.renderer.view.bondScale = value / 100;
break;
case "bondThreshold":
_this.renderer.view.bondThreshold = value;
_this.renderer.renderer.setSystem(_this.renderer.system, _this.renderer.view);
break;
case "bondShade":
_this.renderer.view.bondShade = value / 100;
break;
case "ambientOcclusion":
_this.renderer.view.ao = value / 100;
break;
case "brightness":
_this.renderer.view.brightness = value / 100;
break;
case "AOResolution":
_this.renderer.renderer.setResolution(speck.resolution, value);
break;
case "samplesPerFrame":
_this.renderer.view.spf = value;
break;
case "depthOfFieldStrength":
_this.renderer.view.dofStrength = value / 100;
break;
case "depthOfFieldPosition":
_this.renderer.view.dofPosition = value / 100;
break;
case "outlineStrength":
_this.renderer.view.outline = value / 100;
break;
case "antialiasingPasses":
_this.renderer.view.fxaa = value;
break;
case "resolution":
_this.renderer.renderer.setResolution(value, speck.AOResolution);
break;
}
_view2.default.resolve(_this.renderer.view);
_this.renderer.reset();
});
};
for (var i = 0, len = controllers.length; i < len; i++) {
_loop(i, len);
}
this.gui.closed = true;
}
}, {
key: 'loadStructure',
value: function loadStructure(molecule) {
var structure = (0, _xyz2.default)(molecule)[0];
this.system = _system2.default.new();
for (var i = 0; i < structure.length; i++) {
var a = structure[i];
var x = a.position[0];
var y = a.position[1];
var z = a.position[2];
_system2.default.addAtom(this.system, a.symbol, x, y, z);
}
_system2.default.center(this.system);
_system2.default.calculateBonds(this.system);
this.renderer.system = this.system;
this.renderer.renderer.setSystem(this.system, this.view);
_view2.default.center(this.view, this.system, this.options.zoomRatio);
this.renderer.reset();
}
}, {
key: 'destroy',
value: function destroy() {
this.gui.destroy();
this.renderer.destroy();
}
}]);
return Speck;
}();
Speck.Renderer = function () {
function _class(_ref2) {
var canvas = _ref2.canvas,
canvasContainer = _ref2.canvasContainer,
system = _ref2.system,
view = _ref2.view,
options = _ref2.options;
_classCallCheck(this, _class);
this.atomRadius = 60;
this.relativeAtomRadius = 100;
this.atomShade = 50;
this.displayBonds = true;
this.bondRadius = 50;
this.bondThreshold = 1.2;
this.bondShade = 50;
this.ambientOcclusion = 75;
this.brightness = 50;
this.AOResolution = 512;
this.samplesPerFrame = 32;
this.depthOfFieldStrength = 0;
this.depthOfFieldPosition = 50;
this.outlineStrength = 0;
this.antialiasingPasses = 1;
this.resolution = 512;
this.lastX = 0.0;
this.lastY = 0.0;
this.mouseDown = false;
this.needReset = false;
this.system = system;
this.view = view;
this.canvas = canvas;
this.canvasContainer = canvasContainer;
this.options = options;
this.renderer = new _renderer2.default(this.canvas, this.view.resolution, this.view.aoRes);
}
_createClass(_class, [{
key: 'init',
value: function init() {
this._createListeners();
this._loop();
this.view.atomScale = this.atomRadius / 100;
this.view.relativeAtomScale = this.relativeAtomRadius / 100;
this.view.atomShade = this.atomShade / 100;
this.view.bonds = this.displayBonds;
this.view.bondScale = this.bondRadius / 100;
this.view.bondThreshold = this.bondThreshold;
this.view.bondShade = this.bondShade / 100;
this.view.ao = this.ambientOcclusion / 100;
this.view.brightness = this.brightness / 100;
this.view.spf = this.samplesPerFrame;
this.view.dofStrength = this.depthOfFieldStrength / 100;
this.view.dofPosition = this.depthOfFieldPosition / 100;
this.view.outline = this.outlineStrength / 100;
this.view.fxaa = this.antialiasingPasses;
this.renderer.setSystem(this.system, this.view);
this.renderer.setResolution(this.resolution, this.AOResolution);
_view2.default.resolve(this.view);
this.reset();
}
}, {
key: 'reset',
value: function reset() {
this.needReset = true;
}
}, {
key: 'center',
value: function center() {
_view2.default.center(this.view, this.system, this.options.zoomRatio);
this.needReset = true;
}
}, {
key: 'downloadImage',
value: function downloadImage() {
this.renderer.render(this.view);
var imgURL = this.canvas.toDataURL("image/png");
var iframe = "<iframe width='100%' height='100%' style={border: none, overflow: auto} src='" + imgURL + "'></iframe>";
var x = window.open();
x.document.open();
x.document.write(iframe);
x.document.body.style.margin = "0";
x.document.body.style.height = "100%";
x.document.body.style.width = "100%";
x.document.close();
}
}, {
key: 'destroy',
value: function destroy() {
this._destroyListeners();
this._endLoop();
}
}, {
key: '_canvasScrollListener',
value: function _canvasScrollListener(event) {
var wd = 0;
if (event.deltaY < 0) {
wd = 1;
} else {
wd = -1;
}
this.view.zoom = this.view.zoom * (wd === 1 ? 1 / 0.9 : 0.9);
this.needReset = true;
event.preventDefault();
}
}, {
key: '_canvasMousedownListener',
value: function _canvasMousedownListener(event) {
document.body.style.cursor = "none";
this.mouseDown = true;
this.lastX = event.clientX;
this.lastY = event.clientY;
}
}, {
key: '_windowMouseupListener',
value: function _windowMouseupListener(event) {
document.body.style.cursor = "default";
this.mouseDown = false;
}
}, {
key: '_windowMousemoveListener',
value: function _windowMousemoveListener(event) {
if (!this.mouseDown) {
return;
}
var dx = event.clientX - this.lastX;
var dy = event.clientY - this.lastY;
if (dx == 0 && dy == 0) {
return;
}
if (event.shiftKey) {
_view2.default.translate(this.view, dx, dy);
} else {
_view2.default.rotate(this.view, dx, dy);
}
this.needReset = true;
this.lastX = event.clientX;
this.lastY = event.clientY;
}
}, {
key: '_createListeners',
value: function _createListeners() {
this.canvasContainer.addEventListener("wheel", this._canvasScrollListener.bind(this));
this.canvasContainer.addEventListener("mousedown", this._canvasMousedownListener.bind(this));
window.addEventListener("mouseup", this._windowMouseupListener.bind(this));
window.addEventListener("mousemove", this._windowMousemoveListener.bind(this));
this.mouseCheckInterval = setInterval(function () {
if (!this.mouseDown) {
document.body.style.cursor = "default";
}
}, 20);
}
}, {
key: '_destroyListeners',
value: function _destroyListeners() {
this.canvasContainer.removeEventListener("wheel", this._canvasScrollListener.bind(this));
this.canvasContainer.removeEventListener("mousedown", this._canvasMousedownListener.bind(this));
window.removeEventListener("mouseup", this._windowMouseupListener.bind(this));
window.removeEventListener("mousemove", this._windowMousemoveListener.bind(this));
clearInterval(this.mouseCheckInterval);
}
}, {
key: '_loop',
value: function _loop() {
// document.getElementById("ao-indicator").style.width = Math.min(100, (renderer.getAOProgress() * 100)) + "%";
if (this.needReset) {
this.renderer.reset();
}
this.needReset = false;
this.renderer.render(this.view);
this.reqAnimationFrame = requestAnimationFrame(this._loop.bind(this));
}
}, {
key: '_endLoop',
value: function _endLoop() {
cancelAnimationFrame(this.reqAnimationFrame);
}
}, {
key: 'dataURL',
get: function get() {
this.renderer.render(this.view);
return this.canvas.toDataURL("image/png");
}
}]);
return _class;
}();
exports.default = Speck;