forked from enviPath/enviPy
384 lines
10 KiB
JavaScript
384 lines
10 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 Box2Abs = require('../../util/box2abs');
|
|
var Set = require('../../util/set');
|
|
var Vec2 = require('../../util/vec2');
|
|
|
|
var Atom = require('./atom');
|
|
var Bond = require('./bond');
|
|
|
|
function SGroup(type) { // eslint-disable-line max-statements
|
|
console.assert(type && type in SGroup.TYPES, 'Invalid or unsupported s-group type');
|
|
|
|
this.type = type;
|
|
this.id = -1;
|
|
this.label = -1;
|
|
this.bracketBox = null;
|
|
this.bracketDir = new Vec2(1, 0);
|
|
this.areas = [];
|
|
|
|
this.highlight = false;
|
|
this.highlighting = null;
|
|
this.selected = false;
|
|
this.selectionPlate = null;
|
|
|
|
this.atoms = [];
|
|
this.patoms = [];
|
|
this.bonds = [];
|
|
this.xBonds = [];
|
|
this.neiAtoms = [];
|
|
this.pp = null;
|
|
this.data = {
|
|
mul: 1, // multiplication count for MUL group
|
|
connectivity: 'ht', // head-to-head, head-to-tail or either-unknown
|
|
name: '',
|
|
subscript: 'n',
|
|
|
|
// data s-group fields
|
|
attached: false,
|
|
absolute: true,
|
|
showUnits: false,
|
|
nCharsToDisplay: -1,
|
|
tagChar: '',
|
|
daspPos: 1,
|
|
fieldType: 'F',
|
|
fieldName: '',
|
|
fieldValue: '',
|
|
units: '',
|
|
query: '',
|
|
queryOp: ''
|
|
};
|
|
}
|
|
|
|
SGroup.TYPES = {
|
|
MUL: 1,
|
|
SRU: 2,
|
|
SUP: 3,
|
|
DAT: 4,
|
|
GEN: 5
|
|
};
|
|
|
|
// TODO: these methods should be overridden
|
|
// and should only accept valid attributes for each S-group type.
|
|
// The attributes should be accessed via these methods only and not directly through this.data.
|
|
// stub
|
|
SGroup.prototype.getAttr = function (attr) {
|
|
return this.data[attr];
|
|
};
|
|
|
|
// TODO: should be group-specific
|
|
SGroup.prototype.getAttrs = function () {
|
|
var attrs = {};
|
|
for (var attr in this.data) {
|
|
if (this.data.hasOwnProperty(attr))
|
|
attrs[attr] = this.data[attr];
|
|
}
|
|
return attrs;
|
|
};
|
|
|
|
// stub
|
|
SGroup.prototype.setAttr = function (attr, value) {
|
|
var oldValue = this.data[attr];
|
|
this.data[attr] = value;
|
|
return oldValue;
|
|
};
|
|
|
|
// stub
|
|
SGroup.prototype.checkAttr = function (attr, value) {
|
|
return this.data[attr] == value;
|
|
};
|
|
|
|
// SGroup.numberArrayToString = function (numbers, map) {
|
|
// var str = util.stringPadded(numbers.length, 3);
|
|
// for (var i = 0; i < numbers.length; ++i) {
|
|
// str += ' ' + util.stringPadded(map[numbers[i]], 3);
|
|
// }
|
|
// return str;
|
|
// };
|
|
|
|
SGroup.filterAtoms = function (atoms, map) {
|
|
var newAtoms = [];
|
|
for (var i = 0; i < atoms.length; ++i) {
|
|
var aid = atoms[i];
|
|
if (typeof (map[aid]) !== 'number')
|
|
newAtoms.push(aid);
|
|
else if (map[aid] >= 0)
|
|
newAtoms.push(map[aid]);
|
|
else
|
|
newAtoms.push(-1);
|
|
}
|
|
return newAtoms;
|
|
};
|
|
|
|
SGroup.removeNegative = function (atoms) {
|
|
var newAtoms = [];
|
|
for (var j = 0; j < atoms.length; ++j) {
|
|
if (atoms[j] >= 0)
|
|
newAtoms.push(atoms[j]);
|
|
}
|
|
return newAtoms;
|
|
};
|
|
|
|
SGroup.filter = function (mol, sg, atomMap) {
|
|
sg.atoms = SGroup.removeNegative(SGroup.filterAtoms(sg.atoms, atomMap));
|
|
};
|
|
|
|
SGroup.clone = function (sgroup, aidMap) {
|
|
var cp = new SGroup(sgroup.type);
|
|
|
|
for (var field in sgroup.data) // TODO: remove all non-primitive properties from 'data'
|
|
cp.data[field] = sgroup.data[field];
|
|
cp.atoms = sgroup.atoms.map(function (elem) {
|
|
return aidMap[elem];
|
|
});
|
|
cp.pp = sgroup.pp;
|
|
cp.bracketBox = sgroup.bracketBox;
|
|
cp.patoms = null;
|
|
cp.bonds = null;
|
|
cp.allAtoms = sgroup.allAtoms;
|
|
return cp;
|
|
};
|
|
|
|
SGroup.addAtom = function (sgroup, aid) {
|
|
sgroup.atoms.push(aid);
|
|
};
|
|
|
|
SGroup.removeAtom = function (sgroup, aid) {
|
|
for (var i = 0; i < sgroup.atoms.length; ++i) {
|
|
if (sgroup.atoms[i] === aid) {
|
|
sgroup.atoms.splice(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
console.error('The atom is not found in the given s-group');
|
|
};
|
|
|
|
SGroup.getCrossBonds = function (inBonds, xBonds, mol, parentAtomSet) {
|
|
mol.bonds.each(function (bid, bond) {
|
|
if (Set.contains(parentAtomSet, bond.begin) && Set.contains(parentAtomSet, bond.end)) {
|
|
if (inBonds != null)
|
|
inBonds.push(bid);
|
|
} else if (Set.contains(parentAtomSet, bond.begin) || Set.contains(parentAtomSet, bond.end)) {
|
|
if (xBonds != null)
|
|
xBonds.push(bid);
|
|
}
|
|
}, this);
|
|
};
|
|
|
|
SGroup.bracketPos = function (sg, mol, xbonds) { // eslint-disable-line max-statements
|
|
var atoms = sg.atoms;
|
|
if (!xbonds || xbonds.length !== 2) {
|
|
sg.bracketDir = new Vec2(1, 0);
|
|
} else {
|
|
var p1 = mol.bonds.get(xbonds[0]).getCenter(mol);
|
|
var p2 = mol.bonds.get(xbonds[1]).getCenter(mol);
|
|
sg.bracketDir = Vec2.diff(p2, p1).normalized();
|
|
}
|
|
var d = sg.bracketDir;
|
|
|
|
var bb = null;
|
|
var contentBoxes = [];
|
|
atoms.forEach(function (aid) {
|
|
var atom = mol.atoms.get(aid);
|
|
var pos = new Vec2(atom.pp);
|
|
var ext = new Vec2(0.05 * 3, 0.05 * 3);
|
|
var bba = new Box2Abs(pos, pos).extend(ext, ext);
|
|
contentBoxes.push(bba);
|
|
}, this);
|
|
contentBoxes.forEach(function (bba) {
|
|
var bbb = null;
|
|
[bba.p0.x, bba.p1.x].forEach(function (x) {
|
|
[bba.p0.y, bba.p1.y].forEach(function (y) {
|
|
var v = new Vec2(x, y);
|
|
var p = new Vec2(Vec2.dot(v, d), Vec2.dot(v, d.rotateSC(1, 0)));
|
|
bbb = (bbb === null) ? new Box2Abs(p, p) : bbb.include(p);
|
|
}, this);
|
|
}, this);
|
|
bb = (bb === null) ? bbb : Box2Abs.union(bb, bbb);
|
|
}, this);
|
|
var vext = new Vec2(0.2, 0.4);
|
|
if (bb !== null) bb = bb.extend(vext, vext);
|
|
sg.bracketBox = bb;
|
|
};
|
|
|
|
SGroup.getBracketParameters = function (mol, xbonds, atomSet, bb, d, n) { // eslint-disable-line max-params
|
|
function BracketParams(c, d, w, h) {
|
|
this.c = c;
|
|
this.d = d;
|
|
this.n = d.rotateSC(1, 0);
|
|
this.w = w;
|
|
this.h = h;
|
|
}
|
|
var brackets = [];
|
|
if (xbonds.length < 2) {
|
|
(function () {
|
|
d = d || new Vec2(1, 0);
|
|
n = n || d.rotateSC(1, 0);
|
|
var bracketWidth = Math.min(0.25, bb.sz().x * 0.3);
|
|
var cl = Vec2.lc2(d, bb.p0.x, n, 0.5 * (bb.p0.y + bb.p1.y));
|
|
var cr = Vec2.lc2(d, bb.p1.x, n, 0.5 * (bb.p0.y + bb.p1.y));
|
|
var bracketHeight = bb.sz().y;
|
|
|
|
brackets.push(new BracketParams(cl, d.negated(), bracketWidth, bracketHeight), new BracketParams(cr, d, bracketWidth, bracketHeight));
|
|
})();
|
|
} else if (xbonds.length === 2) {
|
|
(function () { // eslint-disable-line max-statements
|
|
var b1 = mol.bonds.get(xbonds[0]);
|
|
var b2 = mol.bonds.get(xbonds[1]);
|
|
var cl0 = b1.getCenter(mol);
|
|
var cr0 = b2.getCenter(mol);
|
|
var dr = Vec2.diff(cr0, cl0).normalized();
|
|
var dl = dr.negated();
|
|
|
|
var bracketWidth = 0.25;
|
|
var bracketHeight = 1.5;
|
|
brackets.push(new BracketParams(cl0.addScaled(dl, 0), dl, bracketWidth, bracketHeight),
|
|
new BracketParams(cr0.addScaled(dr, 0), dr, bracketWidth, bracketHeight));
|
|
})();
|
|
} else {
|
|
(function () {
|
|
for (var i = 0; i < xbonds.length; ++i) {
|
|
var b = mol.bonds.get(xbonds[i]);
|
|
var c = b.getCenter(mol);
|
|
var d = Set.contains(atomSet, b.begin) ? b.getDir(mol) : b.getDir(mol).negated();
|
|
brackets.push(new BracketParams(c, d, 0.2, 1.0));
|
|
}
|
|
})();
|
|
}
|
|
return brackets;
|
|
};
|
|
|
|
SGroup.getObjBBox = function (atoms, mol) {
|
|
console.assert(atoms.length != 0, 'Atom list is empty');
|
|
|
|
var a0 = mol.atoms.get(atoms[0]).pp;
|
|
var bb = new Box2Abs(a0, a0);
|
|
for (var i = 1; i < atoms.length; ++i) {
|
|
var aid = atoms[i];
|
|
var atom = mol.atoms.get(aid);
|
|
var p = atom.pp;
|
|
bb = bb.include(p);
|
|
}
|
|
return bb;
|
|
};
|
|
|
|
SGroup.getAtoms = function (mol, sg) {
|
|
/* shoud we use prototype? */
|
|
if (!sg.allAtoms)
|
|
return sg.atoms;
|
|
var atoms = [];
|
|
mol.atoms.each(function (aid) {
|
|
atoms.push(aid);
|
|
});
|
|
return atoms;
|
|
};
|
|
|
|
SGroup.getBonds = function (mol, sg) {
|
|
var atoms = SGroup.getAtoms(mol, sg);
|
|
var bonds = [];
|
|
mol.bonds.each(function (bid, bond) {
|
|
if (atoms.indexOf(bond.begin) >= 0 && atoms.indexOf(bond.end) >= 0) bonds.push(bid);
|
|
});
|
|
return bonds;
|
|
};
|
|
|
|
SGroup.prepareMulForSaving = function (sgroup, mol) { // eslint-disable-line max-statements
|
|
sgroup.atoms.sort((a, b) => a - b);
|
|
sgroup.atomSet = Set.fromList(sgroup.atoms);
|
|
sgroup.parentAtomSet = Set.clone(sgroup.atomSet);
|
|
var inBonds = [];
|
|
var xBonds = [];
|
|
|
|
mol.bonds.each(function (bid, bond) {
|
|
if (Set.contains(sgroup.parentAtomSet, bond.begin) && Set.contains(sgroup.parentAtomSet, bond.end))
|
|
inBonds.push(bid);
|
|
else if (Set.contains(sgroup.parentAtomSet, bond.begin) || Set.contains(sgroup.parentAtomSet, bond.end))
|
|
xBonds.push(bid);
|
|
}, sgroup);
|
|
if (xBonds.length !== 0 && xBonds.length !== 2) {
|
|
throw {
|
|
'id': sgroup.id,
|
|
'error-type': 'cross-bond-number',
|
|
'message': 'Unsupported cross-bonds number'
|
|
};
|
|
}
|
|
|
|
var xAtom1 = -1;
|
|
var xAtom2 = -1;
|
|
var crossBond = null;
|
|
if (xBonds.length === 2) {
|
|
var bond1 = mol.bonds.get(xBonds[0]);
|
|
xAtom1 = Set.contains(sgroup.parentAtomSet, bond1.begin) ? bond1.begin : bond1.end;
|
|
|
|
var bond2 = mol.bonds.get(xBonds[1]);
|
|
xAtom2 = Set.contains(sgroup.parentAtomSet, bond2.begin) ? bond2.begin : bond2.end;
|
|
crossBond = bond2;
|
|
}
|
|
|
|
var amap = null;
|
|
var tailAtom = xAtom2;
|
|
|
|
var newAtoms = [];
|
|
for (var j = 0; j < sgroup.data.mul - 1; j++) {
|
|
amap = {};
|
|
sgroup.atoms.forEach(function (aid) {
|
|
var atom = mol.atoms.get(aid);
|
|
var aid2 = mol.atoms.add(new Atom(atom));
|
|
newAtoms.push(aid2);
|
|
sgroup.atomSet[aid2] = 1;
|
|
amap[aid] = aid2;
|
|
});
|
|
inBonds.forEach(function (bid) {
|
|
var bond = mol.bonds.get(bid);
|
|
var newBond = new Bond(bond);
|
|
newBond.begin = amap[newBond.begin];
|
|
newBond.end = amap[newBond.end];
|
|
mol.bonds.add(newBond);
|
|
});
|
|
if (crossBond !== null) {
|
|
var newCrossBond = new Bond(crossBond);
|
|
newCrossBond.begin = tailAtom;
|
|
newCrossBond.end = amap[xAtom1];
|
|
mol.bonds.add(newCrossBond);
|
|
tailAtom = amap[xAtom2];
|
|
}
|
|
}
|
|
if (tailAtom >= 0) {
|
|
var xBond2 = mol.bonds.get(xBonds[1]);
|
|
if (xBond2.begin === xAtom2)
|
|
xBond2.begin = tailAtom;
|
|
else
|
|
xBond2.end = tailAtom;
|
|
}
|
|
sgroup.bonds = xBonds;
|
|
|
|
newAtoms.forEach(function (aid) {
|
|
mol.sGroupForest.getPathToRoot(sgroup.id).reverse().forEach(function (sgid) {
|
|
mol.atomAddToSGroup(sgid, aid);
|
|
});
|
|
});
|
|
};
|
|
|
|
SGroup.getMassCentre = function (mol, atoms) {
|
|
var c = new Vec2(); // mass centre
|
|
for (var i = 0; i < atoms.length; ++i)
|
|
c = c.addScaled(mol.atoms.get(atoms[i]).pp, 1.0 / atoms.length);
|
|
return c;
|
|
};
|
|
|
|
module.exports = SGroup;
|