forked from enviPath/enviPy
588 lines
21 KiB
JavaScript
588 lines
21 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 ReObject = require('./reobject');
|
|
|
|
var Struct = require('../../chem/struct');
|
|
var draw = require('../draw');
|
|
var Vec2 = require('../../util/vec2');
|
|
var util = require('../util');
|
|
var scale = require('../../util/scale');
|
|
|
|
function ReBond(/* chem.Bond*/bond) {
|
|
this.init('bond');
|
|
|
|
this.b = bond; // TODO rename b to item
|
|
this.doubleBondShift = 0;
|
|
}
|
|
|
|
ReBond.prototype = new ReObject();
|
|
ReBond.isSelectable = function () {
|
|
return true;
|
|
};
|
|
|
|
ReBond.prototype.drawHighlight = function (render) {
|
|
var ret = this.makeHighlightPlate(render);
|
|
render.ctab.addReObjectPath('highlighting', this.visel, ret);
|
|
return ret;
|
|
};
|
|
|
|
ReBond.prototype.makeHighlightPlate = function (render) {
|
|
var options = render.options;
|
|
bondRecalc(this, render.ctab, options);
|
|
var c = scale.obj2scaled(this.b.center, options);
|
|
return render.paper.circle(c.x, c.y, 0.8 * options.atomSelectionPlateRadius)
|
|
.attr(options.highlightStyle);
|
|
};
|
|
|
|
ReBond.prototype.makeSelectionPlate = function (restruct, paper, options) {
|
|
bondRecalc(this, restruct, options);
|
|
var c = scale.obj2scaled(this.b.center, options);
|
|
return paper.circle(c.x, c.y, 0.8 * options.atomSelectionPlateRadius)
|
|
.attr(options.selectionStyle);
|
|
};
|
|
|
|
ReBond.prototype.show = function (restruct, bid, options) { // eslint-disable-line max-statements
|
|
var render = restruct.render;
|
|
var struct = restruct.molecule;
|
|
var paper = render.paper;
|
|
var hb1 = struct.halfBonds.get(this.b.hb1),
|
|
hb2 = struct.halfBonds.get(this.b.hb2);
|
|
|
|
checkStereoBold(bid, this, restruct);
|
|
bondRecalc(this, restruct, options);
|
|
setDoubleBondShift(this, struct);
|
|
|
|
this.path = getBondPath(restruct, this, hb1, hb2);
|
|
|
|
this.rbb = util.relBox(this.path.getBBox());
|
|
restruct.addReObjectPath('data', this.visel, this.path, null, true);
|
|
var reactingCenter = {};
|
|
reactingCenter.path = getReactingCenterPath(render, this, hb1, hb2);
|
|
if (reactingCenter.path) {
|
|
reactingCenter.rbb = util.relBox(reactingCenter.path.getBBox());
|
|
restruct.addReObjectPath('data', this.visel, reactingCenter.path, null, true);
|
|
}
|
|
var topology = {};
|
|
topology.path = getTopologyMark(render, this, hb1, hb2);
|
|
if (topology.path) {
|
|
topology.rbb = util.relBox(topology.path.getBBox());
|
|
restruct.addReObjectPath('data', this.visel, topology.path, null, true);
|
|
}
|
|
this.setHighlight(this.highlight, render);
|
|
|
|
var ipath = null;
|
|
var bondIdxOff = options.subFontSize * 0.6;
|
|
if (options.showBondIds) {
|
|
ipath = getIdsPath(bid, paper, hb1, hb2, bondIdxOff, 0.5, 0.5, hb1.norm);
|
|
restruct.addReObjectPath('indices', this.visel, ipath);
|
|
}
|
|
if (options.showHalfBondIds) {
|
|
ipath = getIdsPath(this.b.hb1, paper, hb1, hb2, bondIdxOff, 0.8, 0.2, hb1.norm);
|
|
restruct.addReObjectPath('indices', this.visel, ipath);
|
|
ipath = getIdsPath(this.b.hb2, paper, hb1, hb2, bondIdxOff, 0.2, 0.8, hb2.norm);
|
|
restruct.addReObjectPath('indices', this.visel, ipath);
|
|
}
|
|
if (options.showLoopIds && !options.showBondIds) {
|
|
ipath = getIdsPath(hb1.loop, paper, hb1, hb2, bondIdxOff, 0.5, 0.5, hb2.norm);
|
|
restruct.addReObjectPath('indices', this.visel, ipath);
|
|
ipath = getIdsPath(hb2.loop, paper, hb1, hb2, bondIdxOff, 0.5, 0.5, hb1.norm);
|
|
restruct.addReObjectPath('indices', this.visel, ipath);
|
|
}
|
|
};
|
|
|
|
function findIncomingStereoUpBond(atom, bid0, includeBoldStereoBond, restruct) {
|
|
return atom.neighbors.findIndex(function (hbid) {
|
|
var hb = restruct.molecule.halfBonds.get(hbid);
|
|
var bid = hb.bid;
|
|
if (bid === bid0)
|
|
return false;
|
|
var neibond = restruct.bonds.get(bid);
|
|
if (neibond.b.type === Struct.Bond.PATTERN.TYPE.SINGLE && neibond.b.stereo === Struct.Bond.PATTERN.STEREO.UP)
|
|
return neibond.b.end === hb.begin || (neibond.boldStereo && includeBoldStereoBond);
|
|
return !!(neibond.b.type === Struct.Bond.PATTERN.TYPE.DOUBLE && neibond.b.stereo === Struct.Bond.PATTERN.STEREO.NONE && includeBoldStereoBond && neibond.boldStereo);
|
|
});
|
|
}
|
|
|
|
function findIncomingUpBonds(bid0, bond, restruct) {
|
|
var halfbonds = [bond.b.begin, bond.b.end].map(function (aid) {
|
|
var atom = restruct.molecule.atoms.get(aid);
|
|
var pos = findIncomingStereoUpBond(atom, bid0, true, restruct);
|
|
return pos < 0 ? -1 : atom.neighbors[pos];
|
|
}, this);
|
|
console.assert(halfbonds.length === 2);
|
|
bond.neihbid1 = restruct.atoms.get(bond.b.begin).showLabel ? -1 : halfbonds[0];
|
|
bond.neihbid2 = restruct.atoms.get(bond.b.end).showLabel ? -1 : halfbonds[1];
|
|
}
|
|
|
|
function checkStereoBold(bid0, bond, restruct) {
|
|
var halfbonds = [bond.b.begin, bond.b.end].map(function (aid) {
|
|
var atom = restruct.molecule.atoms.get(aid);
|
|
var pos = findIncomingStereoUpBond(atom, bid0, false, restruct);
|
|
return pos < 0 ? -1 : atom.neighbors[pos];
|
|
}, restruct);
|
|
console.assert(halfbonds.length === 2);
|
|
bond.boldStereo = halfbonds[0] >= 0 && halfbonds[1] >= 0;
|
|
}
|
|
|
|
function getBondPath(restruct, bond, hb1, hb2) {
|
|
var path = null;
|
|
var render = restruct.render;
|
|
var struct = restruct.molecule;
|
|
var shiftA = !restruct.atoms.get(hb1.begin).showLabel;
|
|
var shiftB = !restruct.atoms.get(hb2.begin).showLabel;
|
|
|
|
switch (bond.b.type) {
|
|
case Struct.Bond.PATTERN.TYPE.SINGLE:
|
|
switch (bond.b.stereo) {
|
|
case Struct.Bond.PATTERN.STEREO.UP:
|
|
findIncomingUpBonds(hb1.bid, bond, restruct);
|
|
if (bond.boldStereo && bond.neihbid1 >= 0 && bond.neihbid2 >= 0)
|
|
path = getBondSingleStereoBoldPath(render, hb1, hb2, bond, struct);
|
|
else
|
|
path = getBondSingleUpPath(render, hb1, hb2, bond, struct);
|
|
break;
|
|
case Struct.Bond.PATTERN.STEREO.DOWN:
|
|
path = getBondSingleDownPath(render, hb1, hb2);
|
|
break;
|
|
case Struct.Bond.PATTERN.STEREO.EITHER:
|
|
path = getBondSingleEitherPath(render, hb1, hb2);
|
|
break;
|
|
default:
|
|
path = draw.bondSingle(render.paper, hb1, hb2, render.options);
|
|
break;
|
|
}
|
|
break;
|
|
case Struct.Bond.PATTERN.TYPE.DOUBLE:
|
|
findIncomingUpBonds(hb1.bid, bond, restruct);
|
|
if (bond.b.stereo === Struct.Bond.PATTERN.STEREO.NONE && bond.boldStereo &&
|
|
bond.neihbid1 >= 0 && bond.neihbid2 >= 0)
|
|
path = getBondDoubleStereoBoldPath(render, hb1, hb2, bond, struct, shiftA, shiftB);
|
|
else
|
|
path = getBondDoublePath(render, hb1, hb2, bond, shiftA, shiftB);
|
|
break;
|
|
case Struct.Bond.PATTERN.TYPE.TRIPLE:
|
|
path = draw.bondTriple(render.paper, hb1, hb2, render.options);
|
|
break;
|
|
case Struct.Bond.PATTERN.TYPE.AROMATIC:
|
|
var inAromaticLoop = (hb1.loop >= 0 && struct.loops.get(hb1.loop).aromatic) ||
|
|
(hb2.loop >= 0 && struct.loops.get(hb2.loop).aromatic);
|
|
path = inAromaticLoop ? draw.bondSingle(render.paper, hb1, hb2, render.options) :
|
|
getBondAromaticPath(render, hb1, hb2, bond, shiftA, shiftB);
|
|
break;
|
|
case Struct.Bond.PATTERN.TYPE.SINGLE_OR_DOUBLE:
|
|
path = getSingleOrDoublePath(render, hb1, hb2);
|
|
break;
|
|
case Struct.Bond.PATTERN.TYPE.SINGLE_OR_AROMATIC:
|
|
path = getBondAromaticPath(render, hb1, hb2, bond, shiftA, shiftB);
|
|
break;
|
|
case Struct.Bond.PATTERN.TYPE.DOUBLE_OR_AROMATIC:
|
|
path = getBondAromaticPath(render, hb1, hb2, bond, shiftA, shiftB);
|
|
break;
|
|
case Struct.Bond.PATTERN.TYPE.ANY:
|
|
path = draw.bondAny(render.paper, hb1, hb2, render.options);
|
|
break;
|
|
default:
|
|
throw new Error('Bond type ' + bond.b.type + ' not supported');
|
|
}
|
|
return path;
|
|
}
|
|
|
|
/* Get Path */
|
|
function getBondSingleUpPath(render, hb1, hb2, bond, struct) { // eslint-disable-line max-params
|
|
var a = hb1.p,
|
|
b = hb2.p,
|
|
n = hb1.norm;
|
|
var options = render.options;
|
|
var bsp = 0.7 * options.stereoBond;
|
|
var b2 = b.addScaled(n, bsp);
|
|
var b3 = b.addScaled(n, -bsp);
|
|
if (bond.neihbid2 >= 0) { // if the end is shared with another up-bond heading this way
|
|
var coords = stereoUpBondGetCoordinates(hb2, bond.neihbid2, options.stereoBond, struct);
|
|
b2 = coords[0];
|
|
b3 = coords[1];
|
|
}
|
|
return draw.bondSingleUp(render.paper, a, b2, b3, options);
|
|
}
|
|
|
|
function getBondSingleStereoBoldPath(render, hb1, hb2, bond, struct) { // eslint-disable-line max-params
|
|
var options = render.options;
|
|
var coords1 = stereoUpBondGetCoordinates(hb1, bond.neihbid1, options.stereoBond, struct);
|
|
var coords2 = stereoUpBondGetCoordinates(hb2, bond.neihbid2, options.stereoBond, struct);
|
|
var a1 = coords1[0];
|
|
var a2 = coords1[1];
|
|
var a3 = coords2[0];
|
|
var a4 = coords2[1];
|
|
return draw.bondSingleStereoBold(render.paper, a1, a2, a3, a4, options);
|
|
}
|
|
|
|
function getBondDoubleStereoBoldPath(render, hb1, hb2, bond, struct, shiftA, shiftB) { // eslint-disable-line max-params
|
|
var a = hb1.p,
|
|
b = hb2.p,
|
|
n = hb1.norm,
|
|
shift = bond.doubleBondShift;
|
|
var bsp = 1.5 * render.options.stereoBond;
|
|
var b1 = a.addScaled(n, bsp * shift);
|
|
var b2 = b.addScaled(n, bsp * shift);
|
|
if (shift > 0) {
|
|
if (shiftA)
|
|
b1 = b1.addScaled(hb1.dir, bsp * getBondLineShift(hb1.rightCos, hb1.rightSin));
|
|
if (shiftB)
|
|
b2 = b2.addScaled(hb1.dir, -bsp * getBondLineShift(hb2.leftCos, hb2.leftSin));
|
|
} else if (shift < 0) {
|
|
if (shiftA)
|
|
b1 = b1.addScaled(hb1.dir, bsp * getBondLineShift(hb1.leftCos, hb1.leftSin));
|
|
if (shiftB)
|
|
b2 = b2.addScaled(hb1.dir, -bsp * getBondLineShift(hb2.rightCos, hb2.rightSin));
|
|
}
|
|
var sgBondPath = getBondSingleStereoBoldPath(render, hb1, hb2, bond, struct);
|
|
return draw.bondDoubleStereoBold(render.paper, sgBondPath, b1, b2, render.options);
|
|
}
|
|
|
|
function getBondLineShift(cos, sin) {
|
|
if (sin < 0 || Math.abs(cos) > 0.9)
|
|
return 0;
|
|
return sin / (1 - cos);
|
|
}
|
|
|
|
function stereoUpBondGetCoordinates(hb, neihbid, bondSpace, struct) {
|
|
var neihb = struct.halfBonds.get(neihbid);
|
|
var cos = Vec2.dot(hb.dir, neihb.dir);
|
|
var sin = Vec2.cross(hb.dir, neihb.dir);
|
|
var cosHalf = Math.sqrt(0.5 * (1 - cos));
|
|
var biss = neihb.dir.rotateSC((sin >= 0 ? -1 : 1) * cosHalf, Math.sqrt(0.5 * (1 + cos)));
|
|
|
|
var denomAdd = 0.3;
|
|
var scale = 0.7;
|
|
var a1 = hb.p.addScaled(biss, scale * bondSpace / (cosHalf + denomAdd));
|
|
var a2 = hb.p.addScaled(biss.negated(), scale * bondSpace / (cosHalf + denomAdd));
|
|
return sin > 0 ? [a1, a2] : [a2, a1];
|
|
}
|
|
|
|
function getBondSingleDownPath(render, hb1, hb2) {
|
|
var a = hb1.p,
|
|
b = hb2.p;
|
|
var options = render.options;
|
|
var d = b.sub(a);
|
|
var len = d.length() + 0.2;
|
|
d = d.normalized();
|
|
var interval = 1.2 * options.lineWidth;
|
|
var nlines = Math.max(Math.floor((len - options.lineWidth) /
|
|
(options.lineWidth + interval)), 0) + 2;
|
|
var step = len / (nlines - 1);
|
|
return draw.bondSingleDown(render.paper, hb1, d, nlines, step, options);
|
|
}
|
|
|
|
function getBondSingleEitherPath(render, hb1, hb2) {
|
|
var a = hb1.p,
|
|
b = hb2.p;
|
|
var options = render.options;
|
|
var d = b.sub(a);
|
|
var len = d.length();
|
|
d = d.normalized();
|
|
var interval = 0.6 * options.lineWidth;
|
|
var nlines = Math.max(Math.floor((len - options.lineWidth) /
|
|
(options.lineWidth + interval)), 0) + 2;
|
|
var step = len / (nlines - 0.5);
|
|
return draw.bondSingleEither(render.paper, hb1, d, nlines, step, options);
|
|
}
|
|
|
|
function getBondDoublePath(render, hb1, hb2, bond, shiftA, shiftB) { // eslint-disable-line max-params, max-statements
|
|
var cisTrans = bond.b.stereo === Struct.Bond.PATTERN.STEREO.CIS_TRANS;
|
|
|
|
var a = hb1.p,
|
|
b = hb2.p,
|
|
n = hb1.norm,
|
|
shift = cisTrans ? 0 : bond.doubleBondShift;
|
|
var options = render.options;
|
|
var bsp = options.bondSpace / 2;
|
|
var s1 = bsp + (shift * bsp),
|
|
s2 = -bsp + (shift * bsp);
|
|
var a1 = a.addScaled(n, s1);
|
|
var b1 = b.addScaled(n, s1);
|
|
var a2 = a.addScaled(n, s2);
|
|
var b2 = b.addScaled(n, s2);
|
|
|
|
if (shift > 0) {
|
|
if (shiftA) {
|
|
a1 = a1.addScaled(hb1.dir, options.bondSpace *
|
|
getBondLineShift(hb1.rightCos, hb1.rightSin));
|
|
}
|
|
if (shiftB) {
|
|
b1 = b1.addScaled(hb1.dir, -options.bondSpace *
|
|
getBondLineShift(hb2.leftCos, hb2.leftSin));
|
|
}
|
|
} else if (shift < 0) {
|
|
if (shiftA) {
|
|
a2 = a2.addScaled(hb1.dir, options.bondSpace *
|
|
getBondLineShift(hb1.leftCos, hb1.leftSin));
|
|
}
|
|
if (shiftB) {
|
|
b2 = b2.addScaled(hb1.dir, -options.bondSpace *
|
|
getBondLineShift(hb2.rightCos, hb2.rightSin));
|
|
}
|
|
}
|
|
return draw.bondDouble(render.paper, a1, a2, b1, b2, cisTrans, options);
|
|
}
|
|
|
|
function getSingleOrDoublePath(render, hb1, hb2) {
|
|
var a = hb1.p,
|
|
b = hb2.p;
|
|
var options = render.options;
|
|
|
|
var nSect = (Vec2.dist(a, b) / (options.bondSpace + options.lineWidth)).toFixed() - 0;
|
|
if (!(nSect & 1))
|
|
nSect += 1;
|
|
return draw.bondSingleOrDouble(render.paper, hb1, hb2, nSect, options);
|
|
}
|
|
|
|
function getBondAromaticPath(render, hb1, hb2, bond, shiftA, shiftB) { // eslint-disable-line max-params
|
|
var dashdotPattern = [0.125, 0.125, 0.005, 0.125];
|
|
var mark = null,
|
|
dash = null;
|
|
var options = render.options;
|
|
var bondShift = bond.doubleBondShift;
|
|
|
|
if (bond.b.type == Struct.Bond.PATTERN.TYPE.SINGLE_OR_AROMATIC) {
|
|
mark = bondShift > 0 ? 1 : 2;
|
|
dash = dashdotPattern.map(function (v) {
|
|
return v * options.scale;
|
|
});
|
|
}
|
|
if (bond.b.type == Struct.Bond.PATTERN.TYPE.DOUBLE_OR_AROMATIC) {
|
|
mark = 3;
|
|
dash = dashdotPattern.map(function (v) {
|
|
return v * options.scale;
|
|
});
|
|
}
|
|
var paths = getAromaticBondPaths(hb1, hb2, bondShift, shiftA, shiftB, options.bondSpace, mark, dash);
|
|
return draw.bondAromatic(render.paper, paths, bondShift, options);
|
|
}
|
|
|
|
function getAromaticBondPaths(hb1, hb2, shift, shiftA, shiftB, bondSpace, mask, dash) { // eslint-disable-line max-params, max-statements
|
|
var a = hb1.p,
|
|
b = hb2.p,
|
|
n = hb1.norm;
|
|
var bsp = bondSpace / 2;
|
|
var s1 = bsp + (shift * bsp),
|
|
s2 = -bsp + (shift * bsp);
|
|
var a2 = a.addScaled(n, s1);
|
|
var b2 = b.addScaled(n, s1);
|
|
var a3 = a.addScaled(n, s2);
|
|
var b3 = b.addScaled(n, s2);
|
|
if (shift > 0) {
|
|
if (shiftA) {
|
|
a2 = a2.addScaled(hb1.dir, bondSpace *
|
|
getBondLineShift(hb1.rightCos, hb1.rightSin));
|
|
}
|
|
if (shiftB) {
|
|
b2 = b2.addScaled(hb1.dir, -bondSpace *
|
|
getBondLineShift(hb2.leftCos, hb2.leftSin));
|
|
}
|
|
} else if (shift < 0) {
|
|
if (shiftA) {
|
|
a3 = a3.addScaled(hb1.dir, bondSpace *
|
|
getBondLineShift(hb1.leftCos, hb1.leftSin));
|
|
}
|
|
if (shiftB) {
|
|
b3 = b3.addScaled(hb1.dir, -bondSpace *
|
|
getBondLineShift(hb2.rightCos, hb2.rightSin));
|
|
}
|
|
}
|
|
return draw.aromaticBondPaths(a2, a3, b2, b3, mask, dash);
|
|
}
|
|
|
|
function getReactingCenterPath(render, bond, hb1, hb2) { // eslint-disable-line max-statements
|
|
var a = hb1.p,
|
|
b = hb2.p;
|
|
var c = b.add(a).scaled(0.5);
|
|
var d = b.sub(a).normalized();
|
|
var n = d.rotateSC(1, 0);
|
|
|
|
var p = [];
|
|
|
|
var lw = render.options.lineWidth,
|
|
bs = render.options.bondSpace / 2;
|
|
var alongIntRc = lw, // half interval along for CENTER
|
|
alongIntMadeBroken = 2 * lw, // half interval between along for MADE_OR_BROKEN
|
|
alongSz = 1.5 * bs, // half size along for CENTER
|
|
acrossInt = 1.5 * bs, // half interval across for CENTER
|
|
acrossSz = 3.0 * bs, // half size across for all
|
|
tiltTan = 0.2; // tangent of the tilt angle
|
|
|
|
switch (bond.b.reactingCenterStatus) {
|
|
case Struct.Bond.PATTERN.REACTING_CENTER.NOT_CENTER: // X
|
|
p.push(c.addScaled(n, acrossSz).addScaled(d, tiltTan * acrossSz));
|
|
p.push(c.addScaled(n, -acrossSz).addScaled(d, -tiltTan * acrossSz));
|
|
p.push(c.addScaled(n, acrossSz).addScaled(d, -tiltTan * acrossSz));
|
|
p.push(c.addScaled(n, -acrossSz).addScaled(d, tiltTan * acrossSz));
|
|
break;
|
|
case Struct.Bond.PATTERN.REACTING_CENTER.CENTER: // #
|
|
p.push(c.addScaled(n, acrossSz).addScaled(d, tiltTan * acrossSz).addScaled(d, alongIntRc));
|
|
p.push(c.addScaled(n, -acrossSz).addScaled(d, -tiltTan * acrossSz).addScaled(d, alongIntRc));
|
|
p.push(c.addScaled(n, acrossSz).addScaled(d, tiltTan * acrossSz).addScaled(d, -alongIntRc));
|
|
p.push(c.addScaled(n, -acrossSz).addScaled(d, -tiltTan * acrossSz).addScaled(d, -alongIntRc));
|
|
p.push(c.addScaled(d, alongSz).addScaled(n, acrossInt));
|
|
p.push(c.addScaled(d, -alongSz).addScaled(n, acrossInt));
|
|
p.push(c.addScaled(d, alongSz).addScaled(n, -acrossInt));
|
|
p.push(c.addScaled(d, -alongSz).addScaled(n, -acrossInt));
|
|
break;
|
|
// case Bond.PATTERN.REACTING_CENTER.UNCHANGED: // o
|
|
// //draw a circle
|
|
// break;
|
|
case Struct.Bond.PATTERN.REACTING_CENTER.MADE_OR_BROKEN:
|
|
p.push(c.addScaled(n, acrossSz).addScaled(d, alongIntMadeBroken));
|
|
p.push(c.addScaled(n, -acrossSz).addScaled(d, alongIntMadeBroken));
|
|
p.push(c.addScaled(n, acrossSz).addScaled(d, -alongIntMadeBroken));
|
|
p.push(c.addScaled(n, -acrossSz).addScaled(d, -alongIntMadeBroken));
|
|
break;
|
|
case Struct.Bond.PATTERN.REACTING_CENTER.ORDER_CHANGED:
|
|
p.push(c.addScaled(n, acrossSz));
|
|
p.push(c.addScaled(n, -acrossSz));
|
|
break;
|
|
case Struct.Bond.PATTERN.REACTING_CENTER.MADE_OR_BROKEN_AND_CHANGED:
|
|
p.push(c.addScaled(n, acrossSz).addScaled(d, alongIntMadeBroken));
|
|
p.push(c.addScaled(n, -acrossSz).addScaled(d, alongIntMadeBroken));
|
|
p.push(c.addScaled(n, acrossSz).addScaled(d, -alongIntMadeBroken));
|
|
p.push(c.addScaled(n, -acrossSz).addScaled(d, -alongIntMadeBroken));
|
|
p.push(c.addScaled(n, acrossSz));
|
|
p.push(c.addScaled(n, -acrossSz));
|
|
break;
|
|
default:
|
|
return null;
|
|
}
|
|
return draw.reactingCenter(render.paper, p, render.options);
|
|
}
|
|
|
|
function getTopologyMark(render, bond, hb1, hb2) { // eslint-disable-line max-statements
|
|
var options = render.options;
|
|
var mark = null;
|
|
|
|
if (bond.b.topology == Struct.Bond.PATTERN.TOPOLOGY.RING)
|
|
mark = 'rng';
|
|
else if (bond.b.topology == Struct.Bond.PATTERN.TOPOLOGY.CHAIN)
|
|
mark = 'chn';
|
|
else
|
|
return null;
|
|
|
|
var a = hb1.p,
|
|
b = hb2.p;
|
|
var c = b.add(a).scaled(0.5);
|
|
var d = b.sub(a).normalized();
|
|
var n = d.rotateSC(1, 0);
|
|
var fixed = options.lineWidth;
|
|
if (bond.doubleBondShift > 0)
|
|
n = n.scaled(-bond.doubleBondShift);
|
|
else if (bond.doubleBondShift == 0)
|
|
fixed += options.bondSpace / 2;
|
|
|
|
var s = new Vec2(2, 1).scaled(options.bondSpace);
|
|
if (bond.b.type == Struct.Bond.PATTERN.TYPE.TRIPLE)
|
|
fixed += options.bondSpace;
|
|
var p = c.add(new Vec2(n.x * (s.x + fixed), n.y * (s.y + fixed)));
|
|
|
|
return draw.topologyMark(render.paper, p, mark, options);
|
|
}
|
|
|
|
function getIdsPath(bid, paper, hb1, hb2, bondIdxOff, param1, param2, norm) { // eslint-disable-line max-params
|
|
var pb = Vec2.lc(hb1.p, param1, hb2.p, param2, norm, bondIdxOff);
|
|
var ipath = paper.text(pb.x, pb.y, bid.toString());
|
|
var irbb = util.relBox(ipath.getBBox());
|
|
draw.recenterText(ipath, irbb);
|
|
return ipath;
|
|
}
|
|
/* ----- */
|
|
|
|
function setDoubleBondShift(bond, struct) {
|
|
var loop1, loop2;
|
|
loop1 = struct.halfBonds.get(bond.b.hb1).loop;
|
|
loop2 = struct.halfBonds.get(bond.b.hb2).loop;
|
|
if (loop1 >= 0 && loop2 >= 0) {
|
|
var d1 = struct.loops.get(loop1).dblBonds;
|
|
var d2 = struct.loops.get(loop2).dblBonds;
|
|
var n1 = struct.loops.get(loop1).hbs.length;
|
|
var n2 = struct.loops.get(loop2).hbs.length;
|
|
bond.doubleBondShift = selectDoubleBondShift(n1, n2, d1, d2);
|
|
} else if (loop1 >= 0) {
|
|
bond.doubleBondShift = -1;
|
|
} else if (loop2 >= 0) {
|
|
bond.doubleBondShift = 1;
|
|
} else {
|
|
bond.doubleBondShift = selectDoubleBondShiftChain(struct, bond);
|
|
}
|
|
}
|
|
|
|
function bondRecalc(bond, restruct, options) {
|
|
var render = restruct.render;
|
|
var atom1 = restruct.atoms.get(bond.b.begin);
|
|
var atom2 = restruct.atoms.get(bond.b.end);
|
|
var p1 = scale.obj2scaled(atom1.a.pp, render.options);
|
|
var p2 = scale.obj2scaled(atom2.a.pp, render.options);
|
|
var hb1 = restruct.molecule.halfBonds.get(bond.b.hb1);
|
|
var hb2 = restruct.molecule.halfBonds.get(bond.b.hb2);
|
|
hb1.p = shiftBondEnd(atom1, p1, hb1.dir, 2 * options.lineWidth);
|
|
hb2.p = shiftBondEnd(atom2, p2, hb2.dir, 2 * options.lineWidth);
|
|
bond.b.center = Vec2.lc2(atom1.a.pp, 0.5, atom2.a.pp, 0.5);
|
|
bond.b.len = Vec2.dist(p1, p2);
|
|
bond.b.sb = options.lineWidth * 5;
|
|
/* eslint-disable no-mixed-operators*/
|
|
bond.b.sa = Math.max(bond.b.sb, bond.b.len / 2 - options.lineWidth * 2);
|
|
/* eslint-enable no-mixed-operators*/
|
|
bond.b.angle = Math.atan2(hb1.dir.y, hb1.dir.x) * 180 / Math.PI;
|
|
}
|
|
|
|
function shiftBondEnd(atom, pos0, dir, margin) {
|
|
var t = 0;
|
|
var visel = atom.visel;
|
|
for (var k = 0; k < visel.exts.length; ++k) {
|
|
var box = visel.exts[k].translate(pos0);
|
|
t = Math.max(t, Vec2.shiftRayBox(pos0, dir, box));
|
|
}
|
|
if (t > 0)
|
|
pos0 = pos0.addScaled(dir, t + margin);
|
|
return pos0;
|
|
}
|
|
|
|
function selectDoubleBondShift(n1, n2, d1, d2) {
|
|
if (n1 == 6 && n2 != 6 && (d1 > 1 || d2 == 1))
|
|
return -1;
|
|
if (n2 == 6 && n1 != 6 && (d2 > 1 || d1 == 1))
|
|
return 1;
|
|
if (n2 * d1 > n1 * d2)
|
|
return -1;
|
|
if (n2 * d1 < n1 * d2)
|
|
return 1;
|
|
if (n2 > n1)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
|
|
function selectDoubleBondShiftChain(struct, bond) {
|
|
var hb1 = struct.halfBonds.get(bond.b.hb1);
|
|
var hb2 = struct.halfBonds.get(bond.b.hb2);
|
|
var nLeft = (hb1.leftSin > 0.3 ? 1 : 0) + (hb2.rightSin > 0.3 ? 1 : 0);
|
|
var nRight = (hb2.leftSin > 0.3 ? 1 : 0) + (hb1.rightSin > 0.3 ? 1 : 0);
|
|
if (nLeft > nRight)
|
|
return -1;
|
|
if (nLeft < nRight)
|
|
return 1;
|
|
if ((hb1.leftSin > 0.3 ? 1 : 0) + (hb1.rightSin > 0.3 ? 1 : 0) == 1)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
module.exports = ReBond;
|