Skip to content

Commit

Permalink
#225 add support for stereo cip values in ket format (#2479)
Browse files Browse the repository at this point in the history
* #225 – moved CIP rendering logic to Atom class

* #225 – moved CIP rendering logic to Bond class

* #225 – fixed selection for bonds and atoms

* #225 – fixed unselect for bonds and atoms

* #225 – removed previous crutch solution

* #225 – updated possible CIP values for bonds
  • Loading branch information
Nitvex authored Apr 20, 2023
1 parent 56810a5 commit 77e13a0
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 17 deletions.
17 changes: 16 additions & 1 deletion packages/ketcher-core/src/application/render/restruct/reatom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ class ReAtom extends ReObject {
color: string
component: number
label?: ElemAttr
cip?: {
// Raphael paths
path: any
text: any
rectangle: any
}

constructor(atom: Atom) {
super('atom')
Expand Down Expand Up @@ -135,7 +141,7 @@ class ReAtom extends ReObject {

show(restruct: ReStruct, aid: number, options: any): void {
// eslint-disable-line max-statements
const atom = restruct.molecule.atoms.get(aid)
const atom = restruct.molecule.atoms.get(aid)!
const sgroups = restruct.molecule.sgroups
const functionalGroups = restruct.molecule.functionalGroups
const render = restruct.render
Expand Down Expand Up @@ -404,6 +410,15 @@ class ReAtom extends ReObject {

restruct.addReObjectPath(LayerMap.hovering, this.visel, path)
}

if (atom.cip) {
this.cip = util.drawCIPLabel({
atomOrBond: atom,
position: atom.pp,
restruct: render.ctab,
visel: this.visel
})
}
}
}

Expand Down
19 changes: 18 additions & 1 deletion packages/ketcher-core/src/application/render/restruct/rebond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ class ReBond extends ReObject {
neihbid2 = -1
boldStereo?: boolean
rbb?: { x: number; y: number; width: number; height: number }
cip?: {
// Raphael paths
path: any
text: any
rectangle: any
}

constructor(bond: Bond) {
super('bond')
Expand Down Expand Up @@ -104,7 +110,7 @@ class ReBond extends ReObject {
// eslint-disable-line max-statements
const render = restruct.render
const struct = restruct.molecule
const bond = restruct.molecule.bonds.get(bid)
const bond = restruct.molecule.bonds.get(bid)!
const sgroups = restruct.molecule.sgroups
const functionalGroups = restruct.molecule.functionalGroups
if (
Expand Down Expand Up @@ -246,6 +252,17 @@ class ReBond extends ReObject {
true
)
}

if (bond.cip) {
this.cip = util.drawCIPLabel({
atomOrBond: bond,
position: bond.center,
restruct: render.ctab,
visel: this.visel
})
}

this.path.toBack()
}
}

Expand Down
15 changes: 13 additions & 2 deletions packages/ketcher-core/src/application/render/restruct/restruct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ class ReStruct {
}

this.showItemSelection(item, selected)
item.selectionPlate?.toBack()
})
}
})
Expand All @@ -637,9 +638,19 @@ class ReStruct {
item.selectionPlate
)
}
if (item.selectionPlate) item.selectionPlate.show() // TODO [RB] review
if (item.selectionPlate) {
item.selectionPlate.show()
item.cip?.rectangle.attr({
fill: '#7f7',
stroke: '#7f7'
})
}
} else if (exists && item.selectionPlate) {
item.selectionPlate.hide() // TODO [RB] review
item.selectionPlate.hide()
item.cip?.rectangle.attr({
fill: '#fff',
stroke: '#fff'
})
}
}
}
Expand Down
70 changes: 68 additions & 2 deletions packages/ketcher-core/src/application/render/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
* limitations under the License.
***************************************************************************/

import { Vec2 } from 'domain/entities'
import { Atom, Bond, Vec2 } from 'domain/entities'
import assert from 'assert'
import { ReStruct, LayerMap } from './restruct'
import Visel from './restruct/visel'

