mathpix-markdown-it
Version:
Mathpix-markdown-it is an open source implementation of the mathpix-markdown spec written in Typescript. It relies on the following open source libraries: MathJax v3 (to render math with SVGs), markdown-it (for standard Markdown parsing)
1,081 lines (1,080 loc) • 51 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
// we use the drawer to do all the preprocessing. then we take over the drawing
// portion to output to svg
var ArrayHelper_1 = require("./ArrayHelper");
var Atom_1 = require("./Atom");
var Drawer_1 = require("./Drawer");
var Line_1 = require("./Line");
var SvgWrapper_1 = require("./SvgWrapper");
var ThemeManager_1 = require("./ThemeManager");
var Vector2_1 = require("./Vector2");
var utils_1 = require("../../../utils");
var SvgDrawer = /** @class */ (function () {
function SvgDrawer(options) {
this.preprocessor = new Drawer_1.default(options);
}
/**
* Draws the parsed smiles data to an svg element.
*
* @param {Object} data The tree returned by the smiles parser.
* @param {(String|HTMLElement)} target The id of the HTML svg element the structure is drawn to - or the element itself.
* @param {String} themeName='dark' The name of the theme to use. Built-in themes are 'light' and 'dark'.
* @param {Boolean} infoOnly=false Only output info on the molecule without drawing anything to the canvas.
* @returns {Oject} The dimensions of the drawing in { width, height }
*/
SvgDrawer.prototype.draw = function (data, target, themeName, infoOnly) {
if (themeName === void 0) { themeName = 'light'; }
if (infoOnly === void 0) { infoOnly = false; }
var preprocessor = this.preprocessor;
preprocessor.initDraw(data, themeName, infoOnly);
if (!infoOnly) {
this.themeManager = new ThemeManager_1.default(this.preprocessor.opts.themes, themeName);
this.svgWrapper = new SvgWrapper_1.default(this.themeManager, target, this.preprocessor.opts);
}
preprocessor.processGraph();
// Set the canvas to the appropriate size
this.svgWrapper.determineDimensions(preprocessor.graph.vertices);
this.putEdgesForRings();
//need to checking of correct aromatic ring
this.putEdgesLForRings();
this.checkAllEdgesRings();
this.drawEdges(preprocessor.opts.debug);
this.drawVertices(preprocessor.opts.debug);
if (preprocessor.opts.debug) {
console.log(preprocessor.graph);
console.log(preprocessor.rings);
console.log(preprocessor.ringConnections);
}
return this.svgWrapper.constructSvg();
};
SvgDrawer.prototype.putEdgesForRings = function () {
var preprocessor = this.preprocessor;
var rings = preprocessor.rings;
var graph = preprocessor.graph;
var edges = preprocessor.graph.edges;
var _loop_1 = function (i) {
var ring = rings[i];
ring.membersS = this_1.sortMembers(ring);
if (ring.neighbours.length) {
ring.neighbours.map(function (item) {
var nRing = rings[item];
if (nRing && nRing.neighbours.indexOf(ring.id) === -1) {
nRing.neighbours.push(ring.id);
}
});
}
var members = ring.membersS;
for (var j = 0; j < members.length; j++) {
var v = members[j];
var vertex = graph.vertices[v];
if (this_1.isHydrogenVertices([v])) {
ring.hasHydrogen = true;
}
if (this_1.isVertexHasDoubleBondWithO(ring, v)) {
ring.hasDoubleBondWithO = true;
}
if (!ring.isHaveElements && vertex.value.element !== 'C') {
ring.isHaveElements = true;
}
ring.elements.push(vertex.value.element);
var v2 = j < members.length - 1
? members[j + 1]
: members[0];
var _a = this_1.getEdgeBetweenVertexAB(v, v2), item = _a.item, isBetweenRings = _a.isBetweenRings;
var edge = edges[item];
if (edge) {
var vertexA = graph.vertices[edge.sourceId];
var vertexB = graph.vertices[edge.targetId];
if (!this_1.edgeHaveDoubleBound(edge) && vertexA.neighbourCount > 2
&& vertexA.neighbourCount < vertexA.value.bondCount) {
edge.sourceHasOuterDoubleBond = true;
ring.hasOuterDoubleBond = true;
}
if (!this_1.edgeHaveDoubleBound(edge) && vertexB.neighbourCount > 2
&& vertexB.neighbourCount < vertexB.value.bondCount) {
edge.targetHasOuterDoubleBond = true;
ring.hasOuterDoubleBond = true;
}
}
if (edge === null || edge === void 0 ? void 0 : edge.isPartOfAromaticRing) {
if (ring.edges.indexOf(item) === -1) {
if (ring.edges.length > 0) {
var prev = edges[ring.edges[ring.edges.length - 1]];
if (prev.neighbours.indexOf(item) === -1) {
prev.neighbours.push(item);
}
if (edge.neighbours.indexOf(prev.id) === -1) {
edge.neighbours.push(prev.id);
}
}
ring.edges.push(item);
edge.isPartOfRing = true;
if (edge.rings.indexOf(ring.id) === -1) {
edge.rings.push(ring.id);
}
}
if (isBetweenRings) {
if (ring.edgesR.indexOf(item) === -1) {
ring.edgesR.push(item);
}
}
}
}
//Edit last and first Edges
if (ring.edges.length > 0) {
var first = edges[ring.edges[0]];
var last = edges[ring.edges[ring.edges.length - 1]];
if (first.neighbours.indexOf(last.id) === -1) {
first.neighbours.push(last.id);
}
if (last.neighbours.indexOf(first.id) === -1) {
last.neighbours.push(first.id);
}
}
if (ring.edgesR.length) {
var arr_1 = tslib_1.__spreadArray([], tslib_1.__read(ring.edges), false);
ring.edgesR.map(function (item) {
var index = arr_1.indexOf(item);
if (index > -1) {
arr_1.splice(index, 1);
}
});
if (!arr_1.length || arr_1.length < 1) {
ring.edges = [];
}
}
};
var this_1 = this;
for (var i = 0; i < rings.length; i++) {
_loop_1(i);
}
};
;
SvgDrawer.prototype.putEdgesLForRings = function () {
var preprocessor = this.preprocessor;
var rings = preprocessor.rings;
for (var i = 0; i < rings.length; i++) {
var ring = rings[i];
if (this.isRing_SNN(ring)) {
continue;
}
if (this.isRing_ONN(ring)) {
continue;
}
if (this.isRing_NNN(ring)) {
continue;
}
}
};
;
SvgDrawer.prototype.checkAllEdgesRings = function () {
var preprocessor = this.preprocessor;
var rings = preprocessor.rings;
var graph = preprocessor.graph;
var edges = preprocessor.graph.edges;
var arr = [];
var arrBottoms = [];
for (var i = 0; i < edges.length; i++) {
var edge = edges[i];
if (edge.rings.length > 1) {
if (arr.indexOf(edge.id) === -1) {
arr.push(edge.id);
}
}
if (this.edgeHaveDoubleBound(edge)) {
edge.isHaveLine = true;
edge.isChecked = true;
continue;
}
var currentRing = edge.rings.length === 1
? rings[edge.rings]
: null;
var vertexA = graph.vertices[edge.sourceId];
var vertexB = graph.vertices[edge.targetId];
if (currentRing && !this.ringStartedCheck(currentRing)) {
if (this.ringStartedCheckForEdge(currentRing, edge)) {
edge.isChecked = true;
currentRing.isStartedCheck = true;
continue;
}
if (!edge.isAtomSlat &&
currentRing.members.length === 6 && this.ringHasAtoms_N(currentRing)
&& edge.sourceId === Math.min.apply(Math, tslib_1.__spreadArray([], tslib_1.__read(currentRing.members), false))
&& vertexB.value.element === 'N' &&
currentRing.elements.filter(function (item) { return item === 'N'; }).length > 2) {
edge.isChecked = true;
currentRing.isStartedCheck = true;
continue;
}
}
if ((edge.isBottomSlat && (!edge.isBeforeHaveLine || edge.rings.length > 1)
|| this.edgeNeighboursAreAtomSlat(edge)
|| (currentRing && currentRing.members.length >= 5 && edge.rings.length === 1
&& (this.getVertexElementFromRing(currentRing) !== '' || currentRing.members.length === 6)
&& vertexA.value.rings.length > 1 && vertexB.value.rings.length > 1))
&& !(vertexA.value.element === 'N' && vertexB.value.element === 'N' && (currentRing === null || currentRing === void 0 ? void 0 : currentRing.members.length) > 5)) {
if (edge.isBottomSlat && currentRing && currentRing.neighbours.length === 1
&& rings[currentRing.neighbours[0]].members.length === 5
&& this.getVertexElementFromRing(rings[currentRing.neighbours[0]]) === 'N'
&& currentRing.members.length === 6 && this.getVertexElementFromRing(currentRing) === 'N') {
}
else {
if (arrBottoms.indexOf(edge.id) === -1) {
arrBottoms.push(edge.id);
}
if (currentRing) {
currentRing.isStartedCheck = true;
}
continue;
}
}
if (edge.isAtomVertex) {
continue;
}
if (!edge.isPartOfRing || edge.isChecked) {
continue;
}
if (!edge.isChecked) {
this.checkEdge(edge, vertexA, vertexB, arr, currentRing);
if (currentRing) {
currentRing.isStartedCheck = true;
}
}
}
if (this.preprocessor.opts.debug) {
console.log('arr=>', arr);
}
if (arr.length) {
for (var i = 0; i < arr.length; i++) {
var edge = edges[arr[i]];
if (edge.isChecked && (edge.isHaveLine || edge.isNotHaveLine)) {
continue;
}
var vertexA = graph.vertices[edge.targetId];
var vertexB = graph.vertices[edge.sourceId];
if (this.edgeNeighboursHaveDoubleBound(edge, vertexA, vertexB)
|| (vertexA.value.element === 'N' && vertexA.neighbourCount === 3)
|| (vertexB.value.element === 'N' && vertexB.neighbourCount === 3)) {
edge.isNotHaveLine = true;
continue;
}
edge.isHaveLine = true;
edge.isChecked = true;
}
}
if (this.preprocessor.opts.debug) {
console.log('arrBottoms=>', arrBottoms);
}
if (arrBottoms.length) {
for (var i = 0; i < arrBottoms.length; i++) {
var edge = edges[arrBottoms[i]];
if (edge.isChecked) {
continue;
}
var vertexA = graph.vertices[edge.targetId];
var vertexB = graph.vertices[edge.sourceId];
if (this.edgeNeighboursHaveDoubleBound(edge, vertexA, vertexB)
|| (vertexA.value.element === 'N' && vertexA.neighbourCount === 3 && vertexA.neighbourCount !== vertexA.value.bondCount)
|| (vertexB.value.element === 'N' && vertexB.neighbourCount === 3 && vertexB.neighbourCount !== vertexB.value.bondCount)) {
edge.isNotHaveLine = true;
continue;
}
edge.isHaveLine = true;
edge.isChecked = true;
}
}
};
;
SvgDrawer.prototype.checkEdge = function (edge, vertexA, vertexB, arr, ring) {
var _a, _b;
var atoms = ['O', 'S', 'Se', 'As'];
var vertexA_NotOS = atoms.indexOf(vertexA.value.element) === -1;
var vertexB_NotOS = atoms.indexOf(vertexB.value.element) === -1;
var vertexA_NotN = !(vertexA.value.element === 'N' && vertexA.neighbourCount === 3 && !(((_a = vertexA.value.bracket) === null || _a === void 0 ? void 0 : _a.charge) === 1)
&& !this.checkVertex_N(vertexA));
var vertexB_NotN = !(vertexB.value.element === 'N' && vertexB.neighbourCount === 3 && !(((_b = vertexB.value.bracket) === null || _b === void 0 ? void 0 : _b.charge) === 1)
&& !this.checkVertex_N(vertexB));
if (this.edgeHaveDoubleBound(edge)) {
edge.isHaveLine = true;
edge.isChecked = true;
if (ring && !ring.isStartedCheck) {
var prev = false;
for (var i = 0; i < ring.edges.length; i++) {
if (ring.edges[i] === edge.id) {
prev = true;
continue;
}
if (!prev) {
var rEdge = this.preprocessor.graph.edges[ring.edges[i]];
rEdge.isBeforeHaveLine = true;
prev = true;
}
else {
prev = false;
}
}
}
return;
}
if (this.checkVertex_N(vertexA)) {
if (!this.edgeNeighboursHaveDoubleBound(edge, vertexA, vertexB)) {
edge.isHaveLine = true;
edge.isChecked = true;
return;
}
}
if (this.edgeHaveDoubleBound(edge) ||
vertexA_NotOS && vertexB_NotOS
&& vertexA_NotN && vertexB_NotN
&& !(vertexA.neighbourCount > 2 && vertexA.neighbourCount < vertexA.value.bondCount)
&& !(vertexB.neighbourCount > 2 && vertexB.neighbourCount < vertexB.value.bondCount)
&& (!vertexA.hasDoubleBondWithO || this.checkVertex_N(vertexA))
&& (!vertexB.hasDoubleBondWithO || this.checkVertex_N(vertexB))
&& !this.isHydrogenVertices([vertexA.id, vertexB.id])
&& !this.edgeNeighboursHaveDoubleBound(edge, vertexA, vertexB)) {
if (!edge.isBeforeNotHaveLine) {
edge.isHaveLine = true;
}
edge.isChecked = true;
if (ring && !ring.isStartedCheck
&& ring.members.length === 6
&& !ring.hasDoubleBondWithO
&& !ring.hasOuterDoubleBond) {
var prev = false;
for (var i = 0; i < ring.edges.length; i++) {
if (ring.edges[i] === edge.id) {
prev = true;
continue;
}
var rEdge = this.preprocessor.graph.edges[ring.edges[i]];
if (!prev) {
rEdge.isBeforeHaveLine = true;
prev = true;
}
else {
rEdge.isBeforeNotHaveLine = true;
prev = false;
}
}
}
}
else {
edge.isNotHaveLine = true;
edge.isChecked = true;
}
};
;
SvgDrawer.prototype.edgeHaveDoubleBound = function (edge, vertexA, vertexB) {
if (vertexA === void 0) { vertexA = null; }
if (vertexB === void 0) { vertexB = null; }
if (!vertexA) {
vertexA = this.preprocessor.graph.vertices[edge.sourceId];
}
if (!vertexB) {
vertexB = this.preprocessor.graph.vertices[edge.targetId];
}
if (edge.bondType === '=' && !edge.isPartOfRing
&& vertexB.value.element === 'N') {
return false;
}
return edge.bondType === '=' || this.preprocessor.getRingbondType(vertexA, vertexB) === '=';
};
SvgDrawer.prototype.edgesHaveDoubleBound = function (edges, checkAtom) {
var res = false;
for (var i = 0; i < edges.length; i++) {
var edge = this.preprocessor.graph.edges[edges[i]];
var vertexA = this.preprocessor.graph.vertices[edge.sourceId];
var vertexB = this.preprocessor.graph.vertices[edge.targetId];
if (edge.bondType === '=' || this.preprocessor.getRingbondType(vertexA, vertexB) === '='
|| (checkAtom && (vertexA.value.element === 'O' ||
vertexA.value.neighbouringElements.indexOf('F') !== -1))) {
res = true;
break;
}
}
return res;
};
SvgDrawer.prototype.edgeNeighboursHaveDoubleBound = function (edge, vertexA, vertexB) {
var res = false;
var eNeighbours = [].concat(vertexA.edges, vertexB.edges);
for (var i = 0; i < eNeighbours.length; i++) {
var edge_1 = this.preprocessor.graph.edges[eNeighbours[i]];
if (edge_1.isHaveLine || this.edgeHaveDoubleBound(edge_1)) {
res = true;
break;
}
}
return res;
};
;
SvgDrawer.prototype.ringHasAtoms_N = function (ring) {
var res = false;
for (var i = 0; i < ring.members.length; i++) {
var vertex = this.preprocessor.graph.vertices[ring.members[i]];
if (vertex.value.element === 'N' && vertex.neighbourCount === 3) {
res = true;
break;
}
}
return res;
};
SvgDrawer.prototype.ringStartedCheck = function (ring) {
var res = false;
for (var i = 0; i < ring.edges.length; i++) {
var edge = this.preprocessor.graph.edges[ring.edges[i]];
if (edge.isChecked) {
res = true;
break;
}
}
return res;
};
SvgDrawer.prototype.isVertexNeighboursHasDoubleBond = function (vertexId, ring) {
var vertexA = this.preprocessor.graph.vertices[vertexId];
var neighbours = vertexA.neighbours.filter(function (item) { return ring.members.indexOf(item) === -1; });
var res = false;
for (var i = 0; i < neighbours.length; i++) {
var vertex = this.preprocessor.graph.vertices[neighbours[i]];
if (this.edgesHaveDoubleBound(vertex.edges, true)) {
res = true;
break;
}
}
return res;
};
SvgDrawer.prototype.getVertexElementFromRing = function (ring) {
var res = '';
for (var i = 0; i < ring.members.length; i++) {
var vertex = this.preprocessor.graph.vertices[ring.members[i]];
if (vertex.isAtomVertex) {
if (this.isHydrogenVertices([vertex.id])) {
res = vertex.value.element + 'H';
}
else {
res = vertex.value.element;
}
break;
}
}
return res;
};
SvgDrawer.prototype.ringStartedCheckForEdge = function (ring, edge) {
var _a, _b, _c;
var res = false;
if (ring.isStartedCheck) {
return false;
}
if (ring.members.length === 6) {
if (ring.neighbours.length === 1) {
var commonEdge = this.preprocessor.graph.edges[ring.edgesR[0]];
if (commonEdge) {
var rN = commonEdge.rings.filter(function (item) { return item !== ring.id; });
var nRing = this.preprocessor.rings[rN[0]];
var index = ring.edges.indexOf(commonEdge.id);
if (this.preprocessor.opts.debug) {
console.log('>>>>>>> commonEdge=> ', commonEdge);
console.log('>>>>>>> index=> ', index);
}
if (index === 0) {
if (!nRing.edges.length) {
return true;
}
}
if (index === 3) {
if (commonEdge.isBottomSlat && ((_a = nRing.members) === null || _a === void 0 ? void 0 : _a.length) === 5) {
return true;
}
if (this.edgeHaveDoubleBound(commonEdge)
|| (commonEdge && commonEdge.isBottomSlat && ((_b = commonEdge === null || commonEdge === void 0 ? void 0 : commonEdge.members) === null || _b === void 0 ? void 0 : _b.length) === 5)) {
res = true;
}
if (commonEdge.isAtomVertex && ((_c = nRing.members) === null || _c === void 0 ? void 0 : _c.length) === 5) {
return true;
}
}
if (index === 2 && commonEdge.isAtomSlat) {
if (this.isVertexNeighboursHasDoubleBond(edge.sourceId, ring)) {
if (nRing.members.length === 6) {
return false;
}
else {
return true;
}
}
}
if (index === 4 && commonEdge.isAtomSlat) {
if (nRing && nRing.members.length === 5
&& nRing.hasHydrogen
&& nRing.elements.indexOf('S') === -1
&& nRing.elements.indexOf('O') === -1) {
return true;
}
}
if ((index === 1) && commonEdge.isAtomSlat) {
if (this.isVertexNeighboursHasDoubleBond(edge.sourceId, ring)) {
res = false;
}
else {
if (nRing.members.length === 5) {
return false;
}
if (index === 4) {
var rN_1 = commonEdge.rings.filter(function (item) { return item !== ring.id; });
var nRing_1 = this.preprocessor.rings[rN_1[0]];
if (nRing_1 && nRing_1.members.length > 5) {
return false;
}
}
res = true;
}
}
}
}
}
return res;
};
SvgDrawer.prototype.edgeNeighboursAreAtomSlat = function (edge) {
var res = false;
if (edge.neighbours.length === 2) {
return this.preprocessor.graph.edges[edge.neighbours[0]].isAtomSlat
&& this.preprocessor.graph.edges[edge.neighbours[1]].isAtomSlat;
}
return res;
};
SvgDrawer.prototype.sortMembers = function (ring) {
var members = tslib_1.__spreadArray([], tslib_1.__read(ring.members), false).sort(function (a, b) { return a - b; });
var arr = [];
var edgesAll = [];
var prev;
for (var i = 0; i < members.length; i++) {
var v = members[i];
if (i === 0) {
arr.push(v);
prev = v;
}
var vertex = this.preprocessor.graph.vertices[prev];
var neighbours = vertex.neighbours.filter(function (item) { return members.indexOf(item) !== -1 && arr.indexOf(item) === -1; });
var vNext = void 0;
if (neighbours === null || neighbours === void 0 ? void 0 : neighbours.length) {
if (neighbours.length > 1) {
vNext = Math.min.apply(Math, tslib_1.__spreadArray([], tslib_1.__read(neighbours), false));
}
else {
vNext = neighbours[0];
}
}
else {
vNext = arr[0];
}
var vertexIds = vertex.id + '_' + vNext;
var ed = this.preprocessor.graph.vertexIdsToEdgeId[vertexIds];
prev = vNext;
if (ed || ed === 0) {
if (edgesAll.indexOf(vNext) === -1) {
edgesAll.push(ed);
}
if (arr.indexOf(vNext) === -1) {
arr.push(vNext);
}
}
}
return arr;
};
SvgDrawer.prototype.getEdgeBetweenVertexAB = function (vA, vB) {
var _a, _b;
var vertexA = this.preprocessor.graph.vertices[vA];
var vertexB = this.preprocessor.graph.vertices[vB];
var edgesA = tslib_1.__spreadArray([], tslib_1.__read(vertexA.edges), false);
var edgesB = tslib_1.__spreadArray([], tslib_1.__read(vertexB.edges), false);
var edges = edgesA.filter(function (i) { return edgesB.indexOf(i) >= 0; });
var isBetweenRings = ((_a = vertexA.value.rings) === null || _a === void 0 ? void 0 : _a.length) > 1
&& ((_b = vertexB.value.rings) === null || _b === void 0 ? void 0 : _b.length) > 1
&& (0, utils_1.arraysCompare)(vertexA.value.rings, vertexB.value.rings);
var rings = isBetweenRings
? vertexA.value.rings
: [];
return {
item: edges[0],
isBetweenRings: isBetweenRings,
bRings: rings
};
};
SvgDrawer.prototype.isRing_SNN = function (ring) {
var _this = this;
var elements = ring.elements;
var graph = this.preprocessor.graph;
var arrE = tslib_1.__spreadArray([], tslib_1.__read(ring.edges), false);
if (elements.length < 5) {
return false;
}
var arr = elements.filter(function (item) { return item === 'N' || item === 'S'; });
if ((arr === null || arr === void 0 ? void 0 : arr.length) > 0 && arr.indexOf('S') !== -1) {
var members_1 = ring.membersS;
var indexS = elements.indexOf('S');
if (indexS !== -1) {
var vS = members_1[indexS];
var vertex = graph.vertices[vS];
vertex.isAtomVertex = true;
vertex.edges.map(function (item) {
if (ring.edges.indexOf(item) !== -1) {
var edge = _this.preprocessor.graph.edges[item];
edge.isAtomVertex = true;
(0, utils_1.arrayDelElement)(arrE, item);
}
});
var neighbours = vertex.neighbours;
neighbours.map(function (item) {
if (members_1.indexOf(item) !== -1) {
var vertexN = graph.vertices[item];
var vertexNEdges = vertexN.edges.filter(function (ed) { return ring.edges.indexOf(ed) !== -1; });
vertexNEdges.map(function (ed) {
var edge = _this.preprocessor.graph.edges[ed];
if (!edge.isAtomVertex) {
edge.isAtomSlat = true;
}
(0, utils_1.arrayDelElement)(arrE, ed);
});
}
});
if (elements.length === 5 && (arrE === null || arrE === void 0 ? void 0 : arrE.length) === 1) {
this.preprocessor.graph.edges[arrE[0]].isBottomSlat = true;
}
else {
if (elements.length === 6) {
arrE.map(function (item) {
_this.preprocessor.graph.edges[item].isBottomSlat = true;
});
}
}
ring.isDrawed = true;
return true;
}
}
return false;
};
SvgDrawer.prototype.isRing_ONN = function (ring) {
var _this = this;
var elements = ring.elements;
var arrE = tslib_1.__spreadArray([], tslib_1.__read(ring.edges), false);
var graph = this.preprocessor.graph;
if (elements.length < 5) {
return false;
}
var arr = elements.filter(function (item) { return item === 'N' || item === 'O'; });
if (((arr === null || arr === void 0 ? void 0 : arr.length) > 0
&& arr.indexOf('O') !== -1)) {
var members_2 = ring.membersS;
var indexS = this.findStartElementbyEdges(members_2, elements, 'O');
if (indexS !== -1) {
var vS = members_2[indexS];
var vertex = graph.vertices[vS];
vertex.isAtomVertex = true;
vertex.edges.map(function (item) {
if (ring.edges.indexOf(item) !== -1) {
var edge = _this.preprocessor.graph.edges[item];
edge.isAtomVertex = true;
(0, utils_1.arrayDelElement)(arrE, item);
}
});
var neighbours = vertex.neighbours.filter(function (item) { return members_2.indexOf(item) !== -1; });
neighbours.map(function (item) {
var vertexN = graph.vertices[item];
vertexN.edges.map(function (ed) {
if (ring.edges.indexOf(ed) !== -1) {
var edge = _this.preprocessor.graph.edges[ed];
if (!edge.isAtomVertex) {
edge.isAtomSlat = true;
}
(0, utils_1.arrayDelElement)(arrE, ed);
}
});
});
}
if (elements.length === 5 && (arrE === null || arrE === void 0 ? void 0 : arrE.length) === 1) {
this.preprocessor.graph.edges[arrE[0]].isBottomSlat = true;
}
else {
if (elements.length === 6) {
arrE.map(function (item) {
_this.preprocessor.graph.edges[item].isBottomSlat = true;
});
}
}
return true;
}
return false;
};
SvgDrawer.prototype.isRing_NNN = function (ring) {
var _this = this;
var elements = ring.elements;
var arrE = tslib_1.__spreadArray([], tslib_1.__read(ring.edges), false);
var graph = this.preprocessor.graph;
if (elements.length < 5) {
return false;
}
if (ring.isDrawed) {
return false;
}
var arr = elements.filter(function (item) { return item === 'N'; });
if ((arr === null || arr === void 0 ? void 0 : arr.length) > 0) {
var members_3 = ring.membersS;
var indexS = this.findStartNbyEdges(members_3, elements).indexS;
var vS = -1;
if (indexS !== -1) {
vS = members_3[indexS];
}
if (vS !== -1) {
var vertex = graph.vertices[vS];
vertex.isAtomVertex = true;
vertex.edges
.filter(function (e, i, a) { return a.indexOf(e) == i; })
.map(function (item) {
if (ring.edges.indexOf(item) !== -1) {
var edge = _this.preprocessor.graph.edges[item];
edge.isAtomVertex = true;
(0, utils_1.arrayDelElement)(arrE, item);
}
});
var neighbours = vertex.neighbours.filter(function (item) { return members_3.indexOf(item) !== -1; });
neighbours.map(function (item) {
var vertexN = graph.vertices[item];
var vEdges = vertexN.edges.filter(function (ed) { return ring.edges.indexOf(ed) !== -1 && arrE.indexOf(ed) !== -1; });
vEdges.map(function (ed) {
var edge = _this.preprocessor.graph.edges[ed];
if (!edge.isAtomVertex) {
edge.isAtomSlat = true;
}
(0, utils_1.arrayDelElement)(arrE, ed);
});
});
if (elements.length === 5 && (arrE === null || arrE === void 0 ? void 0 : arrE.length) === 1) {
this.preprocessor.graph.edges[arrE[0]].isBottomSlat = true;
}
else {
if (elements.length === 6) {
arrE.map(function (item) {
_this.preprocessor.graph.edges[item].isBottomSlat = true;
});
}
}
return true;
}
}
return false;
};
SvgDrawer.prototype.isHydrogenVertices = function (arr) {
var res = false;
for (var i = 0; i < arr.length; i++) {
if (this.preprocessor.graph.vertices[arr[i]].value.bracket
&& Number(this.preprocessor.graph.vertices[arr[i]].value.bracket.hcount) > 0) {
res = true;
break;
}
}
return res;
};
SvgDrawer.prototype.isVertexHasDoubleBondWithO = function (ring, v) {
var res = false;
var vertex = this.preprocessor.graph.vertices[v];
var vEdges = vertex.edges.filter(function (item) { return ring.edges.indexOf(item) === -1; });
if (vEdges.length > 1) {
for (var i = 0; i < vEdges.length; i++) {
var edge = this.preprocessor.graph.edges[vEdges[i]];
var vertexB = this.preprocessor.graph.vertices[edge.targetId];
if (this.edgeHaveDoubleBound(edge) && vertexB.value.element === 'O') {
vertex.hasDoubleBondWithO = true;
res = true;
}
}
}
return res;
};
;
SvgDrawer.prototype.checkVertex_N = function (vertex) {
if (vertex.value.element !== 'N') {
return false;
}
var neighbours = this.preprocessor.graph.vertices[vertex.id].neighbours;
var bonds = 0;
for (var j = 0; j < neighbours.length; j++) {
bonds += this.preprocessor.graph.getEdge(vertex.id, neighbours[j]).weight;
}
if (bonds > vertex.value.getMaxBonds()) {
return true;
}
return false;
};
SvgDrawer.prototype.findStartNbyEdges = function (members, elements) {
var _a;
var res = -1;
var isHydrogen = false;
for (var i = 0; i < elements.length; i++) {
if (elements[i] !== 'N') {
continue;
}
var v = members[i];
var vertex = this.preprocessor.graph.vertices[v];
if (((_a = vertex.value.bracket) === null || _a === void 0 ? void 0 : _a.charge) === 1) {
continue;
}
var edges = vertex.edges.filter(function (e, i, a) { return a.indexOf(e) == i; });
if (this.checkVertex_N(vertex)) {
continue;
}
if (edges.length > 2) {
res = i;
if (vertex.value.rings.length > 1) {
continue;
}
else {
break;
}
}
if (members.length >= 5 && this.isHydrogenVertices([v])) {
res = i;
isHydrogen = true;
break;
}
}
return { indexS: res, isHydrogen: isHydrogen };
};
SvgDrawer.prototype.findStartElementbyEdges = function (members, elements, element) {
var _a;
var res = -1;
for (var i = 0; i < elements.length; i++) {
if (elements[i] !== element) {
continue;
}
var v = members[i];
var vertex = this.preprocessor.graph.vertices[v];
if (((_a = vertex.value.bracket) === null || _a === void 0 ? void 0 : _a.charge) === 1) {
continue;
}
res = i;
}
return res;
};
/**
* Draw the actual edges as bonds.
*
* @param {Boolean} debug A boolean indicating whether or not to draw debug helpers.
*/
SvgDrawer.prototype.drawEdges = function (debug) {
var _this = this;
var preprocessor = this.preprocessor, graph = preprocessor.graph, rings = preprocessor.rings, drawn = Array(this.preprocessor.graph.edges.length);
drawn.fill(false);
graph.traverseBF(0, function (vertex) {
var edges = graph.getEdges(vertex.id);
for (var i = 0; i < edges.length; i++) {
var edgeId = edges[i];
if (!drawn[edgeId]) {
drawn[edgeId] = true;
_this.drawEdge(edgeId, debug);
}
}
});
// Draw ring for implicitly defined aromatic rings
if (!this.preprocessor.bridgedRing
&& this.preprocessor.opts.ringVisualization === 'circle') {
for (var i = 0; i < rings.length; i++) {
var ring = rings[i];
if (preprocessor.isRingAromatic(ring)) {
this.svgWrapper.drawAromaticityRing(ring);
}
}
}
};
/**
* Draw the an edge as a bond.
*
* @param {Number} edgeId An edge id.
* @param {Boolean} debug A boolean indicating whether or not to draw debug helpers.
*/
SvgDrawer.prototype.drawEdge = function (edgeId, debug) {
var preprocessor = this.preprocessor, opts = preprocessor.opts, svgWrapper = this.svgWrapper, edge = preprocessor.graph.edges[edgeId], vertexA = preprocessor.graph.vertices[edge.sourceId], vertexB = preprocessor.graph.vertices[edge.targetId], elementA = vertexA.value.element, elementB = vertexB.value.element;
if ((!vertexA.value.isDrawn || !vertexB.value.isDrawn) && preprocessor.opts.atomVisualization === 'default') {
return;
}
var a = vertexA.position, b = vertexB.position, normals = preprocessor.getEdgeNormals(edge),
// Create a point on each side of the line
sides = ArrayHelper_1.default.clone(normals);
sides[0].multiplyScalar(10).add(a);
sides[0].multiplyScalar(10).add(a);
sides[1].multiplyScalar(10).add(a);
if (edge.bondType === '=' || preprocessor.getRingbondType(vertexA, vertexB) === '=' || this.edgeHaveDoubleBound(edge) ||
(edge.isPartOfAromaticRing && preprocessor.bridgedRing
&& edge.isPartOfRing)
|| (edge.isPartOfRing
&& opts.ringVisualization === 'default')) {
// Always draw double bonds inside the ring
var inRing = preprocessor.areVerticesInSameRing(vertexA, vertexB);
var s = preprocessor.chooseSide(vertexA, vertexB, sides);
if (inRing) {
// Always draw double bonds inside a ring
// if the bond is shared by two rings, it is drawn in the larger
// problem: smaller ring is aromatic, bond is still drawn in larger -> fix this
var lcr = preprocessor.getLargestOrAromaticCommonRing(vertexA, vertexB);
var center = lcr.center;
normals[0].multiplyScalar(opts.bondSpacing);
normals[1].multiplyScalar(opts.bondSpacing);
// Choose the normal that is on the same side as the center
var line = null;
if (center.sameSideAs(vertexA.position, vertexB.position, Vector2_1.default.add(a, normals[0]))) {
line = new Line_1.default(Vector2_1.default.add(a, normals[0]), Vector2_1.default.add(b, normals[0]), elementA, elementB);
}
else {
line = new Line_1.default(Vector2_1.default.add(a, normals[1]), Vector2_1.default.add(b, normals[1]), elementA, elementB);
}
line.shorten(opts.bondLength - opts.shortBondLength * opts.bondLength);
// The shortened edge
if (edge.isPartOfAromaticRing) {
if (opts.ringAromaticVisualization === 'dashed' && preprocessor.bridgedRing) {
svgWrapper.drawLine(line, true);
}
else {
if (((edge.isHaveLine && !edge.isNotHaveLine)
|| (edge.bondType === '=' || this.edgeHaveDoubleBound(edge)))) {
svgWrapper.drawLine(line);
}
}
}
else {
svgWrapper.drawLine(line);
}
svgWrapper.drawLine(new Line_1.default(a, b, elementA, elementB));
}
else if ((edge.center || vertexA.isTerminal() && vertexB.isTerminal()) ||
(s.anCount == 0 && s.bnCount > 1 || s.bnCount == 0 && s.anCount > 1)) {
this.multiplyNormals(normals, opts.halfBondSpacing);
var lineA = new Line_1.default(Vector2_1.default.add(a, normals[0]), Vector2_1.default.add(b, normals[0]), elementA, elementB), lineB = new Line_1.default(Vector2_1.default.add(a, normals[1]), Vector2_1.default.add(b, normals[1]), elementA, elementB);
svgWrapper.drawLine(lineA);
svgWrapper.drawLine(lineB);
}
else if ((s.sideCount[0] > s.sideCount[1]) ||
(s.totalSideCount[0] > s.totalSideCount[1])) {
this.multiplyNormals(normals, opts.bondSpacing);
var line = new Line_1.default(Vector2_1.default.add(a, normals[0]), Vector2_1.default.add(b, normals[0]), elementA, elementB);
line.shorten(opts.bondLength - opts.shortBondLength * opts.bondLength);
svgWrapper.drawLine(line);
svgWrapper.drawLine(new Line_1.default(a, b, elementA, elementB));
}
else if ((s.sideCount[0] < s.sideCount[1]) ||
(s.totalSideCount[0] <= s.totalSideCount[1])) {
this.multiplyNormals(normals, opts.bondSpacing);
var line = new Line_1.default(Vector2_1.default.add(a, normals[1]), Vector2_1.default.add(b, normals[1]), elementA, elementB);
line.shorten(opts.bondLength - opts.shortBondLength * opts.bondLength);
svgWrapper.drawLine(line);
svgWrapper.drawLine(new Line_1.default(a, b, elementA, elementB));
}
}
else if ((edge === null || edge === void 0 ? void 0 : edge.bondType) === '#') {
normals[0].multiplyScalar(opts.bondSpacing / 1.5);
normals[1].multiplyScalar(opts.bondSpacing / 1.5);
var lineA = new Line_1.default(Vector2_1.default.add(a, normals[0]), Vector2_1.default.add(b, normals[0]), elementA, elementB);
var lineB = new Line_1.default(Vector2_1.default.add(a, normals[1]), Vector2_1.default.add(b, normals[1]), elementA, elementB);
svgWrapper.drawLine(lineA);
svgWrapper.drawLine(lineB);
svgWrapper.drawLine(new Line_1.default(a, b, elementA, elementB));
}
else if ((edge === null || edge === void 0 ? void 0 : edge.bondType) === '.') {
// TODO: Something... maybe... version 2?
}
else {
var isChiralCenterA = vertexA.value.isStereoCenter;
var isChiralCenterB = vertexB.value.isStereoCenter;
if (edge.wedge === 'up') {
svgWrapper.drawWedge(new Line_1.default(a, b, elementA, elementB, isChiralCenterA, isChiralCenterB));
}
else if (edge.wedge === 'down') {
svgWrapper.drawDashedWedge(new Line_1.default(a, b, elementA, elementB, isChiralCenterA, isChiralCenterB));
}
else {
svgWrapper.drawLine(new Line_1.default(a, b, elementA, elementB, isChiralCenterA, isChiralCenterB));
}
}
if (debug) {
var midpoint = Vector2_1.default.midpoint(a, b);
svgWrapper.drawDebugText(midpoint.x, midpoint.y, 'e: ' + edgeId);
}
};
SvgDrawer.prototype.allNeighboursHasDoubleLine = function (vertex) {
var edges = vertex.edges;
if (edges.length < 2 || vertex.value.rings.length) {
return false;
}
var res = true;
for (var i = 0; i < edges.length; i++) {
var edge = this.preprocessor.graph.edges[edges[i]];
if (edge.bondType !== '=') {
res = false;
break;
}
}
return res;
};
/**
* Draws the vertices representing atoms to the canvas.
*
* @param {Boolean} debug A boolean indicating whether or not to draw debug messages to the canvas.
*/
SvgDrawer.prototype.drawVertices = function (debug) {
var preprocessor = this.preprocessor, opts = preprocessor.opts, graph = preprocessor.graph, rings = preprocessor.rings, svgWrapper = this.svgWrapper;
var i = graph.vertices.length;
for (var i = 0; i < graph.vertices.length; i++) {
var vertex = graph.vertices[i];
var atom = vertex.value;
var charge = 0;
var isotope = 0;
var bondCount = vertex.value.bondCount;
var element = atom.element;
var hydrogens = Atom_1.default.maxBonds[element] - bondCount;
var dir = vertex.getTextDirection(graph.vertices);
var isShowC = element === 'C' && this.allNeighboursHasDoubleLine(vertex);
var isTerminal = opts.terminalCarbons || element !== 'C' || atom.hasAttachedPseudoElements ? vertex.isTerminal() : false;
var isCarbon = atom.element === 'C';
// This is a HACK to remove all hydrogens from nitrogens in aromatic rings, as this
// should be the most common state. This has to be fixed by kekulization
if (atom.element === 'N' && atom.isPartOfAromaticRing) {
hydrogens = 0;
}
if (atom.bracket) {
hydrogens = atom.bracket.hcount;
charge = atom.bracket.charge;
isotope = atom.bracket.isotope;
}
if (opts.atomVisualization === 'allballs') {
svgWrapper.drawBall(vertex.position.x, vertex.position.y, element);
}
else if ((atom.isDrawn && (!isCarbon || atom.drawExplicit || isTerminal || atom.hasAttachedPseudoElements) || isShowC) || graph.vertices.length === 1) {
if (opts.atomVisualization === 'default') {
var isCentre = atom.hasPseudoElements && vertex.neighbours.length === 4 && !vertex.value.rings.length;
svgWrapper.drawText(vertex.position.x, vertex.position.y, element, hydrogens, dir, isTerminal, charge, isotope, atom.getAttachedPseudoElements(), isCentre);
}
else if (opts.atomVisualization === 'balls') {
svgWrapper.drawBall(vertex.position.x, vertex.position.y, element);
}
}
else if (vertex.getNeighbourCount() === 2 && vertex.forcePositioned == true) {
// If there is a carbon which bonds are in a straight line, draw a dot
var a = graph.vertices[vertex.neighbours[0]].position;
var b = graph.vertices[vertex.neighbours[1]].position;
var angle = Vector2_1.default.threePointangle(vertex.position, a, b);
if (Math.abs(Math.PI - angle) < 0.1) {
svgWrapper.drawPoint(vertex.position.x, vertex.position.y, element);
}
}
if (debug) {
var value = 'v: ' + vertex.id + ' ' + ArrayHelper_1.default.print(atom.ringbonds);
svgWrapper.drawDebugText(vertex.position.x, vertex.position.y, value);
}
else {
svgWrapper.drawDebugText(vertex.position.x, vertex.position.y, vertex.value.chirality);
}
}
// Draw the ring centers for debug purposes
if (opts.debug) {
for (var i = 0; i < rings.length; i++) {
var center = rings[i].center;
svgWrapper.drawDebugPoint(center.x, center