forked from enviPath/enviPy
254 lines
7.0 KiB
JavaScript
254 lines
7.0 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 Vec2 = require('../util/vec2');
|
|
|
|
const SELECTION_DISTANCE_COEFFICIENT = 0.4;
|
|
|
|
function findClosestAtom(restruct, pos, skip, minDist) {
|
|
var closestAtom = null;
|
|
var maxMinDist = SELECTION_DISTANCE_COEFFICIENT;
|
|
var skipId = skip && skip.map === 'atoms' ? skip.id : null;
|
|
minDist = minDist || maxMinDist;
|
|
minDist = Math.min(minDist, maxMinDist);
|
|
restruct.atoms.each(function (aid, atom) {
|
|
if (aid !== skipId) {
|
|
var dist = Vec2.dist(pos, atom.a.pp);
|
|
if (dist < minDist) {
|
|
closestAtom = aid;
|
|
minDist = dist;
|
|
}
|
|
}
|
|
});
|
|
if (closestAtom !== null) {
|
|
return {
|
|
id: closestAtom,
|
|
dist: minDist
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function findClosestBond(restruct, pos, skip, minDist, scale) { // eslint-disable-line max-params
|
|
var closestBond = null;
|
|
var closestBondCenter = null;
|
|
var maxMinDist = SELECTION_DISTANCE_COEFFICIENT;
|
|
minDist = minDist || maxMinDist;
|
|
minDist = Math.min(minDist, maxMinDist);
|
|
var minCDist = minDist;
|
|
restruct.bonds.each(function (bid, bond) {
|
|
var p1 = restruct.atoms.get(bond.b.begin).a.pp,
|
|
p2 = restruct.atoms.get(bond.b.end).a.pp;
|
|
var mid = Vec2.lc2(p1, 0.5, p2, 0.5);
|
|
var cdist = Vec2.dist(pos, mid);
|
|
if (cdist < minCDist) {
|
|
minCDist = cdist;
|
|
closestBondCenter = bid;
|
|
}
|
|
});
|
|
restruct.bonds.each(function (bid, bond) {
|
|
var hb = restruct.molecule.halfBonds.get(bond.b.hb1);
|
|
var d = hb.dir;
|
|
var n = hb.norm;
|
|
var p1 = restruct.atoms.get(bond.b.begin).a.pp,
|
|
p2 = restruct.atoms.get(bond.b.end).a.pp;
|
|
|
|
var inStripe = Vec2.dot(pos.sub(p1), d) * Vec2.dot(pos.sub(p2), d) < 0;
|
|
if (inStripe) {
|
|
var dist = Math.abs(Vec2.dot(pos.sub(p1), n));
|
|
if (dist < minDist) {
|
|
closestBond = bid;
|
|
minDist = dist;
|
|
}
|
|
}
|
|
});
|
|
if (closestBondCenter !== null) {
|
|
return {
|
|
id: closestBondCenter,
|
|
dist: minCDist
|
|
};
|
|
}
|
|
if (closestBond !== null &&
|
|
minDist > SELECTION_DISTANCE_COEFFICIENT * scale) { // hack (ported from old code)
|
|
return {
|
|
id: closestBond,
|
|
dist: minDist
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function findClosestChiralFlag(restruct, pos) {
|
|
var minDist;
|
|
var ret = null;
|
|
// there is only one chiral flag, but we treat it as a "map" for convenience
|
|
restruct.chiralFlags.each(function (id, item) {
|
|
var p = item.pp;
|
|
if (Math.abs(pos.x - p.x) < 1.0) {
|
|
var dist = Math.abs(pos.y - p.y);
|
|
if (dist < 0.3 && (!ret || dist < minDist)) {
|
|
minDist = dist;
|
|
ret = { id: id, dist: minDist };
|
|
}
|
|
}
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
function findClosestDataSGroupData(restruct, pos) {
|
|
var minDist = null;
|
|
var ret = null;
|
|
restruct.sgroupData.each(function (id, item) {
|
|
if (item.sgroup.type !== 'DAT')
|
|
throw new Error('Data group expected');
|
|
if (item.sgroup.data.fieldName !== "MRV_IMPLICIT_H") {
|
|
var box = item.sgroup.dataArea;
|
|
var inBox = box.p0.y < pos.y && box.p1.y > pos.y && box.p0.x < pos.x && box.p1.x > pos.x;
|
|
var xDist = Math.min(Math.abs(box.p0.x - pos.x), Math.abs(box.p1.x - pos.x));
|
|
if (inBox && (ret == null || xDist < minDist)) {
|
|
ret = { id: id, dist: xDist };
|
|
minDist = xDist;
|
|
}
|
|
}
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
function findClosestFrag(restruct, pos, skip, minDist) {
|
|
minDist = Math.min(minDist || SELECTION_DISTANCE_COEFFICIENT,
|
|
SELECTION_DISTANCE_COEFFICIENT);
|
|
var ret = null;
|
|
var skipId = skip && skip.map === 'frags' ? skip.id : null;
|
|
restruct.frags.each(function (fid, frag) {
|
|
if (fid != skipId) {
|
|
var bb = frag.calcBBox(restruct, fid); // TODO any faster way to obtain bb?
|
|
if (bb.p0.y < pos.y && bb.p1.y > pos.y && bb.p0.x < pos.x && bb.p1.x > pos.x) {
|
|
var xDist = Math.min(Math.abs(bb.p0.x - pos.x), Math.abs(bb.p1.x - pos.x));
|
|
if (!ret || xDist < minDist) {
|
|
minDist = xDist;
|
|
ret = { id: fid, dist: minDist };
|
|
}
|
|
}
|
|
}
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
function findClosestRGroup(restruct, pos, skip, minDist) {
|
|
minDist = Math.min(minDist || SELECTION_DISTANCE_COEFFICIENT,
|
|
SELECTION_DISTANCE_COEFFICIENT);
|
|
var ret = null;
|
|
restruct.rgroups.each(function (rgid, rgroup) {
|
|
if (rgid != skip && rgroup.labelBox && rgroup.labelBox.contains(pos, 0.5)) {
|
|
var dist = Vec2.dist(rgroup.labelBox.centre(), pos);
|
|
if (!ret || dist < minDist) {
|
|
minDist = dist;
|
|
ret = { id: rgid, dist: minDist };
|
|
}
|
|
}
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
function findClosestRxnArrow(restruct, pos) {
|
|
var minDist;
|
|
var ret = null;
|
|
restruct.rxnArrows.each(function (id, arrow) {
|
|
var p = arrow.item.pp;
|
|
if (Math.abs(pos.x - p.x) < 1.0) {
|
|
var dist = Math.abs(pos.y - p.y);
|
|
if (dist < 0.3 && (!ret || dist < minDist)) {
|
|
minDist = dist;
|
|
ret = { id: id, dist: minDist };
|
|
}
|
|
}
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
function findClosestRxnPlus(restruct, pos) {
|
|
var minDist;
|
|
var ret = null;
|
|
restruct.rxnPluses.each(function (id, plus) {
|
|
var p = plus.item.pp;
|
|
var dist = Math.max(Math.abs(pos.x - p.x), Math.abs(pos.y - p.y));
|
|
if (dist < 0.3 && (!ret || dist < minDist)) {
|
|
minDist = dist;
|
|
ret = { id: id, dist: minDist };
|
|
}
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
function findClosestSGroup(restruct, pos) {
|
|
var ret = null;
|
|
var minDist = SELECTION_DISTANCE_COEFFICIENT;
|
|
restruct.molecule.sgroups.each(function (sgid, sg) {
|
|
var d = sg.bracketDir,
|
|
n = d.rotateSC(1, 0);
|
|
var pg = new Vec2(Vec2.dot(pos, d), Vec2.dot(pos, n));
|
|
for (var i = 0; i < sg.areas.length; ++i) {
|
|
var box = sg.areas[i];
|
|
var inBox = box.p0.y < pg.y && box.p1.y > pg.y && box.p0.x < pg.x && box.p1.x > pg.x;
|
|
var xDist = Math.min(Math.abs(box.p0.x - pg.x), Math.abs(box.p1.x - pg.x));
|
|
if (inBox && (ret == null || xDist < minDist)) {
|
|
ret = sgid;
|
|
minDist = xDist;
|
|
}
|
|
}
|
|
});
|
|
if (ret != null) {
|
|
return {
|
|
id: ret,
|
|
dist: minDist
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
var findMaps = {
|
|
atoms: findClosestAtom,
|
|
bonds: findClosestBond,
|
|
chiralFlags: findClosestChiralFlag,
|
|
sgroupData: findClosestDataSGroupData,
|
|
sgroups: findClosestSGroup,
|
|
rxnArrows: findClosestRxnArrow,
|
|
rxnPluses: findClosestRxnPlus,
|
|
frags: findClosestFrag,
|
|
rgroups: findClosestRGroup
|
|
};
|
|
|
|
function findClosestItem(restruct, pos, maps, skip, scale) { // eslint-disable-line max-params
|
|
maps = maps || Object.keys(findMaps);
|
|
return maps.reduce(function (res, mp) {
|
|
var minDist = res ? res.dist : null;
|
|
var item = findMaps[mp](restruct, pos, skip, minDist, scale);
|
|
if (item !== null && (res === null || item.dist < res.dist)) {
|
|
return {
|
|
map: mp,
|
|
id: item.id,
|
|
dist: item.dist
|
|
};
|
|
}
|
|
return res;
|
|
}, null);
|
|
}
|
|
|
|
module.exports = {
|
|
atom: findClosestAtom, // used in Actions
|
|
item: findClosestItem
|
|
};
|