function tfx(v) {
return parseFloat(v).toFixed(8)
Expand Down Expand Up @@ -127,11 +129,75 @@ function calcCoordinates(aPoint, bPoint, lengthHyp) {
return obj
}

function getCIPValuePath({
paper,
cipLabelPosition,
atomOrBond,
options
}: {
paper
cipLabelPosition: Vec2
atomOrBond: Atom | Bond
options
}) {
const text = paper
.text(cipLabelPosition.x, cipLabelPosition.y, `(${atomOrBond.cip})`)
.attr({
font: options.font,
'font-size': options.fontsz
})
const box = text.getBBox()
const path = paper.set()
const rect = paper
.rect(box.x - 1, box.y - 1, box.width + 2, box.height + 2, 3, 3)
.attr({ fill: '#fff', stroke: '#fff' })
path.push(rect.toFront(), text.toFront())

return {
path,
text,
rectangle: rect
}
}

function drawCIPLabel({
atomOrBond,
position,
restruct,
visel
}: {
atomOrBond: Bond | Atom
position: Vec2
restruct: ReStruct
visel: Visel
}) {
const { options, paper } = restruct.render
const path = paper.set()

const cipLabelPosition = position.scaled(options.scale)
const cipValuePath = getCIPValuePath({
paper,
cipLabelPosition,
atomOrBond,
options
})
const box = relBox(cipValuePath.path.getBBox())

cipValuePath.path.translateAbs(0.5 * box.width, -0.5 * box.height)
path.push(cipValuePath.path.toFront())

restruct.addReObjectPath(LayerMap.data, visel, path, null, true)

return cipValuePath
}

const util = {
tfx,
relBox,
shiftRayBox,
calcCoordinates
calcCoordinates,
getCIPValuePath,
drawCIPLabel
}

export default util
11 changes: 11 additions & 0 deletions packages/ketcher-core/src/domain/entities/atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ export enum StereoLabel {
Or = 'or'
}

enum CIP {
S = 'S',
R = 'R',
s = 's',
r = 'r'
}

export interface AtomAttributes {
stereoParity?: number
stereoLabel?: string | null
Expand All @@ -70,6 +77,7 @@ export interface AtomAttributes {
rglabel?: string | null
charge?: number
radical?: number
cip?: CIP | null
isotope?: number
alias?: string | null
pseudo?: string
Expand Down Expand Up @@ -102,6 +110,7 @@ export class Atom {
label: 'C',
isotope: 0,
radical: 0,
cip: null,
charge: 0,
explicitValence: -1,
ringBondCount: 0,
Expand All @@ -126,6 +135,7 @@ export class Atom {
isotope: number
hCount: number
radical: number
cip: CIP | null
charge: number
explicitValence: number
ringBondCount: number
Expand Down Expand Up @@ -154,6 +164,7 @@ export class Atom {
this.alias = getValueOrDefault(attributes.alias, Atom.attrlist.alias)
this.isotope = getValueOrDefault(attributes.isotope, Atom.attrlist.isotope)
this.radical = getValueOrDefault(attributes.radical, Atom.attrlist.radical)
this.cip = getValueOrDefault(attributes.cip, Atom.attrlist.cip)
this.charge = getValueOrDefault(attributes.charge, Atom.attrlist.charge)
this.rglabel = getValueOrDefault(attributes.rglabel, Atom.attrlist.rglabel)
this.attpnt = getValueOrDefault(attributes.attpnt, Atom.attrlist.attpnt)
Expand Down
13 changes: 12 additions & 1 deletion packages/ketcher-core/src/domain/entities/bond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ import { Pile } from './pile'
import { Struct } from './struct'
import { Vec2 } from './vec2'

enum CIP {
E = 'E',
Z = 'Z',
M = 'M',
P = 'P'
}

export interface BondAttributes {
reactingCenterStatus?: number
topology?: number
Expand All @@ -27,6 +34,7 @@ export interface BondAttributes {
type: number
end: number
begin: number
cip?: CIP | null
}

export class Bond {
Expand Down Expand Up @@ -73,7 +81,8 @@ export class Bond {
type: Bond.PATTERN.TYPE.SINGLE,
stereo: Bond.PATTERN.STEREO.NONE,
topology: Bond.PATTERN.TOPOLOGY.EITHER,
reactingCenterStatus: Bond.PATTERN.REACTING_CENTER.UNMARKED
reactingCenterStatus: Bond.PATTERN.REACTING_CENTER.UNMARKED,
cip: null
}

begin: number
Expand All @@ -86,6 +95,7 @@ export class Bond {
len: number
sb: number
sa: number
cip?: CIP | null
hb1?: number
hb2?: number
angle: number
Expand All @@ -99,6 +109,7 @@ export class Bond {
this.stereo = Bond.PATTERN.STEREO.NONE
this.topology = Bond.PATTERN.TOPOLOGY.EITHER
this.reactingCenterStatus = 0
this.cip = attributes.cip ?? null
this.len = 0
this.sb = 0
this.sa = 0
Expand Down
3 changes: 2 additions & 1 deletion packages/ketcher-core/src/domain/entities/functionalGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import { ReSGroup } from 'application/render'
import assert from 'assert'
import { FunctionalGroupsProvider } from '../helpers'
import { Atom } from './atom'
import { Bond } from './bond'
import { Pool } from './pool'
import { SGroup } from './sgroup'
Expand Down Expand Up @@ -236,7 +237,7 @@ export class FunctionalGroup {
}

static isAtomInContractedFunctionalGroup(
atom,
atom: Atom,
sgroups,
functionalGroups,
sgroupsFromReStruct: boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export function atomToStruct(source) {
ifDef(params, 'explicitValence', source.explicitValence)
ifDef(params, 'isotope', source.isotope)
ifDef(params, 'radical', source.radical)
ifDef(params, 'cip', source.cip)
ifDef(params, 'attpnt', source.attachmentPoints)
// stereo
ifDef(params, 'stereoLabel', source.stereoLabel)
Expand Down Expand Up @@ -125,6 +126,7 @@ export function bondToStruct(source) {
ifDef(params, 'topology', source.topology)
ifDef(params, 'reactingCenterStatus', source.center)
ifDef(params, 'stereo', source.stereo)
ifDef(params, 'cip', source.cip)
// if (params.stereo)
// params.stereo = params.stereo > 1 ? params.stereo * 2 : params.stereo;
// params.xxx = 0;
Expand Down
2 changes: 1 addition & 1 deletion packages/ketcher-react/src/script/editor/tool/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ class SelectTool {
)
const atomFromStruct = atomId !== null && struct.atoms.get(atomId)?.a
if (
atomId !== null &&
atomFromStruct &&
!FunctionalGroup.isAtomInContractedFunctionalGroup(
// TODO: examine if this code is really needed, seems like its a hack
atomFromStruct,
Expand Down
10 changes: 10 additions & 0 deletions packages/ketcher-react/src/script/ui/data/schema/struct-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ export const atom = {
],
default: 0
},
cip: {
title: 'CIP',
type: 'string',
enum: ['R', 'S', 'r', 's']
},
ringBondCount: {
title: 'Ring bond count',
enum: [0, -2, -1, 2, 3, 4],
Expand Down Expand Up @@ -209,6 +214,11 @@ export const bond = {
'Made/broken and changes'
], // "Order changes" x 3
default: 0
},
cip: {
title: 'CIP',
type: 'string',
enum: ['E', 'Z', 'M', 'P']
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,7 @@ const InfoPanel: FC<InfoPanelProps> = (props) => {
const groupName = sGroup?.data?.name

useEffect(() => {
// workaround for current release; just don't show tooltip for CIP data
// in future release CIP will be atom/bond property, not a s-group property
const CIP_FIELD_NAME = 'INDIGO_CIP_DESC'
if (
!groupStruct &&
sGroup?.type === 'DAT' &&
sGroup.data?.fieldName !== CIP_FIELD_NAME
) {
if (!groupStruct && sGroup?.type === 'DAT') {
setSGroupData(`${sGroup.data?.fieldName}=${sGroup.data?.fieldValue}`)
} else {
setSGroupData(null)
Expand Down

0 comments on commit 77e13a0

Please sign in to comment.