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

172 lines
5.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 Vec2 = require('../../util/vec2');
var Struct = require('../../chem/struct');
var Action = require('../action');
var utils = require('./utils');
function BondTool(editor, bondProps) {
if (!(this instanceof BondTool)) {
// Action.fromBondAttrs(editor.render.ctab,
// editor.selection().bonds, {
// type: bondType(mode).type,
// stereo: Bond.PATTERN.STEREO.NONE })
editor.selection(null);
return new BondTool(editor, bondProps);
}
this.editor = editor;
this.atomProps = { label: 'C' };
this.bondProps = bondProps;
}
BondTool.prototype.mousedown = function (event) {
var rnd = this.editor.render;
this.editor.hover(null);
this.dragCtx = {
xy0: rnd.page2obj(event),
item: this.editor.findItem(event, ['atoms', 'bonds'])
};
if (!this.dragCtx.item) // ci.type == 'Canvas'
delete this.dragCtx.item;
return true;
};
BondTool.prototype.mousemove = function (event) { // eslint-disable-line max-statements
var editor = this.editor;
var rnd = editor.render;
if ('dragCtx' in this) {
var dragCtx = this.dragCtx;
if (!('item' in dragCtx) || dragCtx.item.map === 'atoms') {
if ('action' in dragCtx)
dragCtx.action.perform(rnd.ctab);
var i1, i2, p1, p2;
if (('item' in dragCtx && dragCtx.item.map === 'atoms')) {
// first mousedown event intersect with any atom
i1 = dragCtx.item.id;
i2 = editor.findItem(event, ['atoms'], dragCtx.item);
} else {
// first mousedown event intersect with any canvas
i1 = this.atomProps;
p1 = dragCtx.xy0;
i2 = editor.findItem(event, ['atoms']);
}
var dist = Number.MAX_VALUE;
if (i2 && i2.map === 'atoms') {
// after mousedown events is appered, cursor is moved and then cursor intersects any atoms
i2 = i2.id;
} else {
i2 = this.atomProps;
var xy1 = rnd.page2obj(event);
dist = Vec2.dist(dragCtx.xy0, xy1);
if (p1) {
// rotation only, leght of bond = 1;
p2 = utils.calcNewAtomPos(p1, xy1);
} else {
// first mousedown event intersect with any atom and
// rotation only, leght of bond = 1;
var atom = rnd.ctab.molecule.atoms.get(i1);
p1 = utils.calcNewAtomPos(atom.pp.get_xy0(), xy1);
}
}
// don't rotate the bond if the distance between the start and end point is too small
if (dist > 0.3)
dragCtx.action = Action.fromBondAddition(rnd.ctab, this.bondProps, i1, i2, p1, p2)[0];
else
delete dragCtx.action;
this.editor.update(dragCtx.action, true);
return true;
}
}
this.editor.hover(this.editor.findItem(event, ['atoms', 'bonds']));
return true;
};
BondTool.prototype.mouseup = function (event) { // eslint-disable-line max-statements
if ('dragCtx' in this) {
var dragCtx = this.dragCtx;
var rnd = this.editor.render;
var struct = rnd.ctab.molecule;
if ('action' in dragCtx) {
this.editor.update(dragCtx.action);
} else if (!('item' in dragCtx)) {
var xy = rnd.page2obj(event);
var v = new Vec2(1.0 / 2, 0).rotate(
this.bondProps.type == Struct.Bond.PATTERN.TYPE.SINGLE ? -Math.PI / 6 : 0
);
var bondAddition = Action.fromBondAddition(rnd.ctab,
this.bondProps, { label: 'C' }, { label: 'C' },
Vec2.diff(xy, v), Vec2.sum(xy, v));
this.editor.update(bondAddition[0]);
} else if (dragCtx.item.map === 'atoms') {
// when does it hapend?
this.editor.update(Action.fromBondAddition(rnd.ctab, this.bondProps, dragCtx.item.id)[0]);
} else if (dragCtx.item.map === 'bonds') {
var bondProps = Object.assign({}, this.bondProps);
var bond = struct.bonds.get(dragCtx.item.id);
this.editor.update(bondChangingAction(rnd.ctab, dragCtx.item.id, bond, bondProps));
}
delete this.dragCtx;
}
return true;
};
/**
* @param itemID - bond id in structure
* @param bond - bond for change
* @param bondProps - bondTool properties
* @returns Action
*/
function bondChangingAction(restruct, itemID, bond, bondProps) {
if (bondProps.stereo !== Struct.Bond.PATTERN.STEREO.NONE && //
bondProps.type === Struct.Bond.PATTERN.TYPE.SINGLE &&
bond.type === bondProps.type && bond.stereo === bondProps.stereo)
// if bondTool is stereo and equal to bond for change
return Action.fromBondFlipping(restruct, itemID);
var loop = plainBondTypes.indexOf(bondProps.type) >= 0 ? plainBondTypes : null;
if (bondProps.stereo === Struct.Bond.PATTERN.STEREO.NONE &&
bondProps.type === Struct.Bond.PATTERN.TYPE.SINGLE &&
bond.stereo === Struct.Bond.PATTERN.STEREO.NONE &&
loop)
// if `Single bond` tool is chosen and bond for change in `plainBondTypes`
bondProps.type = loop[(loop.indexOf(bond.type) + 1) % loop.length];
return Action.fromBondAttrs(restruct, itemID, bondProps,
bondFlipRequired(restruct.molecule, bond, bondProps));
}
function bondFlipRequired(struct, bond, attrs) {
return attrs.type == Struct.Bond.PATTERN.TYPE.SINGLE &&
bond.stereo == Struct.Bond.PATTERN.STEREO.NONE &&
attrs.stereo != Struct.Bond.PATTERN.STEREO.NONE &&
struct.atoms.get(bond.begin).neighbors.length <
struct.atoms.get(bond.end).neighbors.length;
}
var plainBondTypes = [
Struct.Bond.PATTERN.TYPE.SINGLE,
Struct.Bond.PATTERN.TYPE.DOUBLE,
Struct.Bond.PATTERN.TYPE.TRIPLE
];
module.exports = Object.assign(BondTool, {
bondChangingAction: bondChangingAction
});