Skip to content

Commit

Permalink
#311 convert classes syntax to es6, separate operations to files
Browse files Browse the repository at this point in the history
  • Loading branch information
tomas-light committed Feb 17, 2021
1 parent 33f7eb6 commit 69a8c11
Show file tree
Hide file tree
Showing 41 changed files with 2,342 additions and 1,482 deletions.
2 changes: 1 addition & 1 deletion packages/ketcher-react/src/script/chem/molfile/molfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ class Molfile {
this.writePaddedNumber(q, 3)
this.writeCR()

const parentId = this.molecule.sGroupForest.parent.get(id)
const parentId = this.molecule.sGroupForest.parent.get(id) as number
if (parentId >= 0) {
this.write('M SPL')
this.writePaddedNumber(1, 3)
Expand Down
1 change: 1 addition & 0 deletions packages/ketcher-react/src/script/chem/struct/atom.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function Atom(params) {
ifDef(this, params, 'stereoParity', def('stereoParity')) // {string | null} "<abs|and|or>-<group>"

this.atomList = params.atomList ? new AtomList(params.atomList) : null
/** @type {number[]} */
this.neighbors = [] // set of half-bonds having this atom as their origin
this.badConn = false
}
Expand Down
3 changes: 2 additions & 1 deletion packages/ketcher-react/src/script/chem/struct/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function Struct() {
this.atoms = new Pool()
this.bonds = new Pool()
this.sgroups = new Pool()
/** @type {Pool<HalfBond>} */
this.halfBonds = new Pool()
this.loops = new Pool()
this.isReaction = false
Expand Down Expand Up @@ -464,7 +465,7 @@ Struct.prototype.simpleObjectSetPos = function (id, pos) {
}

/**
* @param atomSet { Pile<number> }
* @param [atomSet] { Pile<number> }
* @returns {*}
*/
Struct.prototype.getCoordBoundingBox = function (atomSet) {
Expand Down
243 changes: 130 additions & 113 deletions packages/ketcher-react/src/script/chem/struct/sgforest.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,138 +17,155 @@
import Pile from '../../util/pile'
import SGroup from './sgroup'

function SGroupForest() {
this.parent = new Map() // child id -> parent id
this.children = new Map() // parent id -> list of child ids
this.children.set(-1, []) // extra root node
this.atomSets = new Map()
}

// returns an array or s-group ids in the order of breadth-first search
SGroupForest.prototype.getSGroupsBFS = function () {
const order = []
let id = -1
let queue = Array.from(this.children.get(-1))
while (queue.length > 0) {
id = queue.shift()
queue = queue.concat(this.children.get(id))
order.push(id)
class SGroupForest {
constructor() {
/** node id -> parent id
* @type {Map<number, number>}
* */
this.parent = new Map()
/** node id -> list of child ids
* @type {Map<number, number[]>}
* */
this.children = new Map()

this.children.set(-1, []) // extra root node
this.atomSets = new Map()
}
return order
}

export function checkOverlapping(struct, atoms) {
const sgroups = atoms.reduce((res, aid) => {
const atom = struct.atoms.get(aid)
return res.union(atom.sgs)
}, new Pile())

return Array.from(sgroups).some(sid => {
const sg = struct.sgroups.get(sid)
if (sg.type === 'DAT') return false
const sgAtoms = SGroup.getAtoms(struct, sg)
// returns an array or s-group ids in the order of breadth-first search
getSGroupsBFS() {
const order = []
let id = -1
let queue = Array.from(this.children.get(-1))
while (queue.length > 0) {
id = queue.shift()
queue = queue.concat(this.children.get(id))
order.push(id)
}
return order
}

return sgAtoms.length < atoms.length
? sgAtoms.findIndex(aid => atoms.indexOf(aid) === -1) >= 0
: atoms.findIndex(aid => sgAtoms.indexOf(aid) === -1) >= 0
})
}
getAtomSetRelations(newId, atoms) {
// find the lowest superset in the hierarchy
const isStrictSuperset = new Map()
const isSubset = new Map()

this.atomSets.delete(newId)

this.atomSets.forEach((atomSet, id) => {
isSubset.set(id, atomSet.isSuperset(atoms))
isStrictSuperset.set(
id,
atoms.isSuperset(atomSet) && !atomSet.equals(atoms)
)
})

const parents = Array.from(this.atomSets.keys()).filter(sgid => {
if (!isSubset.get(sgid)) return false
return (
this.children.get(sgid).findIndex(childId => isSubset.get(childId)) < 0
)
})

const children = Array.from(this.atomSets.keys()).filter(
id =>
isStrictSuperset.get(id) && !isStrictSuperset.get(this.parent.get(id))
)

SGroupForest.prototype.getAtomSetRelations = function (newId, atoms) {
// find the lowest superset in the hierarchy
const isStrictSuperset = new Map()
const isSubset = new Map()
return {
children,
parent: parents.length === 0 ? -1 : parents[0]
}
}

this.atomSets.delete(newId)
getPathToRoot(sgid) {
const path = []
for (let id = sgid; id >= 0; id = this.parent.get(id)) {
console.assert(path.indexOf(id) < 0, 'SGroupForest: loop detected')
path.push(id)
}
return path
}

this.atomSets.forEach((atomSet, id) => {
isSubset.set(id, atomSet.isSuperset(atoms))
isStrictSuperset.set(
id,
atoms.isSuperset(atomSet) && !atomSet.equals(atoms)
)
})
/**
* @param {number} [parent]
* @param {number[]} [children]
* */
insert({ id, atoms }, parent, children) {
console.assert(!this.parent.has(id), 'sgid already present in the forest')
console.assert(!this.children.has(id), 'sgid already present in the forest')

if (!parent || !children) {
// if these are not provided, deduce automatically
const guess = this.getAtomSetRelations(id, new Pile(atoms))
parent = guess.parent
children = guess.children
}

// TODO: make children Map<int, Pile> instead of Map<int, []>?
children.forEach(childId => {
this.resetParentLink(childId, id)
})
this.children.set(id, children)
this.parent.set(id, parent)
this.children.get(parent).push(id)
this.atomSets.set(id, new Pile(atoms))

return { parent, children }
}

const parents = Array.from(this.atomSets.keys()).filter(sgid => {
if (!isSubset.get(sgid)) return false
return (
this.children.get(sgid).findIndex(childId => isSubset.get(childId)) < 0
)
})
resetParentLink(childId, id) {
const parentId = this.parent.get(childId)
if (typeof parentId === 'undefined') {
return
}

const children = Array.from(this.atomSets.keys()).filter(
id => isStrictSuperset.get(id) && !isStrictSuperset.get(this.parent.get(id))
)
const childs = this.children.get(parentId)
if (!childs) {
return
}

return {
children,
parent: parents.length === 0 ? -1 : parents[0]
const childIndex = childs.indexOf(childId)
childs.splice(childIndex, 1)
this.parent.set(childId, id)
}
}

SGroupForest.prototype.getPathToRoot = function (sgid) {
const path = []
for (let id = sgid; id >= 0; id = this.parent.get(id)) {
console.assert(path.indexOf(id) < 0, 'SGroupForest: loop detected')
path.push(id)
}
return path
}
remove(id) {
console.assert(this.parent.has(id), 'sgid is not in the forest')
console.assert(this.children.has(id), 'sgid is not in the forest')

SGroupForest.prototype.insert = function (
{ id, atoms },
parent /* int, optional */,
children /* [int], optional */
) {
console.assert(!this.parent.has(id), 'sgid already present in the forest')
console.assert(!this.children.has(id), 'sgid already present in the forest')

if (!parent || !children) {
// if these are not provided, deduce automatically
const guess = this.getAtomSetRelations(id, new Pile(atoms))
parent = guess.parent
children = guess.children
}
const parentId = this.parent.get(id)
const childs = this.children.get(parentId)
this.children.get(id).forEach(childId => {
// reset parent links
this.parent.set(childId, parentId)
this.children.get(parentId).push(childId)
})

// TODO: make children Map<int, Pile> instead of Map<int, []>?
children.forEach(childId => {
// reset parent links
var childs = this.children.get(this.parent.get(childId))
var i = childs.indexOf(childId)
console.assert(
i >= 0 && childs.indexOf(childId, i + 1) < 0,
'Assertion failed'
) // one element
const i = childs.indexOf(id)
childs.splice(i, 1)
this.parent.set(childId, id)
})
this.children.set(id, children)
this.parent.set(id, parent)
this.children.get(parent).push(id)
this.atomSets.set(id, new Pile(atoms))

return { parent, children }
this.children.delete(id)
this.parent.delete(id)
this.atomSets.delete(id)
}
}

SGroupForest.prototype.remove = function (id) {
console.assert(this.parent.has(id), 'sgid is not in the forest')
console.assert(this.children.has(id), 'sgid is not in the forest')

const parentId = this.parent.get(id)
this.children.get(id).forEach(childId => {
// reset parent links
this.parent.set(childId, parentId)
this.children.get(parentId).push(childId)
})
export function checkOverlapping(struct, atoms) {
const sgroups = atoms.reduce((res, aid) => {
const atom = struct.atoms.get(aid)
return res.union(atom.sgs)
}, new Pile())

const childs = this.children.get(parentId)
const i = childs.indexOf(id)
console.assert(i >= 0 && childs.indexOf(id, i + 1) < 0, 'Assertion failed') // one element
childs.splice(i, 1)
return Array.from(sgroups).some(sid => {
const sg = struct.sgroups.get(sid)
if (sg.type === 'DAT') return false
const sgAtoms = SGroup.getAtoms(struct, sg)

this.children.delete(id)
this.parent.delete(id)
this.atomSets.delete(id)
return sgAtoms.length < atoms.length
? sgAtoms.findIndex(aid => atoms.indexOf(aid) === -1) >= 0
: atoms.findIndex(aid => sgAtoms.indexOf(aid) === -1) >= 0
})
}

export default SGroupForest
43 changes: 43 additions & 0 deletions packages/ketcher-react/src/script/editor/operations/CanvasLoad.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/****************************************************************************
* Copyright 2021 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.
***************************************************************************/
import Restruct from '../../render/restruct'
import Struct from '../../chem/struct'
import { BaseOperation } from './base'
import { OperationType } from './OperationType'

export class CanvasLoad extends BaseOperation {
data: {
struct?: Struct
}

constructor(struct?: Struct) {
super(OperationType.CANVAS_LOAD)
this.data = { struct }
}

execute(restruct: Restruct) {
const oldStruct = restruct.molecule
restruct.clearVisels() // TODO: What is it?
restruct.render.setMolecule(this.data.struct)
this.data.struct = oldStruct
}

invert() {
const inverted = new CanvasLoad()
inverted.data = this.data
return inverted
}
}
Loading

0 comments on commit 69a8c11

Please sign in to comment.