Files
enviPy-bayer/static/js/ketcher2/script/chem/molfile/parseSGroup.js
2025-06-23 20:13:54 +02:00

302 lines
9.1 KiB
JavaScript

/****************************************************************************
* Copyright 2017 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
var Set = require('../../util/set');
var Vec2 = require('../../util/vec2');
var Struct = require('./../struct/index');
var utils = require('./utils');
function readKeyValuePairs(str, /* bool */ valueString) {
/* reader */
var ret = {};
var partition = utils.partitionLineFixed(str, 3, true);
var count = utils.parseDecimalInt(partition[0]);
for (var i = 0; i < count; ++i) {
/* eslint-disable no-mixed-operators*/
ret[utils.parseDecimalInt(partition[2 * i + 1]) - 1] =
valueString ? partition[2 * i + 2].trim() :
utils.parseDecimalInt(partition[2 * i + 2]);
/* eslint-enable no-mixed-operators*/
}
return ret;
}
function readKeyMultiValuePairs(str, /* bool */ valueString) {
/* reader */
var ret = [];
var partition = utils.partitionLineFixed(str, 3, true);
var count = utils.parseDecimalInt(partition[0]);
for (var i = 0; i < count; ++i) {
ret.push([
/* eslint-disable no-mixed-operators*/
utils.parseDecimalInt(partition[2 * i + 1]) - 1,
valueString ? partition[2 * i + 2].trim() : utils.parseDecimalInt(partition[2 * i + 2])
/* eslint-enable no-mixed-operators*/
]);
}
return ret;
}
function postLoadMul(sgroup, mol, atomMap) { // eslint-disable-line max-statements
sgroup.data.mul = sgroup.data.subscript - 0;
var atomReductionMap = {};
sgroup.atoms = Struct.SGroup.filterAtoms(sgroup.atoms, atomMap);
sgroup.patoms = Struct.SGroup.filterAtoms(sgroup.patoms, atomMap);
// mark repetitions for removal
for (var k = 1; k < sgroup.data.mul; ++k) {
for (var m = 0; m < sgroup.patoms.length; ++m) {
var raid = sgroup.atoms[k * sgroup.patoms.length + m]; // eslint-disable-line no-mixed-operators
if (raid < 0)
continue; // eslint-disable-line no-continue
if (sgroup.patoms[m] < 0)
throw new Error('parent atom missing');
atomReductionMap[raid] = sgroup.patoms[m]; // "merge" atom in parent
}
}
sgroup.patoms = Struct.SGroup.removeNegative(sgroup.patoms);
var patomsMap = identityMap(sgroup.patoms);
var bondsToRemove = [];
mol.bonds.each(function (bid, bond) {
var beginIn = bond.begin in atomReductionMap;
var endIn = bond.end in atomReductionMap;
// if both adjacent atoms of a bond are to be merged, remove it
/* eslint-disable no-mixed-operators*/
if (beginIn && endIn ||
beginIn && bond.end in patomsMap ||
endIn && bond.begin in patomsMap)
bondsToRemove.push(bid);
/* eslint-enable no-mixed-operators*/
// if just one atom is merged, modify the bond accordingly
else if (beginIn)
bond.begin = atomReductionMap[bond.begin];
else if (endIn)
bond.end = atomReductionMap[bond.end];
}, sgroup);
// apply removal lists
for (var b = 0; b < bondsToRemove.length; ++b)
mol.bonds.remove(bondsToRemove[b]);
for (var a in atomReductionMap) {
mol.atoms.remove(a);
atomMap[a] = -1;
}
sgroup.atoms = sgroup.patoms;
sgroup.patoms = null;
}
function postLoadSru(sgroup) {
sgroup.data.connectivity = (sgroup.data.connectivity || 'EU').trim().toLowerCase();
}
function postLoadSup(sgroup) {
sgroup.data.name = (sgroup.data.subscript || '').trim();
sgroup.data.subscript = '';
}
function postLoadGen(sgroup, mol, atomMap) { // eslint-disable-line no-unused-vars
}
function postLoadDat(sgroup, mol) {
if (!sgroup.data.absolute)
sgroup.pp = sgroup.pp.add(Struct.SGroup.getMassCentre(mol, sgroup.atoms));
}
function loadSGroup(mol, sg, atomMap) {
var postLoadMap = {
MUL: postLoadMul,
SRU: postLoadSru,
SUP: postLoadSup,
DAT: postLoadDat,
GEN: postLoadGen
};
// add the group to the molecule
sg.id = mol.sgroups.add(sg);
// apply type-specific post-processing
postLoadMap[sg.type](sg, mol, atomMap);
// mark atoms in the group as belonging to it
for (var s = 0; s < sg.atoms.length; ++s) {
if (mol.atoms.has(sg.atoms[s]))
Set.add(mol.atoms.get(sg.atoms[s]).sgs, sg.id);
}
if (sg.type == 'DAT')
mol.sGroupForest.insert(sg.id, -1, []);
else
mol.sGroupForest.insert(sg.id);
return sg.id;
}
function initSGroup(sGroups, propData) {
/* reader */
var kv = readKeyValuePairs(propData, true);
for (var key in kv) {
var type = kv[key];
if (!(type in Struct.SGroup.TYPES))
throw new Error('Unsupported S-group type');
var sg = new Struct.SGroup(type);
sg.number = key;
sGroups[key] = sg;
}
}
function applySGroupProp(sGroups, propName, propData, numeric, core) { // eslint-disable-line max-params
var kv = readKeyValuePairs(propData, !(numeric));
for (var key in kv)
// "core" properties are stored directly in an sgroup, not in sgroup.data
(core ? sGroups[key] : sGroups[key].data)[propName] = kv[key];
}
function applySGroupArrayProp(sGroups, propName, propData, shift) {
/* reader */
var sid = utils.parseDecimalInt(propData.slice(1, 4)) - 1;
var num = utils.parseDecimalInt(propData.slice(4, 8));
var part = toIntArray(utils.partitionLineFixed(propData.slice(8), 3, true));
if (part.length != num)
throw new Error('File format invalid');
if (shift) {
part = part.map(function (v) {
return v + shift;
});
}
sGroups[sid][propName] = sGroups[sid][propName].concat(part);
}
function applyDataSGroupName(sg, name) {
/* reader */
sg.data.fieldName = name;
}
function applyDataSGroupQuery(sg, query) {
/* reader */
sg.data.query = query;
}
function applyDataSGroupQueryOp(sg, queryOp) {
/* reader */
sg.data.queryOp = queryOp;
}
function applyDataSGroupDesc(sGroups, propData) {
/* reader */
var split = utils.partitionLine(propData, [4, 31, 2, 20, 2, 3], false);
var id = utils.parseDecimalInt(split[0]) - 1;
var fieldName = split[1].trim();
var fieldType = split[2].trim();
var units = split[3].trim();
var query = split[4].trim();
var queryOp = split[5].trim();
var sGroup = sGroups[id];
sGroup.data.fieldType = fieldType;
sGroup.data.fieldName = fieldName;
sGroup.data.units = units;
sGroup.data.query = query;
sGroup.data.queryOp = queryOp;
}
function applyDataSGroupInfo(sg, propData) { // eslint-disable-line max-statements
/* reader */
var split = utils.partitionLine(propData, [10/* x.x*/, 10/* y.y*/, 4/* eee*/, 1/* f*/, 1/* g*/, 1/* h*/, 3/* i */, 3/* jjj*/, 3/* kkk*/, 3/* ll*/, 2/* m*/, 3/* n*/, 2/* oo*/], false);
var x = parseFloat(split[0]);
var y = parseFloat(split[1]);
var attached = split[3].trim() == 'A';
var absolute = split[4].trim() == 'A';
var showUnits = split[5].trim() == 'U';
var nCharsToDisplay = split[7].trim();
nCharsToDisplay = nCharsToDisplay == 'ALL' ? -1 : utils.parseDecimalInt(nCharsToDisplay);
var tagChar = split[10].trim();
var daspPos = utils.parseDecimalInt(split[11].trim());
sg.pp = new Vec2(x, -y);
sg.data.attached = attached;
sg.data.absolute = absolute;
sg.data.showUnits = showUnits;
sg.data.nCharsToDisplay = nCharsToDisplay;
sg.data.tagChar = tagChar;
sg.data.daspPos = daspPos;
}
function applyDataSGroupInfoLine(sGroups, propData) {
/* reader */
var id = utils.parseDecimalInt(propData.substr(0, 4)) - 1;
var sg = sGroups[id];
applyDataSGroupInfo(sg, propData.substr(5));
}
function applyDataSGroupData(sg, data, finalize) {
/* reader */
sg.data.fieldValue = (sg.data.fieldValue || '') + data;
if (finalize) {
sg.data.fieldValue = trimRight(sg.data.fieldValue);
if (sg.data.fieldValue.startsWith('"') && sg.data.fieldValue.endsWith('"'))
sg.data.fieldValue = sg.data.fieldValue.substr(1, sg.data.fieldValue.length - 2);
}
}
function applyDataSGroupDataLine(sGroups, propData, finalize) {
/* reader */
var id = utils.parseDecimalInt(propData.substr(0, 5)) - 1;
var data = propData.substr(5);
var sg = sGroups[id];
applyDataSGroupData(sg, data, finalize);
}
// Utilities functions
function toIntArray(strArray) {
/* reader */
var ret = [];
for (var j = 0; j < strArray.length; ++j)
ret[j] = utils.parseDecimalInt(strArray[j]);
return ret;
}
function trimRight(str) {
return str.replace(/\s+$/, '');
}
function identityMap(array) {
var map = {};
for (var i = 0; i < array.length; ++i)
map[array[i]] = array[i];
return map;
}
module.exports = {
readKeyValuePairs: readKeyValuePairs,
readKeyMultiValuePairs: readKeyMultiValuePairs,
loadSGroup: loadSGroup,
initSGroup: initSGroup,
applySGroupProp: applySGroupProp,
applySGroupArrayProp: applySGroupArrayProp,
applyDataSGroupName: applyDataSGroupName,
applyDataSGroupQuery: applyDataSGroupQuery,
applyDataSGroupQueryOp: applyDataSGroupQueryOp,
applyDataSGroupDesc: applyDataSGroupDesc,
applyDataSGroupInfo: applyDataSGroupInfo,
applyDataSGroupData: applyDataSGroupData,
applyDataSGroupInfoLine: applyDataSGroupInfoLine,
applyDataSGroupDataLine: applyDataSGroupDataLine
};