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

296 lines
8.8 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 util = require('./util');
var Vec2 = require('../util/vec2');
var Raphael = require('../raphael-ext');
var tfx = util.tfx;
function arrow(paper, a, b, options) {
var width = 5,
length = 7;
return paper.path('M{0},{1}L{2},{3}L{4},{5}M{2},{3}L{4},{6}', tfx(a.x), tfx(a.y), tfx(b.x), tfx(b.y), tfx(b.x - length), tfx(b.y - width), tfx(b.y + width))
.attr(options.lineattr);
}
function plus(paper, c, options) {
var s = options.scale / 5;
return paper.path('M{0},{4}L{0},{5}M{2},{1}L{3},{1}', tfx(c.x), tfx(c.y), tfx(c.x - s), tfx(c.x + s), tfx(c.y - s), tfx(c.y + s))
.attr(options.lineattr);
}
function bondSingle(paper, hb1, hb2, options) {
var a = hb1.p,
b = hb2.p;
return paper.path(makeStroke(a, b))
.attr(options.lineattr);
}
function bondSingleUp(paper, a, b2, b3, options) { // eslint-disable-line max-params
return paper.path('M{0},{1}L{2},{3}L{4},{5}Z', tfx(a.x), tfx(a.y), tfx(b2.x), tfx(b2.y), tfx(b3.x), tfx(b3.y))
.attr(options.lineattr).attr({ fill: '#000' });
}
function bondSingleStereoBold(paper, a1, a2, a3, a4, options) { // eslint-disable-line max-params
return paper.path('M{0},{1}L{2},{3}L{4},{5}L{6},{7}Z',
tfx(a1.x), tfx(a1.y), tfx(a2.x), tfx(a2.y), tfx(a3.x), tfx(a3.y), tfx(a4.x), tfx(a4.y))
.attr(options.lineattr).attr({
stroke: '#000',
fill: '#000'
});
}
function bondDoubleStereoBold(paper, sgBondPath, b1, b2, options) { // eslint-disable-line max-params
return paper.set([sgBondPath, paper.path('M{0},{1}L{2},{3}', tfx(b1.x), tfx(b1.y), tfx(b2.x), tfx(b2.y))
.attr(options.lineattr)]);
}
function bondSingleDown(paper, hb1, d, nlines, step, options) { // eslint-disable-line max-params
var a = hb1.p,
n = hb1.norm;
var bsp = 0.7 * options.stereoBond;
var path = '',
p,
q,
r;
for (var i = 0; i < nlines; ++i) {
r = a.addScaled(d, step * i);
p = r.addScaled(n, bsp * (i + 0.5) / (nlines - 0.5));
q = r.addScaled(n, -bsp * (i + 0.5) / (nlines - 0.5));
path += makeStroke(p, q);
}
return paper.path(path).attr(options.lineattr);
}
function bondSingleEither(paper, hb1, d, nlines, step, options) { // eslint-disable-line max-params
var a = hb1.p,
n = hb1.norm;
var bsp = 0.7 * options.stereoBond;
var path = 'M' + tfx(a.x) + ',' + tfx(a.y),
r = a;
for (var i = 0; i < nlines; ++i) {
r = a.addScaled(d, step * (i + 0.5)).addScaled(n,
((i & 1) ? -1 : +1) * bsp * (i + 0.5) / (nlines - 0.5));
path += 'L' + tfx(r.x) + ',' + tfx(r.y);
}
return paper.path(path)
.attr(options.lineattr);
}
function bondDouble(paper, a1, a2, b1, b2, cisTrans, options) { // eslint-disable-line max-params
return paper.path(cisTrans ?
'M{0},{1}L{6},{7}M{4},{5}L{2},{3}' :
'M{0},{1}L{2},{3}M{4},{5}L{6},{7}',
tfx(a1.x), tfx(a1.y), tfx(b1.x), tfx(b1.y), tfx(a2.x), tfx(a2.y), tfx(b2.x), tfx(b2.y))
.attr(options.lineattr);
}
function bondSingleOrDouble(paper, hb1, hb2, nSect, options) { // eslint-disable-line max-statements, max-params
var a = hb1.p,
b = hb2.p,
n = hb1.norm;
var bsp = options.bondSpace / 2;
var path = '',
pi,
pp = a;
for (var i = 1; i <= nSect; ++i) {
pi = Vec2.lc2(a, (nSect - i) / nSect, b, i / nSect);
if (i & 1) {
path += makeStroke(pp, pi);
} else {
path += makeStroke(pp.addScaled(n, bsp), pi.addScaled(n, bsp));
path += makeStroke(pp.addScaled(n, -bsp), pi.addScaled(n, -bsp));
}
pp = pi;
}
return paper.path(path)
.attr(options.lineattr);
}
function bondTriple(paper, hb1, hb2, options) {
var a = hb1.p,
b = hb2.p,
n = hb1.norm;
var a2 = a.addScaled(n, options.bondSpace);
var b2 = b.addScaled(n, options.bondSpace);
var a3 = a.addScaled(n, -options.bondSpace);
var b3 = b.addScaled(n, -options.bondSpace);
return paper.path(makeStroke(a, b) + makeStroke(a2, b2) + makeStroke(a3, b3))
.attr(options.lineattr);
}
function bondAromatic(paper, paths, bondShift, options) {
var l1 = paper.path(paths[0]).attr(options.lineattr);
var l2 = paper.path(paths[1]).attr(options.lineattr);
if (bondShift !== undefined && bondShift !== null)
(bondShift > 0 ? l1 : l2).attr({ 'stroke-dasharray': '- ' });
return paper.set([l1, l2]);
}
function bondAny(paper, hb1, hb2, options) {
var a = hb1.p,
b = hb2.p;
return paper.path(makeStroke(a, b))
.attr(options.lineattr).attr({ 'stroke-dasharray': '- ' });
}
function reactingCenter(paper, p, options) {
var pathdesc = '';
for (var i = 0; i < p.length / 2; ++i)
pathdesc += makeStroke(p[2 * i], p[(2 * i) + 1]);
return paper.path(pathdesc).attr(options.lineattr);
}
function topologyMark(paper, p, mark, options) {
var path = paper.text(p.x, p.y, mark)
.attr({
'font': options.font,
'font-size': options.fontszsub,
'fill': '#000'
});
var rbb = util.relBox(path.getBBox());
recenterText(path, rbb);
return path;
}
function radicalCap(paper, p, options) {
var s = options.lineWidth * 0.9;
var dw = s,
dh = 2 * s;
return paper.path('M{0},{1}L{2},{3}L{4},{5}',
tfx(p.x - dw), tfx(p.y + dh), tfx(p.x), tfx(p.y), tfx(p.x + dw), tfx(p.y + dh))
.attr({
'stroke': '#000',
'stroke-width': options.lineWidth * 0.7,
'stroke-linecap': 'square',
'stroke-linejoin': 'miter'
});
}
function radicalBullet(paper, p, options) {
return paper.circle(tfx(p.x), tfx(p.y), options.lineWidth)
.attr({
stroke: null,
fill: '#000'
});
}
function bracket(paper, d, n, c, bracketWidth, bracketHeight, options) { // eslint-disable-line max-params
bracketWidth = bracketWidth || 0.25;
bracketHeight = bracketHeight || 1.0;
var a0 = c.addScaled(n, -0.5 * bracketHeight);
var a1 = c.addScaled(n, 0.5 * bracketHeight);
var b0 = a0.addScaled(d, -bracketWidth);
var b1 = a1.addScaled(d, -bracketWidth);
return paper.path('M{0},{1}L{2},{3}L{4},{5}L{6},{7}',
tfx(b0.x), tfx(b0.y), tfx(a0.x), tfx(a0.y),
tfx(a1.x), tfx(a1.y), tfx(b1.x), tfx(b1.y))
.attr(options.sgroupBracketStyle);
}
function selectionRectangle(paper, p0, p1, options) {
return paper.rect(tfx(Math.min(p0.x, p1.x)),
tfx(Math.min(p0.y, p1.y)),
tfx(Math.abs(p1.x - p0.x)),
tfx(Math.abs(p1.y - p0.y))).attr(options.lassoStyle);
}
function selectionPolygon(paper, r, options) {
var v = r[r.length - 1];
var pstr = 'M' + tfx(v.x) + ',' + tfx(v.y);
for (var i = 0; i < r.length; ++i)
pstr += 'L' + tfx(r[i].x) + ',' + tfx(r[i].y);
return paper.path(pstr).attr(options.lassoStyle);
}
function selectionLine(paper, p0, p1, options) {
return paper.path(makeStroke(p0, p1)).attr(options.lassoStyle);
}
function makeStroke(a, b) {
return 'M' + tfx(a.x) + ',' + tfx(a.y) +
'L' + tfx(b.x) + ',' + tfx(b.y) + ' ';
}
function dashedPath(p0, p1, dash) {
var t0 = 0;
var t1 = Vec2.dist(p0, p1);
var d = Vec2.diff(p1, p0).normalized();
var black = true;
var path = '';
var i = 0;
while (t0 < t1) {
var len = dash[i % dash.length];
var t2 = t0 + Math.min(len, t1 - t0);
if (black)
path += 'M ' + p0.addScaled(d, t0).coordStr() + ' L ' + p0.addScaled(d, t2).coordStr();
t0 += len;
black = !black;
i++;
}
return path;
}
function aromaticBondPaths(a2, a3, b2, b3, mask, dash) { // eslint-disable-line max-params
var l1 = dash && (mask & 1) ? dashedPath(a2, b2, dash) : makeStroke(a2, b2);
var l2 = dash && (mask & 2) ? dashedPath(a3, b3, dash) : makeStroke(a3, b3);
return [l1, l2];
}
function recenterText(path, rbb) {
// TODO: find a better way
if (Raphael.vml) { // dirty hack
console.assert(null, "Souldn't go here!");
var gap = rbb.height * 0.16;
path.translateAbs(0, gap);
rbb.y += gap;
}
}
module.exports = {
recenterText: recenterText,
arrow: arrow,
plus: plus,
aromaticBondPaths: aromaticBondPaths,
bondSingle: bondSingle,
bondSingleUp: bondSingleUp,
bondSingleStereoBold: bondSingleStereoBold,
bondDoubleStereoBold: bondDoubleStereoBold,
bondSingleDown: bondSingleDown,
bondSingleEither: bondSingleEither,
bondDouble: bondDouble,
bondSingleOrDouble: bondSingleOrDouble,
bondTriple: bondTriple,
bondAromatic: bondAromatic,
bondAny: bondAny,
reactingCenter: reactingCenter,
topologyMark: topologyMark,
radicalCap: radicalCap,
radicalBullet: radicalBullet,
bracket: bracket,
selectionRectangle: selectionRectangle,
selectionPolygon: selectionPolygon,
selectionLine: selectionLine
};