kekule
Version:
Open source JavaScript toolkit for chemoinformatics
264 lines (253 loc) • 6.07 kB
JavaScript
/**
* @fileoverview
* Web worker to generator 3D structure from 2D one by OpenBabel library.
* @author Partridge Jiang
*/
(function($root)
{
var importedScriptUrls = [];
var obScriptLoaded = false;
var runtimeInited = false;
var initOps;
var Module;
var pendingFuncs = [];
function initEnv()
{
if (!obScriptLoaded) // OpenBabel.js not loaded, can not init now, suspend
return;
if (typeof(Module) === 'object') // module already set up, no need to set up again
return;
if (initOps)
{
var moduleName = initOps.moduleName;
var initCallbackName = initOps.initCallbackName;
var module = $root[moduleName];
if (module && initCallbackName)
{
$root[initCallbackName] = function() // init callback function, 3D generation can only done after wasm runtime is done
{
runtimeInited = true;
execPendingFuncs(pendingFuncs);
}
}
if (initOps.usingModulaize && typeof(module) === 'function')
{
module = module();
initModule(module);
}
Module = module;
}
}
function initModule(module)
{
module.print = function(data) {
postMessage({'type': 'print', 'data': data});
console.log(data);
};
module.printErr = function(data) {
postMessage({'type': 'printErr', 'data': data});
console.log(data);
};
}
function execPendingFuncs(funcs)
{
if (!funcs)
funcs = pendingFuncs;
var func = funcs.shift();
if (func)
{
func.apply($root);
execPendingFuncs(funcs);
}
}
function requestExec(func)
{
pendingFuncs.push(func);
if (runtimeInited)
execPendingFuncs();
}
addEventListener('message', function(e)
{
handleMessages(e.data);
});
function handleMessages(msgData)
{
var msgType = msgData.type;
if (msgType === 'importScript')
{
if (importedScriptUrls.indexOf(msgData.url) < 0)
{
//console.log('import script', msgData.url);
importScripts(msgData.url);
obScriptLoaded = true;
importedScriptUrls.push(msgData.url);
// after import, set intial data
initEnv();
}
}
else if (msgType === 'obInit') // set up initial environment
{
initOps = msgData;
initEnv();
}
else if (msgType === 'gen3D' || msgType === 'gen2D')
{
//console.log('recieve', msgType, msgData);
var invokerUid = msgData.uid;
var molData = msgData.molData;
if (msgType === 'gen2D')
{
var execFunc = function ()
{
var genData = generate2DMolData(molData, {});
//console.log('post data', invokerUid, genData);
// feedback
postMessage({'type': 'output2D', 'molData': genData, 'uid': invokerUid});
};
}
else if (msgType === 'gen3D')
{
var forceFieldName = msgData.forceField;
var useFFCalc = msgData.applyFFCalc; // whether use force field calculation or quick approach
var speed = msgData.speed; // speed using OB gen3D operation, can be 1-6 (the larger the faster) or string like 'fastest', 'med', etc.
var execFunc = function ()
{
var genData = generate3DMolData(molData, {
'useFFCalc': useFFCalc,
'forceFieldName': forceFieldName,
'speed': speed
});
// feedback
postMessage({'type': 'output3D', 'molData': genData, 'uid': invokerUid});
};
}
/*
if (runtimeInited)
execFunc();
else
pendingFuncs.push(execFunc);
*/
requestExec(execFunc);
}
};
function generate2DMolData(molData, options)
{
var result;
var obMol = _gen2DByObOperation(molData, options || {});
if (!obMol) // failed
{
// Kekule.raise(Kekule.$L('ErrorMsg.OpenBabel.FAIL_TO_GENERATE_3D_STRUCTURE'));
throw 'Fail to generate 2D structure';
}
else // success
{
// fetch back coords of obMol to mol
//var mol3D = AU.obObjToKekule(obMol);
var obConv = new (Module['ObConversionWrapper'])();
try
{
obConv.setOutFormat('chemical/x-mdl-molfile', 'mol');
if (obConv.writeToOutput(obMol))
result = obConv.getOutStr();
else
throw 'Fail to save generated 3D data';
}
finally
{
obConv['delete']();
}
obMol['delete']();
}
return result;
}
function generate3DMolData(molData, /*forceFieldName*/options)
{
var result;
//console.log('Module', Module);
var op = options || {};
var forceFieldName = op.forceFieldName;
var result = null;
var obMol;
if (op.useFFCalc)
{
var _obGen = new (Module['OB3DGenWrapper'])();
try
{
obMol = _obGen.generate3DStructureFromMolData(molData, forceFieldName || '');
}
finally
{
_obGen['delete']();
}
}
else
{
obMol = _gen3DByObOperation(molData, op);
}
if (!obMol) // failed
{
// Kekule.raise(Kekule.$L('ErrorMsg.OpenBabel.FAIL_TO_GENERATE_3D_STRUCTURE'));
throw 'Fail to generate 3D structure';
}
else // success
{
// fetch back coords of obMol to mol
//var mol3D = AU.obObjToKekule(obMol);
var obConv = new (Module['ObConversionWrapper'])();
try
{
obConv.setOutFormat('chemical/x-mdl-molfile', 'mol');
if (obConv.writeToOutput(obMol))
result = obConv.getOutStr();
else
throw 'Fail to save generated 3D data';
}
finally
{
obConv['delete']();
}
obMol['delete']();
}
return result;
};
function _genStructByObOperation(molData, dimension, options)
{
var speed = ('' + options.speed) || ''; // ensure speed is string
var conv = new (Module['ObConversionWrapper'])();
var result = null;
try
{
conv.setInFormat('', 'mol');
var mol = new Module['OBMol']();
try
{
conv.readString(mol, molData);
var opName = (dimension === 2)? 'Gen2D': 'Gen3D';
var generator = Module['OBOp'].FindType(opName);
if (generator)
{
generator.Do(mol, speed);
result = mol;
}
}
finally
{
if (!result)
mol['delete']();
}
}
finally
{
conv['delete']();
}
return result;
}
function _gen2DByObOperation(molData, options)
{
return _genStructByObOperation(molData, 2, options);
}
function _gen3DByObOperation(molData, options)
{
return _genStructByObOperation(molData, 3, options);
}
})(this);