forked from enviPath/enviPy
Current Dev State
This commit is contained in:
980
static/js/ketcher2/script/editor/op.js
Normal file
980
static/js/ketcher2/script/editor/op.js
Normal file
@ -0,0 +1,980 @@
|
||||
/****************************************************************************
|
||||
* 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 Set = require('../util/set');
|
||||
var scale = require('../util/scale');
|
||||
|
||||
var Struct = require('../chem/struct');
|
||||
var ReStruct = require('../render/restruct');
|
||||
|
||||
var DEBUG = { debug: false, logcnt: 0, logmouse: false, hl: false };
|
||||
DEBUG.logMethod = function () { };
|
||||
|
||||
function Base() {
|
||||
this.type = 'OpBase';
|
||||
|
||||
// assert here?
|
||||
this.execute = function () {
|
||||
throw new Error('Operation.execute() is not implemented');
|
||||
};
|
||||
this.invert = function () {
|
||||
throw new Error('Operation.invert() is not implemented');
|
||||
};
|
||||
|
||||
this.perform = function (restruct) {
|
||||
this.execute(restruct);
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
if (!this._inverted) {
|
||||
this._inverted = this.invert();
|
||||
this._inverted._inverted = this;
|
||||
}
|
||||
return this._inverted;
|
||||
};
|
||||
this.isDummy = function (restruct) {
|
||||
return this._isDummy ? this._isDummy(restruct) : false;
|
||||
/* eslint-enable no-underscore-dangle */
|
||||
};
|
||||
}
|
||||
|
||||
function AtomAdd(atom, pos) {
|
||||
this.data = { aid: null, atom: atom, pos: pos };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
var pp = {};
|
||||
if (this.data.atom) {
|
||||
for (var p in this.data.atom)
|
||||
if (this.data.atom.hasOwnProperty(p)) pp[p] = this.data.atom[p];
|
||||
}
|
||||
pp.label = pp.label || 'C';
|
||||
if (!(typeof this.data.aid === "number"))
|
||||
this.data.aid = struct.atoms.add(new Struct.Atom(pp));
|
||||
else
|
||||
struct.atoms.set(this.data.aid, new Struct.Atom(pp));
|
||||
|
||||
// notifyAtomAdded
|
||||
var atomData = new ReStruct.Atom(restruct.molecule.atoms.get(this.data.aid));
|
||||
atomData.component = restruct.connectedComponents.add(Set.single(this.data.aid));
|
||||
restruct.atoms.set(this.data.aid, atomData);
|
||||
restruct.markAtom(this.data.aid, 1);
|
||||
|
||||
struct.atomSetPos(this.data.aid, new Vec2(this.data.pos));
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new AtomDelete();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
AtomAdd.prototype = new Base();
|
||||
|
||||
function AtomDelete(aid) {
|
||||
this.data = { aid: aid, atom: null, pos: null };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
if (!this.data.atom) {
|
||||
this.data.atom = struct.atoms.get(this.data.aid);
|
||||
this.data.pos = this.data.atom.pp;
|
||||
}
|
||||
|
||||
// notifyAtomRemoved(this.data.aid);
|
||||
var atom = restruct.atoms.get(this.data.aid);
|
||||
var set = restruct.connectedComponents.get(atom.component);
|
||||
Set.remove(set, this.data.aid);
|
||||
if (Set.size(set) == 0)
|
||||
restruct.connectedComponents.remove(atom.component);
|
||||
restruct.clearVisel(atom.visel);
|
||||
restruct.atoms.unset(this.data.aid);
|
||||
restruct.markItemRemoved();
|
||||
|
||||
struct.atoms.remove(this.data.aid);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new AtomAdd();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
AtomDelete.prototype = new Base();
|
||||
|
||||
function AtomAttr(aid, attribute, value) {
|
||||
this.data = { aid: aid, attribute: attribute, value: value };
|
||||
this.data2 = null;
|
||||
this.execute = function (restruct) {
|
||||
var atom = restruct.molecule.atoms.get(this.data.aid);
|
||||
if (!this.data2)
|
||||
this.data2 = { aid: this.data.aid, attribute: this.data.attribute, value: atom[this.data.attribute] };
|
||||
atom[this.data.attribute] = this.data.value;
|
||||
invalidateAtom(restruct, this.data.aid);
|
||||
};
|
||||
this._isDummy = function (restruct) { // eslint-disable-line no-underscore-dangle
|
||||
return restruct.molecule.atoms.get(this.data.aid)[this.data.attribute] == this.data.value;
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new AtomAttr();
|
||||
ret.data = this.data2;
|
||||
ret.data2 = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
AtomAttr.prototype = new Base();
|
||||
|
||||
function AtomMove(aid, d, noinvalidate) {
|
||||
this.data = { aid: aid, d: d, noinvalidate: noinvalidate };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
var aid = this.data.aid;
|
||||
var d = this.data.d;
|
||||
struct.atoms.get(aid).pp.add_(d); // eslint-disable-line no-underscore-dangle
|
||||
restruct.atoms.get(aid).visel.translate(scale.obj2scaled(d, restruct.render.options));
|
||||
this.data.d = d.negated();
|
||||
if (!this.data.noinvalidate)
|
||||
invalidateAtom(restruct, aid, 1);
|
||||
};
|
||||
this._isDummy = function () { // eslint-disable-line no-underscore-dangle
|
||||
return this.data.d.x == 0 && this.data.d.y == 0;
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new AtomMove();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
AtomMove.prototype = new Base();
|
||||
|
||||
function BondMove(bid, d) {
|
||||
this.data = { bid: bid, d: d };
|
||||
this.execute = function (restruct) {
|
||||
restruct.bonds.get(this.data.bid).visel.translate(scale.obj2scaled(this.data.d, restruct.render.options));
|
||||
this.data.d = this.data.d.negated();
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new BondMove();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
BondMove.prototype = new Base();
|
||||
|
||||
function LoopMove(id, d) {
|
||||
this.data = { id: id, d: d };
|
||||
this.execute = function (restruct) {
|
||||
// not sure if there should be an action to move a loop in the first place
|
||||
// but we have to somehow move the aromatic ring, which is associated with the loop, rather than with any of the bonds
|
||||
if (restruct.reloops.get(this.data.id) && restruct.reloops.get(this.data.id).visel)
|
||||
restruct.reloops.get(this.data.id).visel.translate(scale.obj2scaled(this.data.d, restruct.render.options));
|
||||
this.data.d = this.data.d.negated();
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new LoopMove();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
LoopMove.prototype = new Base();
|
||||
|
||||
function SGroupAtomAdd(sgid, aid) {
|
||||
this.type = 'OpSGroupAtomAdd';
|
||||
this.data = { sgid, aid };
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const struct = restruct.molecule;
|
||||
const aid = this.data.aid;
|
||||
const sgid = this.data.sgid;
|
||||
const atom = struct.atoms.get(aid);
|
||||
const sg = struct.sgroups.get(sgid);
|
||||
|
||||
if (sg.atoms.indexOf(aid) >= 0)
|
||||
throw new Error('The same atom cannot be added to an S-group more than once');
|
||||
|
||||
if (!atom)
|
||||
throw new Error('OpSGroupAtomAdd: Atom ' + aid + ' not found');
|
||||
|
||||
struct.atomAddToSGroup(sgid, aid);
|
||||
invalidateAtom(restruct, aid);
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
const ret = new SGroupAtomRemove();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
SGroupAtomAdd.prototype = new Base();
|
||||
|
||||
function SGroupAtomRemove(sgid, aid) {
|
||||
this.type = 'OpSGroupAtomRemove';
|
||||
this.data = { sgid, aid };
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const aid = this.data.aid;
|
||||
const sgid = this.data.sgid;
|
||||
const struct = restruct.molecule;
|
||||
const atom = struct.atoms.get(aid);
|
||||
const sg = struct.sgroups.get(sgid);
|
||||
|
||||
Struct.SGroup.removeAtom(sg, aid);
|
||||
Set.remove(atom.sgs, sgid);
|
||||
invalidateAtom(restruct, aid);
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
const ret = new SGroupAtomAdd();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
SGroupAtomRemove.prototype = new Base();
|
||||
|
||||
function SGroupAttr(sgid, attr, value) {
|
||||
this.type = 'OpSGroupAttr';
|
||||
this.data = { sgid, attr, value };
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const struct = restruct.molecule;
|
||||
const sgid = this.data.sgid;
|
||||
const sg = struct.sgroups.get(sgid);
|
||||
|
||||
if (sg.type === 'DAT' && restruct.sgroupData.has(sgid)) {
|
||||
// clean the stuff here, else it might be left behind if the sgroups is set to "attached"
|
||||
restruct.clearVisel(restruct.sgroupData.get(sgid).visel);
|
||||
restruct.sgroupData.unset(sgid);
|
||||
}
|
||||
|
||||
this.data.value = sg.setAttr(this.data.attr, this.data.value);
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
const ret = new SGroupAttr();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
SGroupAttr.prototype = new Base();
|
||||
|
||||
function SGroupCreate(sgid, type, pp) {
|
||||
this.type = 'OpSGroupCreate';
|
||||
this.data = { sgid, type, pp };
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const struct = restruct.molecule;
|
||||
const sg = new Struct.SGroup(this.data.type);
|
||||
const sgid = this.data.sgid;
|
||||
|
||||
sg.id = sgid;
|
||||
struct.sgroups.set(sgid, sg);
|
||||
|
||||
if (this.data.pp)
|
||||
struct.sgroups.get(sgid).pp = new Vec2(this.data.pp);
|
||||
|
||||
restruct.sgroups.set(sgid, new ReStruct.SGroup(struct.sgroups.get(sgid)));
|
||||
this.data.sgid = sgid;
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
const ret = new SGroupDelete();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
SGroupCreate.prototype = new Base();
|
||||
|
||||
function SGroupDelete(sgid) {
|
||||
this.type = 'OpSGroupDelete';
|
||||
this.data = { sgid };
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const struct = restruct.molecule;
|
||||
const sgid = this.data.sgid;
|
||||
const sg = restruct.sgroups.get(sgid);
|
||||
|
||||
this.data.type = sg.item.type;
|
||||
this.data.pp = sg.item.pp;
|
||||
|
||||
if (sg.item.type === 'DAT' && restruct.sgroupData.has(sgid)) {
|
||||
restruct.clearVisel(restruct.sgroupData.get(sgid).visel);
|
||||
restruct.sgroupData.unset(sgid);
|
||||
}
|
||||
|
||||
restruct.clearVisel(sg.visel);
|
||||
if (sg.item.atoms.length !== 0)
|
||||
throw new Error('S-Group not empty!');
|
||||
|
||||
restruct.sgroups.unset(sgid);
|
||||
struct.sgroups.remove(sgid);
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
const ret = new SGroupCreate();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
SGroupDelete.prototype = new Base();
|
||||
|
||||
function SGroupAddToHierarchy(sgid, parent, children) {
|
||||
this.type = 'OpSGroupAddToHierarchy';
|
||||
this.data = { sgid, parent, children };
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const struct = restruct.molecule;
|
||||
const sgid = this.data.sgid;
|
||||
const relations = struct.sGroupForest.insert(sgid, parent, children);
|
||||
|
||||
this.data.parent = relations.parent;
|
||||
this.data.children = relations.children;
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
const ret = new SGroupRemoveFromHierarchy();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
SGroupAddToHierarchy.prototype = new Base();
|
||||
|
||||
function SGroupRemoveFromHierarchy(sgid) {
|
||||
this.type = 'OpSGroupRemoveFromHierarchy';
|
||||
this.data = { sgid };
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const struct = restruct.molecule;
|
||||
const sgid = this.data.sgid;
|
||||
|
||||
this.data.parent = struct.sGroupForest.parent.get(sgid);
|
||||
this.data.children = struct.sGroupForest.children.get(sgid);
|
||||
struct.sGroupForest.remove(sgid);
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
const ret = new SGroupAddToHierarchy();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
SGroupRemoveFromHierarchy.prototype = new Base();
|
||||
|
||||
function BondAdd(begin, end, bond) {
|
||||
this.data = { bid: null, bond: bond, begin: begin, end: end };
|
||||
this.execute = function (restruct) { // eslint-disable-line max-statements
|
||||
var struct = restruct.molecule;
|
||||
if (this.data.begin == this.data.end)
|
||||
throw new Error('Distinct atoms expected');
|
||||
if (DEBUG.debug && this.molecule.checkBondExists(this.data.begin, this.data.end))
|
||||
throw new Error('Bond already exists');
|
||||
|
||||
invalidateAtom(restruct, this.data.begin, 1);
|
||||
invalidateAtom(restruct, this.data.end, 1);
|
||||
|
||||
var pp = {};
|
||||
if (this.data.bond) {
|
||||
for (var p in this.data.bond)
|
||||
if (this.data.bond.hasOwnProperty(p)) pp[p] = this.data.bond[p];
|
||||
}
|
||||
pp.type = pp.type || Struct.Bond.PATTERN.TYPE.SINGLE;
|
||||
pp.begin = this.data.begin;
|
||||
pp.end = this.data.end;
|
||||
|
||||
if (!(typeof this.data.bid === "number"))
|
||||
this.data.bid = struct.bonds.add(new Struct.Bond(pp));
|
||||
else
|
||||
struct.bonds.set(this.data.bid, new Struct.Bond(pp));
|
||||
struct.bondInitHalfBonds(this.data.bid);
|
||||
struct.atomAddNeighbor(struct.bonds.get(this.data.bid).hb1);
|
||||
struct.atomAddNeighbor(struct.bonds.get(this.data.bid).hb2);
|
||||
|
||||
// notifyBondAdded
|
||||
restruct.bonds.set(this.data.bid, new ReStruct.Bond(restruct.molecule.bonds.get(this.data.bid)));
|
||||
restruct.markBond(this.data.bid, 1);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new BondDelete();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
BondAdd.prototype = new Base();
|
||||
|
||||
function BondDelete(bid) {
|
||||
this.data = { bid: bid, bond: null, begin: null, end: null };
|
||||
this.execute = function (restruct) { // eslint-disable-line max-statements
|
||||
var struct = restruct.molecule;
|
||||
if (!this.data.bond) {
|
||||
this.data.bond = struct.bonds.get(this.data.bid);
|
||||
this.data.begin = this.data.bond.begin;
|
||||
this.data.end = this.data.bond.end;
|
||||
}
|
||||
|
||||
invalidateBond(restruct, this.data.bid);
|
||||
|
||||
// notifyBondRemoved
|
||||
var rebond = restruct.bonds.get(this.data.bid);
|
||||
[rebond.b.hb1, rebond.b.hb2].forEach(function (hbid) {
|
||||
var hb = restruct.molecule.halfBonds.get(hbid);
|
||||
if (hb.loop >= 0)
|
||||
restruct.loopRemove(hb.loop);
|
||||
}, restruct);
|
||||
restruct.clearVisel(rebond.visel);
|
||||
restruct.bonds.unset(this.data.bid);
|
||||
restruct.markItemRemoved();
|
||||
|
||||
var bond = struct.bonds.get(this.data.bid);
|
||||
[bond.hb1, bond.hb2].forEach(function (hbid) {
|
||||
var hb = struct.halfBonds.get(hbid);
|
||||
var atom = struct.atoms.get(hb.begin);
|
||||
var pos = atom.neighbors.indexOf(hbid);
|
||||
var prev = (pos + atom.neighbors.length - 1) % atom.neighbors.length;
|
||||
var next = (pos + 1) % atom.neighbors.length;
|
||||
struct.setHbNext(atom.neighbors[prev], atom.neighbors[next]);
|
||||
atom.neighbors.splice(pos, 1);
|
||||
}, this);
|
||||
struct.halfBonds.unset(bond.hb1);
|
||||
struct.halfBonds.unset(bond.hb2);
|
||||
|
||||
struct.bonds.remove(this.data.bid);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new BondAdd();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
BondDelete.prototype = new Base();
|
||||
|
||||
function BondAttr(bid, attribute, value) {
|
||||
this.data = { bid: bid, attribute: attribute, value: value };
|
||||
this.data2 = null;
|
||||
this.execute = function (restruct) {
|
||||
var bond = restruct.molecule.bonds.get(this.data.bid);
|
||||
if (!this.data2)
|
||||
this.data2 = { bid: this.data.bid, attribute: this.data.attribute, value: bond[this.data.attribute] };
|
||||
|
||||
bond[this.data.attribute] = this.data.value;
|
||||
|
||||
invalidateBond(restruct, this.data.bid);
|
||||
if (this.data.attribute === 'type')
|
||||
invalidateLoop(restruct, this.data.bid);
|
||||
};
|
||||
this._isDummy = function (restruct) { // eslint-disable-line no-underscore-dangle
|
||||
return restruct.molecule.bonds.get(this.data.bid)[this.data.attribute] == this.data.value;
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new BondAttr();
|
||||
ret.data = this.data2;
|
||||
ret.data2 = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
BondAttr.prototype = new Base();
|
||||
|
||||
function FragmentAdd(frid) {
|
||||
this.frid = (typeof frid === 'undefined') ? null : frid;
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
var frag = {};
|
||||
if (this.frid == null)
|
||||
this.frid = struct.frags.add(frag);
|
||||
else
|
||||
struct.frags.set(this.frid, frag);
|
||||
restruct.frags.set(this.frid, new ReStruct.Frag(frag)); // TODO add ReStruct.notifyFragmentAdded
|
||||
};
|
||||
this.invert = function () {
|
||||
return new FragmentDelete(this.frid);
|
||||
};
|
||||
}
|
||||
FragmentAdd.prototype = new Base();
|
||||
|
||||
function FragmentDelete(frid) {
|
||||
this.frid = frid;
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
invalidateItem(restruct, 'frags', this.frid, 1);
|
||||
restruct.frags.unset(this.frid);
|
||||
struct.frags.remove(this.frid); // TODO add ReStruct.notifyFragmentRemoved
|
||||
};
|
||||
this.invert = function () {
|
||||
return new FragmentAdd(this.frid);
|
||||
};
|
||||
}
|
||||
FragmentDelete.prototype = new Base();
|
||||
|
||||
function RGroupAttr(rgid, attribute, value) {
|
||||
this.data = { rgid: rgid, attribute: attribute, value: value };
|
||||
this.data2 = null;
|
||||
this.execute = function (restruct) {
|
||||
var rgp = restruct.molecule.rgroups.get(this.data.rgid);
|
||||
if (!this.data2)
|
||||
this.data2 = { rgid: this.data.rgid, attribute: this.data.attribute, value: rgp[this.data.attribute] };
|
||||
|
||||
rgp[this.data.attribute] = this.data.value;
|
||||
|
||||
invalidateItem(restruct, 'rgroups', this.data.rgid);
|
||||
};
|
||||
this._isDummy = function (restruct) { // eslint-disable-line no-underscore-dangle
|
||||
return restruct.molecule.rgroups.get(this.data.rgid)[this.data.attribute] == this.data.value;
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new RGroupAttr();
|
||||
ret.data = this.data2;
|
||||
ret.data2 = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
RGroupAttr.prototype = new Base();
|
||||
|
||||
function RGroupFragment(rgid, frid, rg) {
|
||||
this.type = 'OpAddOrDeleteRGFragment';
|
||||
this.rgid_new = rgid;
|
||||
this.rg_new = rg;
|
||||
this.rgid_old = null;
|
||||
this.rg_old = null;
|
||||
this.frid = frid;
|
||||
|
||||
this.execute = function (restruct) { // eslint-disable-line max-statements
|
||||
const struct = restruct.molecule;
|
||||
this.rgid_old = this.rgid_old || Struct.RGroup.findRGroupByFragment(struct.rgroups, this.frid);
|
||||
this.rg_old = (this.rgid_old ? struct.rgroups.get(this.rgid_old) : null);
|
||||
|
||||
if (this.rg_old) {
|
||||
this.rg_old.frags.remove(this.rg_old.frags.keyOf(this.frid));
|
||||
restruct.clearVisel(restruct.rgroups.get(this.rgid_old).visel);
|
||||
|
||||
if (this.rg_old.frags.count() === 0) {
|
||||
restruct.rgroups.unset(this.rgid_old);
|
||||
struct.rgroups.unset(this.rgid_old);
|
||||
restruct.markItemRemoved();
|
||||
} else {
|
||||
restruct.markItem('rgroups', this.rgid_old, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.rgid_new) {
|
||||
let rgNew = struct.rgroups.get(this.rgid_new);
|
||||
if (!rgNew) {
|
||||
rgNew = this.rg_new || new Struct.RGroup();
|
||||
struct.rgroups.set(this.rgid_new, rgNew);
|
||||
restruct.rgroups.set(this.rgid_new, new ReStruct.RGroup(rgNew));
|
||||
} else {
|
||||
restruct.markItem('rgroups', this.rgid_new, 1);
|
||||
}
|
||||
rgNew.frags.add(this.frid);
|
||||
}
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
return new RGroupFragment(this.rgid_old, this.frid, this.rg_old);
|
||||
};
|
||||
}
|
||||
RGroupFragment.prototype = new Base();
|
||||
|
||||
function UpdateIfThen(rgNew, rgOld) {
|
||||
this.type = 'OpUpdateIfThenValues';
|
||||
this.rgid_new = rgNew;
|
||||
this.rgid_old = rgOld;
|
||||
this.ifThenHistory = {};
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const struct = restruct.molecule;
|
||||
|
||||
struct.rgroups.keys().forEach(rgKey => {
|
||||
const rgValue = struct.rgroups.get(rgKey);
|
||||
|
||||
if (rgValue.ifthen === this.rgid_old) {
|
||||
rgValue.ifthen = this.rgid_new;
|
||||
this.ifThenHistory[rgKey] = this.rgid_old;
|
||||
struct.rgroups.set(rgKey, rgValue);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
return new RestoreIfThen(this.rgid_new, this.rgid_old, this.ifThenHistory);
|
||||
};
|
||||
}
|
||||
UpdateIfThen.prototype = new Base();
|
||||
|
||||
function RestoreIfThen(rgNew, rgOld, history) {
|
||||
this.type = 'OpRestoreIfThenValues';
|
||||
this.rgid_new = rgNew;
|
||||
this.rgid_old = rgOld;
|
||||
this.ifThenHistory = history || {};
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const struct = restruct.molecule;
|
||||
|
||||
Object.keys(this.ifThenHistory).forEach(rgid => {
|
||||
const rgValue = struct.rgroups.get(rgid);
|
||||
rgValue.ifthen = this.ifThenHistory[rgid];
|
||||
struct.rgroups.set(rgid, rgValue);
|
||||
});
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
return new UpdateIfThen(this.rgid_old, this.rgid_new);
|
||||
};
|
||||
}
|
||||
RestoreIfThen.prototype = new Base();
|
||||
|
||||
function RxnArrowAdd(pos) {
|
||||
this.data = { arid: null, pos: pos };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
if (!(typeof this.data.arid === 'number'))
|
||||
this.data.arid = struct.rxnArrows.add(new Struct.RxnArrow());
|
||||
else
|
||||
struct.rxnArrows.set(this.data.arid, new Struct.RxnArrow());
|
||||
|
||||
// notifyRxnArrowAdded
|
||||
restruct.rxnArrows.set(this.data.arid, new ReStruct.RxnArrow(restruct.molecule.rxnArrows.get(this.data.arid)));
|
||||
|
||||
struct.rxnArrowSetPos(this.data.arid, new Vec2(this.data.pos));
|
||||
|
||||
invalidateItem(restruct, 'rxnArrows', this.data.arid, 1);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new RxnArrowDelete();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
RxnArrowAdd.prototype = new Base();
|
||||
|
||||
function RxnArrowDelete(arid) {
|
||||
this.data = { arid: arid, pos: null };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
if (!this.data.pos)
|
||||
this.data.pos = struct.rxnArrows.get(this.data.arid).pp;
|
||||
|
||||
// notifyRxnArrowRemoved
|
||||
restruct.markItemRemoved();
|
||||
restruct.clearVisel(restruct.rxnArrows.get(this.data.arid).visel);
|
||||
restruct.rxnArrows.unset(this.data.arid);
|
||||
|
||||
struct.rxnArrows.remove(this.data.arid);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new RxnArrowAdd();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
RxnArrowDelete.prototype = new Base();
|
||||
|
||||
function RxnArrowMove(id, d, noinvalidate) {
|
||||
this.data = { id: id, d: d, noinvalidate: noinvalidate };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
var id = this.data.id;
|
||||
var d = this.data.d;
|
||||
struct.rxnArrows.get(id).pp.add_(d); // eslint-disable-line no-underscore-dangle
|
||||
restruct.rxnArrows.get(id).visel.translate(scale.obj2scaled(d, restruct.render.options));
|
||||
this.data.d = d.negated();
|
||||
if (!this.data.noinvalidate)
|
||||
invalidateItem(restruct, 'rxnArrows', id, 1);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new RxnArrowMove();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
RxnArrowMove.prototype = new Base();
|
||||
|
||||
function RxnPlusAdd(pos) {
|
||||
this.data = { plid: null, pos: pos };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
if (!(typeof this.data.plid === 'number'))
|
||||
this.data.plid = struct.rxnPluses.add(new Struct.RxnPlus());
|
||||
else
|
||||
struct.rxnPluses.set(this.data.plid, new Struct.RxnPlus());
|
||||
|
||||
// notifyRxnPlusAdded
|
||||
restruct.rxnPluses.set(this.data.plid, new ReStruct.RxnPlus(restruct.molecule.rxnPluses.get(this.data.plid)));
|
||||
|
||||
struct.rxnPlusSetPos(this.data.plid, new Vec2(this.data.pos));
|
||||
|
||||
invalidateItem(restruct, 'rxnPluses', this.data.plid, 1);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new RxnPlusDelete();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
RxnPlusAdd.prototype = new Base();
|
||||
|
||||
function RxnPlusDelete(plid) {
|
||||
this.data = { plid: plid, pos: null };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
if (!this.data.pos)
|
||||
this.data.pos = struct.rxnPluses.get(this.data.plid).pp;
|
||||
|
||||
// notifyRxnPlusRemoved
|
||||
restruct.markItemRemoved();
|
||||
restruct.clearVisel(restruct.rxnPluses.get(this.data.plid).visel);
|
||||
restruct.rxnPluses.unset(this.data.plid);
|
||||
|
||||
struct.rxnPluses.remove(this.data.plid);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new RxnPlusAdd();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
RxnPlusDelete.prototype = new Base();
|
||||
|
||||
function RxnPlusMove(id, d, noinvalidate) {
|
||||
this.data = { id: id, d: d, noinvalidate: noinvalidate };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
var id = this.data.id;
|
||||
var d = this.data.d;
|
||||
struct.rxnPluses.get(id).pp.add_(d); // eslint-disable-line no-underscore-dangle
|
||||
restruct.rxnPluses.get(id).visel.translate(scale.obj2scaled(d, restruct.render.options));
|
||||
this.data.d = d.negated();
|
||||
if (!this.data.noinvalidate)
|
||||
invalidateItem(restruct, 'rxnPluses', id, 1);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new RxnPlusMove();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
RxnPlusMove.prototype = new Base();
|
||||
|
||||
function SGroupDataMove(id, d) {
|
||||
this.data = { id: id, d: d };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
struct.sgroups.get(this.data.id).pp.add_(this.data.d); // eslint-disable-line no-underscore-dangle
|
||||
this.data.d = this.data.d.negated();
|
||||
invalidateItem(restruct, 'sgroupData', this.data.id, 1); // [MK] this currently does nothing since the DataSGroupData Visel only contains the highlighting/selection and SGroups are redrawn every time anyway
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new SGroupDataMove();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
SGroupDataMove.prototype = new Base();
|
||||
|
||||
function CanvasLoad(struct) {
|
||||
this.data = { struct: struct };
|
||||
this.execute = function (restruct) {
|
||||
var oldStruct = restruct.molecule;
|
||||
restruct.clearVisels(); // TODO: What is it?
|
||||
restruct.render.setMolecule(this.data.struct);
|
||||
this.data.struct = oldStruct;
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
var ret = new CanvasLoad();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
CanvasLoad.prototype = new Base();
|
||||
|
||||
function ChiralFlagAdd(pos) {
|
||||
this.data = { pos: pos };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
if (restruct.chiralFlags.count() > 0) {
|
||||
// throw new Error('Cannot add more than one Chiral flag');
|
||||
restruct.clearVisel(restruct.chiralFlags.get(0).visel);
|
||||
restruct.chiralFlags.unset(0);
|
||||
}
|
||||
|
||||
restruct.chiralFlags.set(0, new ReStruct.ChiralFlag(pos));
|
||||
struct.isChiral = true;
|
||||
invalidateItem(restruct, 'chiralFlags', 0, 1);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new ChiralFlagDelete();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
ChiralFlagAdd.prototype = new Base();
|
||||
|
||||
function ChiralFlagDelete() {
|
||||
this.data = { pos: null };
|
||||
this.execute = function (restruct) {
|
||||
var struct = restruct.molecule;
|
||||
if (restruct.chiralFlags.count() < 1)
|
||||
throw new Error('Cannot remove chiral flag');
|
||||
restruct.clearVisel(restruct.chiralFlags.get(0).visel);
|
||||
this.data.pos = restruct.chiralFlags.get(0).pp;
|
||||
restruct.chiralFlags.unset(0);
|
||||
struct.isChiral = false;
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new ChiralFlagAdd(this.data.pos);
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
ChiralFlagDelete.prototype = new Base();
|
||||
|
||||
function ChiralFlagMove(d) {
|
||||
this.data = { d: d };
|
||||
this.execute = function (restruct) {
|
||||
restruct.chiralFlags.get(0).pp.add_(this.data.d); // eslint-disable-line no-underscore-dangle
|
||||
this.data.d = this.data.d.negated();
|
||||
invalidateItem(restruct, 'chiralFlags', 0, 1);
|
||||
};
|
||||
this.invert = function () {
|
||||
var ret = new ChiralFlagMove();
|
||||
ret.data = this.data;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
ChiralFlagMove.prototype = new Base();
|
||||
|
||||
function AlignDescriptors() {
|
||||
this.type = 'OpAlignDescriptors';
|
||||
this.history = {};
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const sgroups = restruct.molecule.sgroups.values().reverse();
|
||||
|
||||
let alignPoint = sgroups.reduce(
|
||||
(acc, sg) => new Vec2(
|
||||
Math.max(sg.bracketBox.p1.x, acc.x),
|
||||
Math.min(sg.bracketBox.p0.y, acc.y)
|
||||
), new Vec2(0.0, Infinity)
|
||||
)
|
||||
.add(new Vec2(0.5, -0.5));
|
||||
|
||||
sgroups.forEach(sg => {
|
||||
this.history[sg.id] = sg.pp;
|
||||
alignPoint = alignPoint.add(new Vec2(0.0, 0.5));
|
||||
sg.pp = alignPoint;
|
||||
restruct.molecule.sgroups.set(sg.id, sg);
|
||||
});
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
return new RestoreDescriptorsPosition(this.history);
|
||||
};
|
||||
}
|
||||
AlignDescriptors.prototype = new Base();
|
||||
|
||||
function RestoreDescriptorsPosition(history) {
|
||||
this.type = 'OpRestoreDescriptorsPosition';
|
||||
this.history = history;
|
||||
|
||||
this.execute = function (restruct) {
|
||||
const sgroups = restruct.molecule.sgroups.values();
|
||||
|
||||
sgroups.forEach(sg => {
|
||||
sg.pp = this.history[sg.id];
|
||||
restruct.molecule.sgroups.set(sg.id, sg);
|
||||
});
|
||||
};
|
||||
|
||||
this.invert = function () {
|
||||
return new AlignDescriptors();
|
||||
};
|
||||
}
|
||||
RestoreDescriptorsPosition.prototype = new Base();
|
||||
|
||||
function invalidateAtom(restruct, aid, level) {
|
||||
var atom = restruct.atoms.get(aid);
|
||||
restruct.markAtom(aid, level ? 1 : 0);
|
||||
var hbs = restruct.molecule.halfBonds;
|
||||
for (var i = 0; i < atom.a.neighbors.length; ++i) {
|
||||
var hbid = atom.a.neighbors[i];
|
||||
if (hbs.has(hbid)) {
|
||||
var hb = hbs.get(hbid);
|
||||
restruct.markBond(hb.bid, 1);
|
||||
restruct.markAtom(hb.end, 0);
|
||||
if (level)
|
||||
invalidateLoop(restruct, hb.bid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function invalidateLoop(restruct, bid) {
|
||||
var bond = restruct.bonds.get(bid);
|
||||
var lid1 = restruct.molecule.halfBonds.get(bond.b.hb1).loop;
|
||||
var lid2 = restruct.molecule.halfBonds.get(bond.b.hb2).loop;
|
||||
if (lid1 >= 0)
|
||||
restruct.loopRemove(lid1);
|
||||
if (lid2 >= 0)
|
||||
restruct.loopRemove(lid2);
|
||||
}
|
||||
|
||||
function invalidateBond(restruct, bid) {
|
||||
var bond = restruct.bonds.get(bid);
|
||||
invalidateLoop(restruct, bid);
|
||||
invalidateAtom(restruct, bond.b.begin, 0);
|
||||
invalidateAtom(restruct, bond.b.end, 0);
|
||||
}
|
||||
|
||||
function invalidateItem(restruct, map, id, level) {
|
||||
if (map === 'atoms') {
|
||||
invalidateAtom(restruct, id, level);
|
||||
} else if (map === 'bonds') {
|
||||
invalidateBond(restruct, id);
|
||||
if (level > 0)
|
||||
invalidateLoop(restruct, id);
|
||||
} else {
|
||||
restruct.markItem(map, id, level);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AtomAdd: AtomAdd,
|
||||
AtomDelete: AtomDelete,
|
||||
AtomAttr: AtomAttr,
|
||||
AtomMove: AtomMove,
|
||||
BondMove: BondMove,
|
||||
LoopMove: LoopMove,
|
||||
SGroupAtomAdd: SGroupAtomAdd,
|
||||
SGroupAtomRemove: SGroupAtomRemove,
|
||||
SGroupAttr: SGroupAttr,
|
||||
SGroupCreate: SGroupCreate,
|
||||
SGroupDelete: SGroupDelete,
|
||||
SGroupAddToHierarchy: SGroupAddToHierarchy,
|
||||
SGroupRemoveFromHierarchy: SGroupRemoveFromHierarchy,
|
||||
BondAdd: BondAdd,
|
||||
BondDelete: BondDelete,
|
||||
BondAttr: BondAttr,
|
||||
FragmentAdd: FragmentAdd,
|
||||
FragmentDelete: FragmentDelete,
|
||||
RGroupAttr: RGroupAttr,
|
||||
RGroupFragment: RGroupFragment,
|
||||
RxnArrowAdd: RxnArrowAdd,
|
||||
RxnArrowDelete: RxnArrowDelete,
|
||||
RxnArrowMove: RxnArrowMove,
|
||||
RxnPlusAdd: RxnPlusAdd,
|
||||
RxnPlusDelete: RxnPlusDelete,
|
||||
RxnPlusMove: RxnPlusMove,
|
||||
SGroupDataMove: SGroupDataMove,
|
||||
CanvasLoad: CanvasLoad,
|
||||
ChiralFlagAdd: ChiralFlagAdd,
|
||||
ChiralFlagDelete: ChiralFlagDelete,
|
||||
ChiralFlagMove: ChiralFlagMove,
|
||||
UpdateIfThen: UpdateIfThen,
|
||||
AlignDescriptors: AlignDescriptors,
|
||||
RestoreDescriptorsPosition: RestoreDescriptorsPosition
|
||||
};
|
||||
Reference in New Issue
Block a user