Skip to content

Commit

Permalink
#632 Adding a stereo flag when opening the structure (#655)
Browse files Browse the repository at this point in the history
* Move isCorrectStereoCenter func to core

* Move isStereoCanter from Struct to helpers/utils

* Rename ValidateStereo tp StereoValidator
  • Loading branch information
MagomedGasanov authored Jul 19, 2021
1 parent 049551b commit 3295523
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 90 deletions.
15 changes: 15 additions & 0 deletions packages/ketcher-core/src/domain/entities/Struct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ import { SimpleObject } from './SimpleObject'
import { Text } from './Text'
import { Vec2 } from './Vec2'

export type Neighbor = {
aid: number
bid: number
}

function arrayAddIfMissing(array, item) {
for (var i = 0; i < array.length; ++i) {
if (array[i] === item) return false
Expand Down Expand Up @@ -923,6 +928,16 @@ export class Struct {
}
}

atomGetNeighbors(aid: number): Array<Neighbor> | undefined {
return this.atoms.get(aid)?.neighbors.map(nei => {
const hb = this.halfBonds.get(nei)!
return {
aid: hb.end,
bid: hb.bid
}
})
}

getComponents() {
// eslint-disable-line max-statements
/* saver */
Expand Down
1 change: 1 addition & 0 deletions packages/ketcher-core/src/domain/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './Scale'
export * from './utils/StereoValidator'
49 changes: 49 additions & 0 deletions packages/ketcher-core/src/domain/helpers/utils/StereoValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Bond } from '../../entities/Bond'
import { Struct, Neighbor } from '../../entities/Struct'

function isCorrectStereoCenter(
bond: Bond,
beginNeighs: Array<Neighbor> | undefined,
endNeighs: Array<Neighbor> | undefined,
struct: Struct
) {
const beginAtom = struct.atoms.get(bond.begin)

let EndAtomNeigh: number | undefined = NaN

if (endNeighs?.length === 2) {
EndAtomNeigh =
endNeighs[0].aid === bond.begin ? endNeighs[1].aid : endNeighs[0].aid
}

if (bond.stereo > 0) {
if (
endNeighs?.length === 1 &&
beginNeighs?.length === 2 &&
Number(beginAtom?.implicitH) % 2 === 0
) {
return false
}

if (
endNeighs?.length === 2 &&
beginNeighs?.length === 2 &&
Number(beginAtom?.implicitH) % 2 === 0 &&
struct.atomGetNeighbors(EndAtomNeigh)?.length === 1
) {
return false
}

if (beginNeighs?.length === 1) {
return false
}

return true
} else {
return false
}
}

export const StereoValidator = {
isCorrectStereoCenter
}
22 changes: 22 additions & 0 deletions packages/ketcher-core/src/domain/serializers/mol/Molfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { Elements } from 'domain/constants'
import common from './common'
import utils from './utils'

import { StereoValidator } from 'domain/helpers/utils/StereoValidator'

const END_V2000 = '2D 1 1.00000 0.00000 0'

type Mapping = {
Expand Down Expand Up @@ -52,7 +54,27 @@ export class Molfile {
}
ret.initHalfBonds()
ret.initNeighbors()
ret.setImplicitHydrogen()

ret.bonds.forEach(bond => {
const beginNeighs = ret.atomGetNeighbors(bond.begin)
const endNeighs = ret.atomGetNeighbors(bond.end)
if (bond.stereo) {
if (
!StereoValidator.isCorrectStereoCenter(
bond,
beginNeighs,
endNeighs,
ret
)
) {
ret.atoms.get(bond.begin).stereoLabel = null
}
}
})

ret.markFragments()

return ret
}

Expand Down
15 changes: 5 additions & 10 deletions packages/ketcher-react/src/script/editor/actions/atom.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,7 @@ import {
SGroupAtomAdd,
CalcImplicitH
} from '../operations'
import {
atomGetAttr,
atomGetDegree,
atomGetNeighbors,
atomGetSGroups
} from './utils'
import { atomGetAttr, atomGetDegree, atomGetSGroups } from './utils'
import { fromRGroupFragment, fromUpdateIfThen } from './rgroup'
import { removeAtomFromSgroupIfNeeded, removeSgroupIfNeeded } from './sgroup'
import { fromBondStereoUpdate } from './bond'
Expand Down Expand Up @@ -72,11 +67,11 @@ export function fromAtomsAttrs(restruct, ids, attrs, reset) {

switch (key) {
case 'stereoLabel':
if ('stereoLabel' in attrs)
if (key in attrs && value)
action.addOp(new AtomAttr(aid, key, value).perform(restruct))
break
case 'stereoParity':
if ('stereoParity' in attrs)
if (key in attrs && value)
action.addOp(new AtomAttr(aid, key, value).perform(restruct))
break
default:
Expand All @@ -96,7 +91,7 @@ export function fromAtomsAttrs(restruct, ids, attrs, reset) {

action.addOp(new CalcImplicitH([aid]).perform(restruct))

const atomNeighbors = atomGetNeighbors(restruct, aid)
const atomNeighbors = restruct.molecule.atomGetNeighbors(aid)
const bond = restruct.molecule.bonds.get(atomNeighbors[0]?.bid)
if (bond) {
action.mergeWith(fromBondStereoUpdate(restruct, bond))
Expand Down Expand Up @@ -165,7 +160,7 @@ export function fromAtomMerge(restruct, srcId, dstId) {

const action = new Action()

const atomNeighbors = atomGetNeighbors(restruct, srcId)
const atomNeighbors = restruct.molecule.atomGetNeighbors(srcId)
atomNeighbors.forEach(nei => {
const bond = restruct.molecule.bonds.get(nei.bid)

Expand Down
79 changes: 21 additions & 58 deletions packages/ketcher-react/src/script/editor/actions/bond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@
* limitations under the License.
***************************************************************************/

import { Atom, Bond, StereoLabel, Vec2 } from 'ketcher-core'
import {
Atom,
Bond,
StereoLabel,
Vec2,
Neighbor,
StereoValidator
} from 'ketcher-core'
import {
AtomAdd,
AtomAttr,
Expand All @@ -24,7 +31,7 @@ import {
CalcImplicitH,
FragmentAdd
} from '../operations'
import { atomForNewBond, atomGetAttr, atomGetNeighbors } from './utils'
import { atomForNewBond, atomGetAttr } from './utils'
import {
fromAtomMerge,
fromStereoAtomAttrs,
Expand Down Expand Up @@ -179,11 +186,6 @@ function fromBondFlipping(restruct: ReStruct, id: number): Action {
return action.perform(restruct)
}

type Neighbor = {
aid: Number
bid: Number
}

export function fromBondStereoUpdate(
restruct: ReStruct,
bond: Bond,
Expand Down Expand Up @@ -218,13 +220,21 @@ export function fromBondStereoUpdate(

fragmentStereoBonds.forEach((bond: Bond | undefined) => {
if (bond) {
const beginNeighs: Array<Neighbor> = atomGetNeighbors(
restruct,
const beginNeighs: Array<Neighbor> | undefined = struct.atomGetNeighbors(
bond.begin
)
const endNeighs: Array<Neighbor> = atomGetNeighbors(restruct, bond.end)
const endNeighs: Array<Neighbor> | undefined = struct.atomGetNeighbors(
bond.end
)

if (isCorrectStereoCenter(bond, beginNeighs, endNeighs, restruct)) {
if (
StereoValidator.isCorrectStereoCenter(
bond,
beginNeighs,
endNeighs,
struct
)
) {
const stereoLabel = struct.atoms.get(bond.begin)?.stereoLabel
if (
stereoLabel == null ||
Expand Down Expand Up @@ -273,53 +283,6 @@ export function fromBondStereoUpdate(
return action
}

// TODO the function for calculating the stereocenter should be for the atom, not for bond
function isCorrectStereoCenter(
bond: Bond,
beginNeighs: Array<Neighbor>,
endNeighs: Array<Neighbor>,
restruct: ReStruct
) {
const struct = restruct.molecule
const beginAtom = struct.atoms.get(bond.begin)

let EndAtomNeigh: Number = NaN

if (endNeighs.length === 2) {
EndAtomNeigh =
endNeighs[0].aid === bond.begin ? endNeighs[1].aid : endNeighs[0].aid
}

if (bond.stereo > 0) {
if (
endNeighs.length === 1 &&
beginNeighs.length === 2 &&
Number(beginAtom?.implicitH) % 2 === 0
) {
return false
}

if (bond.stereo > 0) {
if (
endNeighs.length === 2 &&
beginNeighs.length === 2 &&
Number(beginAtom?.implicitH) % 2 === 0 &&
atomGetNeighbors(restruct, EndAtomNeigh).length === 1
) {
return false
}
}

if (beginNeighs.length === 1) {
return false
}

return true
} else {
return false
}
}

function getStereoParity(stereo: Number): number | null {
let newAtomParity: number | null = null
switch (stereo) {
Expand Down
4 changes: 2 additions & 2 deletions packages/ketcher-react/src/script/editor/actions/erase.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
TextDelete
} from '../operations'
import { fromBondStereoUpdate } from '../actions/bond'
import { atomGetDegree, atomGetNeighbors } from './utils'
import { atomGetDegree } from './utils'
import {
fromSgroupDeletion,
removeAtomFromSgroupIfNeeded,
Expand Down Expand Up @@ -116,7 +116,7 @@ export function fromFragmentDeletion(restruct, selection) {
})

selection.atoms.forEach(aid => {
atomGetNeighbors(restruct, aid).forEach(nei => {
restruct.molecule.atomGetNeighbors(aid).forEach(nei => {
if (selection.bonds.indexOf(nei.bid) === -1) {
selection.bonds = selection.bonds.concat([nei.bid])
}
Expand Down
4 changes: 2 additions & 2 deletions packages/ketcher-react/src/script/editor/actions/fragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
* limitations under the License.
***************************************************************************/
import { Pile, RGroup, Vec2 } from 'ketcher-core'
import { atomGetNeighbors, getRelSgroupsBySelection } from './utils'
import { getRelSgroupsBySelection } from './utils'
import { fromRGroupFragment, fromUpdateIfThen } from './rgroup'

import Action from '../shared/action'
Expand Down Expand Up @@ -158,7 +158,7 @@ function processAtom(restruct, aid, frid, newfrid) {
while (queue.length > 0) {
const id = queue.shift()

atomGetNeighbors(restruct, id).forEach(nei => {
restruct.molecule.atomGetNeighbors(id).forEach(nei => {
if (
restruct.molecule.atoms.get(nei.aid).fragment === frid &&
!usedIds.has(nei.aid)
Expand Down
16 changes: 3 additions & 13 deletions packages/ketcher-react/src/script/editor/actions/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,6 @@ export function atomGetDegree(restruct, aid) {
return restruct.atoms.get(aid).a.neighbors.length
}

export function atomGetNeighbors(restruct, aid) {
return restruct.atoms.get(aid)?.a.neighbors.map(nei => {
const hb = restruct.molecule.halfBonds.get(nei)
return {
aid: hb.end,
bid: hb.bid
}
})
}

export function atomGetSGroups(restruct, aid) {
return Array.from(restruct.atoms.get(aid).a.sgs)
}
Expand Down Expand Up @@ -71,7 +61,7 @@ export function atomForNewBond(restruct, id) {
const neighbours = []
const pos = atomGetPos(restruct, id)

atomGetNeighbors(restruct, id).forEach(nei => {
restruct.molecule.atomGetNeighbors(id).forEach(nei => {
const neiPos = atomGetPos(restruct, nei.aid)

if (Vec2.dist(pos, neiPos) < 0.1) return
Expand Down Expand Up @@ -112,14 +102,14 @@ export function atomForNewBond(restruct, id) {
maxAngle = -((4 * Math.PI) / 3)

// zig-zag
const nei = atomGetNeighbors(restruct, id)[0]
const nei = restruct.molecule.atomGetNeighbors(id)[0]
if (atomGetDegree(restruct, nei.aid) > 1) {
const neiNeighbours = []
const neiPos = atomGetPos(restruct, nei.aid)
const neiV = Vec2.diff(pos, neiPos)
const neiAngle = Math.atan2(neiV.y, neiV.x)

atomGetNeighbors(restruct, nei.aid).forEach(neiNei => {
restruct.molecule.atomGetNeighbors(nei.aid).forEach(neiNei => {
const neiNeiPos = atomGetPos(restruct, neiNei.aid)

if (neiNei.bid === nei.bid || Vec2.dist(neiPos, neiNeiPos) < 0.1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* limitations under the License.
***************************************************************************/

import { BaseOperation } from '../base'
import { OperationType } from '../OperationType'
import { BaseOperation } from '../base'
import { OperationType } from '../OperationType'
import Restruct from '../../../render/restruct'

type Data = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
* limitations under the License.
***************************************************************************/

import Restruct, { ReBond } from '../../../render/restruct'
import Restruct, { ReBond } from '../../../render/restruct'

import { BaseOperation } from '../base'
import { BaseOperation } from '../base'
import { Bond } from 'ketcher-core'
import { OperationType } from '../OperationType'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class CalcImplicitH extends BaseOperation {
atomIds: Array<number>

constructor(aids: Array<number>) {
super(OperationType.CALC_IMPLICIT_H, 5)
super(OperationType.CALC_IMPLICIT_H, 10)
this.atomIds = aids
}

Expand Down

0 comments on commit 3295523

Please sign in to comment.