Skip to content

Commit

Permalink
Backmerge: #3459 - All atom attributes should be displayed as SMARTS …
Browse files Browse the repository at this point in the history
…if at least one purely SMARTS attribute exists
  • Loading branch information
AKZhuk committed Oct 23, 2023
1 parent 330b908 commit 62950be
Show file tree
Hide file tree
Showing 48 changed files with 306 additions and 92 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
210 changes: 128 additions & 82 deletions packages/ketcher-core/src/application/render/restruct/reatom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class ReAtom extends ReObject {

getSelectionContour(render: Render) {
const hasLabel =
(this.a.pseudo && this.a.pseudo.length > 1) ||
(this.a.pseudo && this.a.pseudo.length > 1 && !getQueryAttrsText(this)) ||
(this.showLabel && this.a.implicitH !== 0);
return hasLabel
? this.getLabeledSelectionContour(render)
Expand Down Expand Up @@ -252,20 +252,26 @@ class ReAtom extends ReObject {
let leftMargin;
let implh;
let isHydrogen;
let isHydrogenIsotope;
let label;
let index: any = null;

if (this.showLabel) {
label = buildLabel(this, render.paper, ps, options);
const isSmartPropertiesExist = checkIsSmartPropertiesExist(this.a);

if (isSmartPropertiesExist) {
const customQueryText =
getAtomCustomQuery({
...this.a,
...this.a.queryProperties,
}) || (this.a.queryProperties.customQuery as string);
const label = showSmartsLabel(this, render, customQueryText);
restruct.addReObjectPath(LayerMap.data, this.visel, label.path, ps, true);
} else if (this.showLabel) {
const data = buildLabel(this, render.paper, ps, options);
delta = 0.5 * options.lineWidth;
rightMargin =
(label.rbb.width / 2) * (options.zoom > 1 ? 1 : options.zoom);
leftMargin =
(-label.rbb.width / 2) * (options.zoom > 1 ? 1 : options.zoom);
label = data.label;
rightMargin = data.rightMargin;
leftMargin = data.leftMargin;
implh = Math.floor(this.a.implicitH);
isHydrogen = label.text === 'H';
isHydrogenIsotope = label.text === 'D' || label.text === 'T';
restruct.addReObjectPath(LayerMap.data, this.visel, label.path, ps, true);
}
if (options.showAtomIds) {
Expand All @@ -287,7 +293,7 @@ class ReAtom extends ReObject {
restruct.addReObjectPath(LayerMap.indices, this.visel, index.path, ps);
}

if (this.showLabel && (!this.a.pseudo || isHydrogenIsotope)) {
if (this.showLabel && !isSmartPropertiesExist) {
let hydroIndex: any = null;
if (isHydrogen && implh > 0) {
hydroIndex = showHydroIndex(this, render, implh, rightMargin);
Expand Down Expand Up @@ -406,8 +412,7 @@ class ReAtom extends ReObject {

const stereoLabel = this.a.stereoLabel; // Enhanced Stereo
const aamText = getAamText(this);
const isAromatized = Atom.isInAromatizedRing(restruct.molecule, aid);
const queryAttrsText = getQueryAttrsText(this, isAromatized);
const queryAttrsText = getQueryAttrsText(this);

// we render them together to avoid possible collisions

Expand Down Expand Up @@ -436,7 +441,7 @@ class ReAtom extends ReObject {
text += `.${aamText}.`;
}

if (text.length > 0) {
if (text.length > 0 && !isSmartPropertiesExist) {
const elem = Elements.get(this.a.label);
const aamPath = render.paper.text(ps.x, ps.y, text).attr({
font: options.font,
Expand Down Expand Up @@ -699,17 +704,26 @@ function shouldHydrogenBeOnLeft(struct, atom) {

return false;
}
function addTooltip(label: ElemAttr, text: string) {
const tooltip = `<p>${text.split(/(?<=[;,])/).join(' ')}</p>`;
label?.path.node.childNodes[0].setAttribute('data-tooltip', tooltip);
}

function buildLabel(
atom: ReAtom,
paper: any,
ps: Vec2,
options: any,
): ElemAttr {
): {
rightMargin: number;
leftMargin: number;
label: ElemAttr;
} {
// eslint-disable-line max-statements
const label: any = {};
label.text = getLabelText(atom.a);

const label: any = {
text: getLabelText(atom.a),
};
let tooltip: string | null = null;
if (!label.text) {
label.text = 'R#';
}
Expand All @@ -721,6 +735,11 @@ function buildLabel(
}
}

if (label.text?.length > 8) {
tooltip = label.text;
label.text = `${label.text?.substring(0, 8)}...`;
}

const { previewOpacity } = options;
label.path = paper.text(ps.x, ps.y, label.text).attr({
font: options.font,
Expand All @@ -730,22 +749,35 @@ function buildLabel(
'fill-opacity': atom.a.isPreview ? previewOpacity : 1,
});

if (tooltip) {
addTooltip(label, tooltip);
}

label.rbb = util.relBox(label.path.getBBox());
draw.recenterText(label.path, label.rbb);
let rightMargin =
(label.rbb.width / 2) * (options.zoom > 1 ? 1 : options.zoom); //
let leftMargin =
(-label.rbb.width / 2) * (options.zoom > 1 ? 1 : options.zoom);

if (atom.a.atomList !== null) {
const xShift =
((atom.hydrogenOnTheLeft ? -1 : 1) *
(label.rbb.width - label.rbb.height)) /
2;
pathAndRBoxTranslate(
label.path,
label.rbb,
((atom.hydrogenOnTheLeft ? -1 : 1) *
(label.rbb.width - label.rbb.height)) /
2,
xShift,

0,
);
rightMargin += xShift;
leftMargin += xShift;
}

atom.label = label;
return label;
return { label, rightMargin, leftMargin };
}

function getLabelText(atom) {
Expand Down Expand Up @@ -1036,6 +1068,36 @@ function showHydrogen(
return Object.assign(data, { hydrogen, hydroIndex });
}

function showSmartsLabel(atom: ReAtom, render: Render, text: string): ElemAttr {
// eslint-disable-line max-statements
const ps = Scale.modelToCanvas(atom.a.pp, render.options);
const options = render.options;
const label = {} as ElemAttr;
let tooltip: string | null = null;

if (text.length > 8) {
tooltip = text;
label.text = `${text.substring(0, 8)}...`;
} else {
label.text = text;
}

label.path = render.paper.text(ps.x, ps.y, label.text).attr({
font: options.font,
'font-size': options.fontsz,
fill: atom.color,
});
label.rbb = util.relBox(label.path.getBBox());
draw.recenterText(label.path, label.rbb);
pathAndRBoxTranslate(label.path, label.rbb, 0, 0);

if (tooltip) {
addTooltip(label, tooltip);
}

return label;
}

function showWarning(
atom,
render,
Expand Down Expand Up @@ -1125,32 +1187,54 @@ function getSubstitutionCountAttrText(value: number) {
return attrText;
}

export function getAtomType(atom: Atom) {
return atom.atomList
? 'list'
: atom.pseudo === atom.label
? 'pseudo'
: 'single';
}

function getAtomLabelAttrText(value: string, atom) {
const { atomType, atomList, notList, isotope } = atom;
let { atomType, atomList, notList } = atom;
if (!atomType) {
atomType = getAtomType(atom);
}
if (atomType === 'single') {
let labelText = isotope || '';
if (atom.aromaticity) {
labelText +=
atom.aromaticity === 'aromatic'
? value.toLowerCase()
: value.toUpperCase();
return labelText;
}
let labelText = '';
const number = Elements.get(capitalize(value))?.number;
labelText += number ? `#${number}` : value;
labelText += number ? `#${number}` : '';
return labelText;
} else if (atomType === 'list' && atomList !== '') {
return atomList
.split(',')
.map((el: string) => {
let atomNumbers: [];
if (atomList.ids) {
notList = atomList.notList;
atomNumbers = atomList.ids.map(
(number: string) => `${notList ? '!' : ''}#${number}`,
);
} else {
atomNumbers = atomList.split(',').map((el: string) => {
const number = Elements.get(capitalize(el))?.number || '';
return `${notList ? '!' : ''}#${number}`;
})
.join(notList ? ';' : ',');
});
}
return atomNumbers.join(notList ? ';' : ',');
} else {
return '';
}
}
export function checkIsSmartPropertiesExist(atom) {
const smartsSpecificProperties = [
'ringMembership',
'ringSize',
'connectivity',
'chirality',
'aromaticity',
'customQuery',
];
return smartsSpecificProperties.some((name) => atom.queryProperties?.[name]);
}

export function getAtomCustomQuery(atom) {
let queryAttrsText = '';

Expand All @@ -1161,6 +1245,8 @@ export function getAtomCustomQuery(atom) {
[key: string]: (value: string, atom) => string;
} = {
label: getAtomLabelAttrText,
isotope: (value) => value,
aromaticity: (value) => (value === 'aromatic' ? 'a' : 'A'),
charge: (value) => {
if (value === '') return value;
const regExpResult = /^([+-]?)([0-9]{1,3}|1000)([+-]?)$/.exec(value);
Expand All @@ -1183,9 +1269,9 @@ export function getAtomCustomQuery(atom) {
chirality: (value) => (value === 'clockwise' ? '@@' : '@'),
};

for (const propertyName in atom) {
for (const propertyName in patterns) {
const value = atom[propertyName];
if (propertyName in patterns && value !== null) {
if (propertyName in atom && value !== null) {
const attrText = patterns[propertyName](value, atom);
if (attrText) {
addSemicolon();
Expand All @@ -1197,31 +1283,15 @@ export function getAtomCustomQuery(atom) {
return queryAttrsText;
}

function getQueryAttrsText(atom, isAromatized: boolean) {
function getQueryAttrsText(atom): string {
let queryAttrsText = '';

const addSemicolon = () => {
if (queryAttrsText.length > 0) queryAttrsText += ';';
};

const {
ringBondCount,
substitutionCount,
unsaturatedAtom,
hCount,
implicitHCount,
queryProperties: {
aromaticity,
ringMembership,
ringSize,
connectivity,
chirality,
customQuery,
},
} = atom.a;
if (customQuery) {
return customQuery;
}
const { ringBondCount, substitutionCount, unsaturatedAtom, hCount } = atom.a;

if (ringBondCount !== 0) {
queryAttrsText += getRingBondCountAttrText(ringBondCount);
}
Expand All @@ -1238,30 +1308,6 @@ function getQueryAttrsText(atom, isAromatized: boolean) {
addSemicolon();
queryAttrsText += 'H' + (hCount - 1).toString();
}
if (implicitHCount !== null && !isAromatized) {
addSemicolon();
queryAttrsText += `h${implicitHCount}`;
}
if (aromaticity !== null) {
addSemicolon();
queryAttrsText += aromaticity === 'aromatic' ? 'a' : 'A';
}
if (Number.isFinite(ringMembership)) {
addSemicolon();
queryAttrsText += `R${ringMembership}`;
}
if (Number.isFinite(ringSize)) {
addSemicolon();
queryAttrsText += `r${ringSize}`;
}
if (Number.isFinite(connectivity)) {
addSemicolon();
queryAttrsText += `X${connectivity}`;
}
if (chirality !== null) {
addSemicolon();
queryAttrsText += chirality === 'clockwise' ? '@@' : '@';
}
return queryAttrsText;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1250,8 +1250,13 @@ function getBondMark(
// eslint-disable-line max-statements
const options = render.options;
let mark: string | null = null;
let tooltip: string | null = null;
if (bond.b.customQuery) {
mark = bond.b.customQuery;
if (bond.b.customQuery.length > 8) {
tooltip = bond.b.customQuery;
mark = `${bond.b.customQuery.substring(0, 8)}...`;
}
} else if (bond.b.topology === Bond.PATTERN.TOPOLOGY.RING) {
mark = 'rng';
} else if (bond.b.topology === Bond.PATTERN.TOPOLOGY.CHAIN) {
Expand All @@ -1272,8 +1277,10 @@ function getBondMark(
const s = new Vec2(2, 1).scaled(options.bondSpace);
if (bond.b.type === Bond.PATTERN.TYPE.TRIPLE) fixed += options.bondSpace;
const p = c.add(new Vec2(n.x * (s.x + fixed), n.y * (s.y + fixed)));
const path = draw.bondMark(render.paper, p, mark, options);
tooltip && path.node.childNodes[0].setAttribute('data-tooltip', tooltip);

return draw.bondMark(render.paper, p, mark, options);
return path;
}

function getIdsPath(
Expand Down
1 change: 1 addition & 0 deletions packages/ketcher-react/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"src/script/ui/views/AppClipArea.tsx",
"src/script/ui/views/components/Dialog/Dialog.tsx",
"src/script/ui/views/components/StructEditor/InfoPanel.tsx",
"src/script/ui/views/components/StructEditor/InfoTooltip.tsx",
"src/script/ui/views/modal/Modal.tsx",
"src/script/ui/views/modal/components/Text/Text.tsx",
"src/script/ui/views/modal/components/document/Open/Open.tsx",
Expand Down
Loading

0 comments on commit 62950be

Please sign in to comment.