Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into #1990-functional-gr…
Browse files Browse the repository at this point in the history
…oup-does-not-connect-with-another-functional-group-on-clickdrag
  • Loading branch information
Stanislav Permiakov committed Feb 16, 2023
2 parents 7b74abd + cedf2a9 commit 69a2568
Show file tree
Hide file tree
Showing 44 changed files with 902 additions and 1,129 deletions.
41 changes: 41 additions & 0 deletions packages/ketcher-core/__tests__/domain/entities/pile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Pile } from 'domain/entities/pile'

describe('unionIntersections', () => {
it('unions multiple sets which have intersections', () => {
const setA = new Pile([0, 1])
const setB = new Pile([1, 2, 3])
const setC = new Pile([2, 3])

const union = Pile.unionIntersections([setA, setB, setC])

expect(union).toHaveLength(1)
expect(union[0]).toEqual(new Pile([0, 1, 2, 3]))
})

it('does not union sets which have no intersections', () => {
const setA = new Pile([0, 1])
const setB = new Pile([2, 3])

const union = Pile.unionIntersections([setA, setB])

expect(union).toHaveLength(2)
expect(union[0]).toEqual(setA)
expect(union[1]).toEqual(setB)
})

// Combines above two situations
it('unions multiple sets which have intersections, and skips sets without intersections', () => {
const setA = new Pile([0, 1])
const setB = new Pile([1, 2, 3])
const setC = new Pile([2, 3])
const setD = new Pile([4, 5])
const setE = new Pile([6])

const union = Pile.unionIntersections([setA, setB, setC, setD, setE])

expect(union).toHaveLength(3)
expect(union[0]).toEqual(new Pile([0, 1, 2, 3]))
expect(union[1]).toEqual(setD)
expect(union[2]).toEqual(setE)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -205,20 +205,6 @@ describe('serialize (ToKet)', () => {
const plusKet = parsedPrepareContent.root.nodes[3]
expect(structPlus).toEqual(plusKet)
})
it('correct work with sgroups', () => {
const parsedSgroupStruct = JSON.parse(ket.serialize(moleculeSgroupStruct))
expect(parsedSgroupStruct.root.nodes[6].data.type).toEqual('GEN')
expect(parsedSgroupStruct.root.nodes[7].data.type).toEqual('MUL')
expect(parsedSgroupStruct.root.nodes[7].data.mul).toEqual(1)
expect(parsedSgroupStruct.root.nodes[8].data.type).toEqual('SRU')
expect(parsedSgroupStruct.root.nodes[8].data.subscript).toEqual('n')
expect(parsedSgroupStruct.root.nodes[8].data.connectivity).toEqual('HT')
expect(parsedSgroupStruct.root.nodes[9].data.type).toEqual('MUL')
expect(parsedSgroupStruct.root.nodes[9].data.mul).toEqual(1)
expect(parsedSgroupStruct.root.nodes[10].data.type).toEqual('SUP')
expect(parsedSgroupStruct.root.nodes[11].data.subscript).toEqual('n')
expect(parsedSgroupStruct.root.nodes[11].data.connectivity).toEqual('HT')
})
it('moleculeToKet', () => {
const spy = jest.spyOn(moleculeToKet, 'moleculeToKet')
ket.serialize(moleculeContentStruct)
Expand All @@ -244,6 +230,19 @@ describe('serialize (ToKet)', () => {
expect(
spy.mock.results[1].value.bonds.filter((bond) => bond.type === 2).length
).toEqual(3)
// sgroups
ket.serialize(moleculeSgroupStruct)
expect(spy.mock.results[2].value.sgroups[0].type).toEqual('GEN')
expect(spy.mock.results[2].value.sgroups[1].type).toEqual('MUL')
expect(spy.mock.results[2].value.sgroups[1].mul).toEqual(1)
expect(spy.mock.results[2].value.sgroups[2].type).toEqual('SRU')
expect(spy.mock.results[2].value.sgroups[2].subscript).toEqual('n')
expect(spy.mock.results[2].value.sgroups[2].connectivity).toEqual('HT')
expect(spy.mock.results[2].value.sgroups[3].type).toEqual('MUL')
expect(spy.mock.results[2].value.sgroups[3].mul).toEqual(1)
expect(spy.mock.results[2].value.sgroups[4].type).toEqual('SUP')
expect(spy.mock.results[2].value.sgroups[5].subscript).toEqual('n')
expect(spy.mock.results[2].value.sgroups[5].connectivity).toEqual('HT')
})
it('rgroupToKet', () => {
const spy = jest.spyOn(rgroupToKet, 'rgroupToKet')
Expand Down
41 changes: 41 additions & 0 deletions packages/ketcher-core/src/domain/entities/pile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,45 @@ export class Pile<TValue = any> extends Set<TValue> {

return union
}

intersection(setB: Pile): Pile<TValue> {
const thisSet = new Pile(this)
return new Pile([...thisSet].filter((item) => setB.has(item)))
}

/**
* Union multiple sets which have intersections
* @example ```
* const setA = new Pile([0, 1])
* const setB = new Pile([1, 2])
* const setC = new Pile([2, 3])
* const setD = new Pile([4, 5])
* console.log(Pile.unionMultiple([setA, setB, setC, setD]))
* // [{0, 1, 2, 3}, {4, 5}]
* ```
*/
static unionIntersections<T>(sets: Array<Pile<T>>): Array<Pile<T>> {
let unionized = false

// Union two of sets
const setsToReturn = sets.reduce((prevSets, curSet) => {
let isCurSetMerged = false

const newSets = prevSets.map((set) => {
const intersec = set.intersection(curSet)
if (intersec.size > 0) {
unionized = true
isCurSetMerged = true
return set.union(curSet)
}
return set
})

if (!isCurSetMerged) newSets.push(curSet)
return newSets
}, new Array<Pile<T>>())

// Recursively union two of sets === union all sets
return unionized ? Pile.unionIntersections(setsToReturn) : setsToReturn
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export function moleculeToStruct(ketItem: any): Struct {
struct.initHalfBonds()
struct.initNeighbors()
struct.markFragments()
struct.bindSGroupsToFunctionalGroups()

return struct
}
Expand Down
20 changes: 4 additions & 16 deletions packages/ketcher-core/src/domain/serializers/ket/ketSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import { arrowToKet, plusToKet } from './toKet/rxnToKet'

import { Serializer } from '../serializers.types'
import { headerToKet } from './toKet/headerToKet'
import { moleculeToKet, sgroupToKet } from './toKet/moleculeToKet'
import { moleculeToStruct, sgroupToStruct } from './fromKet/moleculeToStruct'
import { moleculeToKet } from './toKet/moleculeToKet'
import { moleculeToStruct } from './fromKet/moleculeToStruct'
import { prepareStructForKet } from './toKet/prepare'
import { rgroupToKet } from './toKet/rgroupToKet'
import { rgroupToStruct } from './fromKet/rgroupToStruct'
Expand Down Expand Up @@ -64,10 +64,6 @@ function parseNode(node: any, struct: any) {
textToStruct(node, struct)
break
}
case 'sgroup': {
struct.sgroups.add(sgroupToStruct(node.data))
break
}
default:
break
}
Expand All @@ -85,7 +81,6 @@ export class KetSerializer implements Serializer<Struct> {
else if (nodes[i].$ref) parseNode(ket[nodes[i].$ref], resultingStruct)
})
resultingStruct.name = ket.header ? ket.header.moleculeName : null
resultingStruct.bindSGroupsToFunctionalGroups()

return resultingStruct
}
Expand All @@ -107,13 +102,13 @@ export class KetSerializer implements Serializer<Struct> {
switch (item.type) {
case 'molecule': {
result.root.nodes.push({ $ref: `mol${moleculeId}` })
result[`mol${moleculeId++}`] = moleculeToKet(item.fragment)
result[`mol${moleculeId++}`] = moleculeToKet(item.fragment!)
break
}
case 'rgroup': {
result.root.nodes.push({ $ref: `rg${item.data!.rgnumber}` })
result[`rg${item.data!.rgnumber}`] = rgroupToKet(
item.fragment,
item.fragment!,
item.data
)
break
Expand All @@ -134,13 +129,6 @@ export class KetSerializer implements Serializer<Struct> {
result.root.nodes.push(textToKet(item))
break
}
case 'sgroup': {
result.root.nodes.push({
type: item.type,
data: sgroupToKet(struct, item.data)
})
break
}
default:
break
}
Expand Down
Loading

0 comments on commit 69a2568

Please sign in to comment.