Skip to content

Commit

Permalink
#4431 – Implement support of unresolved IDT monomers (#4866)
Browse files Browse the repository at this point in the history
* #4431 – Implement support of unresolved IDT monomers

* #4431 – Address review comment
  • Loading branch information
svvald authored Jun 25, 2024
1 parent d769f60 commit 1d4c2fa
Show file tree
Hide file tree
Showing 24 changed files with 274 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import {
RNABaseRenderer,
SugarRenderer,
PhosphateRenderer,
UnresolvedMonomerRenderer,
} from 'application/render/renderers';
import { MonomerItemType } from 'domain/types';
import { Peptide } from 'domain/entities/Peptide';
import { Chem } from 'domain/entities/Chem';
import { Sugar } from 'domain/entities/Sugar';
import { Phosphate } from 'domain/entities/Phosphate';
import { RNABase } from 'domain/entities/RNABase';
import {
Peptide,
Chem,
Sugar,
Phosphate,
RNABase,
UnresolvedMonomer,
} from 'domain/entities';
import { KetMonomerClass } from 'application/formatters/types/ket';

type DerivedClass<T> = new (...args: unknown[]) => T;
Expand Down Expand Up @@ -47,7 +51,11 @@ export const monomerFactory = (
let MonomerRenderer;
let ketMonomerClass: KetMonomerClass;

if (
if (monomer.props.unresolved) {
Monomer = UnresolvedMonomer;
MonomerRenderer = UnresolvedMonomerRenderer;
ketMonomerClass = KetMonomerClass.CHEM;
} else if (
monomer.props.MonomerType === MONOMER_CONST.CHEM ||
(monomer.props.MonomerType === MONOMER_CONST.RNA &&
(monomer.props.MonomerClass === MONOMER_CONST.MODDNA ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export interface IKetMonomerTemplate {
classHELM?: string;
name?: string;
idtAliases?: IKetIdtAliases;
unresolved?: boolean;
}

export interface IKetMonomerTemplateRef {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { BaseMonomerRenderer } from 'application/render/renderers/BaseMonomerRenderer';
import { UnresolvedMonomer } from 'domain/entities';
import { Selection } from 'd3';

const UNRESOLVED_MONOMER_SELECTED_ELEMENT_ID = '#unresolved-monomer-selection';
const UNRESOLVED_MONOMER_HOVERED_ELEMENT_ID = '#unresolved-monomer-hover';
const UNRESOLVED_MONOMER_SYMBOL_ELEMENT_ID = '#unresolved-monomer';

export class UnresolvedMonomerRenderer extends BaseMonomerRenderer {
constructor(public monomer: UnresolvedMonomer, scale?: number) {
super(
monomer,
UNRESOLVED_MONOMER_SELECTED_ELEMENT_ID,
UNRESOLVED_MONOMER_HOVERED_ELEMENT_ID,
UNRESOLVED_MONOMER_SYMBOL_ELEMENT_ID,
scale,
);
}

public get textColor() {
return 'white';
}

protected appendBody(
rootElement: Selection<SVGGElement, void, HTMLElement, never>,
) {
return rootElement
.append('use')
.data([this])
.attr('href', UNRESOLVED_MONOMER_SYMBOL_ELEMENT_ID);
}

show(theme) {
super.show(theme);
}

protected get enumerationElementPosition() {
return undefined;
}

protected get beginningElementPosition() {
return undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export { RNABaseRenderer } from './RNABaseRenderer';
export { BaseRenderer } from './BaseRenderer';
export { BaseMonomerRenderer } from './BaseMonomerRenderer';
export { PolymerBondRenderer } from './PolymerBondRenderer';
export { UnresolvedMonomerRenderer } from './UnresolvedMonomerRenderer';
export * from './sequence';
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import { Chem, Peptide, Phosphate, Vec2 } from 'domain/entities';
import { PeptideSequenceItemRenderer } from 'application/render/renderers/sequence/PeptideSequenceItemRenderer';
import { ChemSequenceItemRenderer } from 'application/render/renderers/sequence/ChemSequenceItemRenderer';
import { PhosphateSequenceItemRenderer } from 'application/render/renderers/sequence/PhosphateSequenceItemRenderer';
import { NucleotideSequenceItemRenderer } from 'application/render/renderers/sequence/NucleotideSequenceItemRenderer';
import {
Chem,
Peptide,
Phosphate,
Vec2,
Nucleotide,
Nucleoside,
EmptySequenceNode,
LinkerSequenceNode,
UnresolvedMonomer,
} from 'domain/entities';
import {
PeptideSequenceItemRenderer,
ChemSequenceItemRenderer,
PhosphateSequenceItemRenderer,
NucleotideSequenceItemRenderer,
EmptySequenceItemRenderer,
BaseMonomerRenderer,
BaseSequenceItemRenderer,
NucleosideSequenceItemRenderer,
UnresolvedMonomerSequenceItemRenderer,
} from 'application/render';
import { SubChainNode } from 'domain/entities/monomer-chains/types';
import { Nucleotide } from 'domain/entities/Nucleotide';
import { Nucleoside } from 'domain/entities/Nucleoside';
import { BaseSubChain } from 'domain/entities/monomer-chains/BaseSubChain';
import { EmptySequenceNode } from 'domain/entities/EmptySequenceNode';
import { EmptySequenceItemRenderer } from 'application/render/renderers/sequence/EmptySequenceItemRenderer';
import { BaseMonomerRenderer } from 'application/render';
import { BaseSequenceItemRenderer } from 'application/render/renderers/sequence/BaseSequenceItemRenderer';
import { LinkerSequenceNode } from 'domain/entities/LinkerSequenceNode';
import { NucleosideSequenceItemRenderer } from './NucleosideSequenceItemRenderer';

export class SequenceNodeRendererFactory {
static fromNode(
Expand Down Expand Up @@ -50,6 +59,9 @@ export class SequenceNodeRendererFactory {
case Chem:
RendererClass = ChemSequenceItemRenderer;
break;
case UnresolvedMonomer:
RendererClass = UnresolvedMonomerSequenceItemRenderer;
break;
default:
RendererClass = ChemSequenceItemRenderer;
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { BaseSequenceItemRenderer } from 'application/render/renderers/sequence/BaseSequenceItemRenderer';

export class UnresolvedMonomerSequenceItemRenderer extends BaseSequenceItemRenderer {
get symbolToDisplay(): string {
return '?';
}

protected drawModification(): void {
return undefined;
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
export * from './SequenceRenderer';
export * from './BaseSequenceItemRenderer';
export * from './BackBoneBondSequenceRenderer';
export * from './BaseSequenceRenderer';
export * from './ChemSequenceItemRenderer';
export * from './EmptySequenceItemRenderer';
export * from './NucleotideSequenceItemRenderer';
export * from './NucleosideSequenceItemRenderer';
export * from './PeptideSequenceItemRenderer';
export * from './PhosphateSequenceItemRenderer';
export * from './PolymerBondSequenceRenderer';
export * from './RNASequenceItemRenderer';
export * from './SequenceNodeRendererFactory';
export * from './UnresolvedMonomerSequenceItemRenderer';
24 changes: 24 additions & 0 deletions packages/ketcher-core/src/domain/entities/UnresolvedMonomer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { BaseMonomer, Peptide } from 'domain/entities';
import { ChemSubChain } from 'domain/entities/monomer-chains/ChemSubChain';
import { PeptideSubChain } from 'domain/entities/monomer-chains/PeptideSubChain';
import { SubChainNode } from 'domain/entities/monomer-chains/types';

export class UnresolvedMonomer extends BaseMonomer {
public getValidSourcePoint(monomer?: BaseMonomer) {
return Peptide.prototype.getValidSourcePoint.call(this, monomer);
}

public getValidTargetPoint(monomer: BaseMonomer) {
return Peptide.prototype.getValidTargetPoint.call(this, monomer);
}

public get SubChainConstructor() {
return ChemSubChain;
}

public isMonomerTypeDifferentForChaining(monomerToChain: SubChainNode) {
return ![PeptideSubChain, ChemSubChain].includes(
monomerToChain.SubChainConstructor,
);
}
}
3 changes: 3 additions & 0 deletions packages/ketcher-core/src/domain/entities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ export * from './Nucleoside';
export * from './Nucleotide';
export * from './monomer-chains/types';
export * from './MonomerSequenceNode';
export * from './EmptySequenceNode';
export * from './LinkerSequenceNode';
export * from './UnresolvedMonomer';
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Phosphate,
SubChainNode,
Sugar,
UnresolvedMonomer,
} from 'domain/entities';
import {
getNextMonomerInChain,
Expand Down Expand Up @@ -49,6 +50,11 @@ export class Chain {
public add(monomer: BaseMonomer) {
this.createSubChainIfNeed(monomer);

if (monomer instanceof UnresolvedMonomer) {
this.lastSubChain.add(new MonomerSequenceNode(monomer));
return;
}

if (monomer instanceof Sugar) {
if (isValidNucleoside(monomer, this.firstMonomer)) {
this.lastSubChain.add(Nucleoside.fromSugar(monomer, false));
Expand All @@ -59,10 +65,12 @@ export class Chain {
return;
}
}

if (monomer instanceof Peptide) {
this.lastSubChain.add(new MonomerSequenceNode(monomer));
return;
}

const nextMonomer = getNextMonomerInChain(monomer);
const isNextMonomerNucleosideOrNucleotideOrPeptide = () => {
const isNucleosideOrNucleotide =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Phosphate,
RNABase,
Sugar,
UnresolvedMonomer,
} from 'domain/entities';
import {
getNextMonomerInChain,
Expand Down Expand Up @@ -102,7 +103,8 @@ export class ChainsCollection {
| typeof Phosphate
| typeof Sugar
| typeof RNABase
> = [Peptide, Chem, Phosphate, Sugar, RNABase],
| typeof UnresolvedMonomer
> = [Peptide, Chem, Phosphate, Sugar, RNABase, UnresolvedMonomer],
) {
const monomersList = monomers.filter((monomer) =>
MonomerTypes.some((MonomerType) => monomer instanceof MonomerType),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function templateToMonomerProps(template: IKetMonomerTemplate) {
MonomerClass: template.class,
MonomerCaps: {},
idtAliases: template.idtAliases,
unresolved: template.unresolved,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ export class KetSerializer implements Serializer<Struct> {
template: IKetMonomerTemplate,
monomerItem: MonomerItemType,
) {
if (monomerItem.props.unresolved) {
return;
}

const { attachmentPointsList } =
BaseMonomer.getAttachmentPointDictFromMonomerDefinition(
template.attachmentPoints || [],
Expand Down
1 change: 1 addition & 0 deletions packages/ketcher-core/src/domain/types/monomers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type MonomerItemType = {
MonomerClass?: string;
isMicromoleculeFragment?: boolean;
idtAliases?: IKetIdtAliases;
unresolved?: boolean;
};
attachmentPoints?: IKetAttachmentPoint[];
seqId?: number;
Expand Down
2 changes: 2 additions & 0 deletions packages/ketcher-macromolecules/src/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
SugarAvatar,
PhosphateAvatar,
RNABaseAvatar,
UnresolvedMonomerAvatar,
} from 'components/shared/monomerOnCanvas';
import { MonomerConnectionOnlyProps } from 'components/modal/modalContainer/types';
import { ErrorModal } from 'components/modal/Error';
Expand Down Expand Up @@ -372,6 +373,7 @@ function Editor({ theme, togglerComponent }: EditorProps) {
<SugarAvatar />
<PhosphateAvatar />
<RNABaseAvatar />
<UnresolvedMonomerAvatar />
<SequenceStartArrow />
</defs>
<g className="drawn-structures"></g>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { preview } from '../../../constants';
import styled from '@emotion/styled';
import { useAppSelector } from 'hooks';
import { selectShowPreview } from 'state/common';
import UnresolvedMonomerPreview from 'components/shared/UnresolvedMonomerPreview/UnresolvedMonomerPreview';

const MonomerPreview = ({ className }: IPreviewProps) => {
const preview = useAppSelector(selectShowPreview);
Expand All @@ -39,20 +40,26 @@ const MonomerPreview = ({ className }: IPreviewProps) => {
data-testid="polymer-library-preview"
>
<MonomerName>{preview.monomer.struct.name}</MonomerName>
<StyledStructRender
struct={preview.monomer.struct}
options={{ needCache: false }}
/>
{preview.monomer.props?.unresolved ? (
<UnresolvedMonomerPreview />
) : (
<StyledStructRender
struct={preview.monomer.struct}
options={{ needCache: false }}
/>
)}
</ContainerDynamic>
)
);
};

const StyledPreview = styled(MonomerPreview)`
const StyledPreview = styled(MonomerPreview)<IPreviewProps>`
z-index: 5;
position: absolute;
width: ${preview.width}px;
height: ${preview.height}px;
width: ${(props) =>
props.unresolvedMonomer ? 'auto' : preview.width + 'px'};
height: ${(props) =>
props.unresolvedMonomer ? 'auto' : preview.height + 'px'};
transform: translate(-50%, 0);
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/

export interface IPreviewProps {
className?: string;
unresolvedMonomer?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,10 @@ export const Preview = () => {
return <MonomerWithIDTAliasesPreview className="polymer-library-preview" />;
}

return <MonomerPreview className="polymer-library-preview" />;
return (
<MonomerPreview
className="polymer-library-preview"
unresolvedMonomer={preview?.monomer?.props?.unresolved}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Icon } from 'ketcher-react';
import { StyledContent } from 'components/shared/UnresolvedMonomerPreview/styles';

const UnresolvedMonomerPreview = () => {
return (
<StyledContent>
<Icon name="questionMark" />
Unknown structure
</StyledContent>
);
};

export default UnresolvedMonomerPreview;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import styled from '@emotion/styled';

export const StyledContent = styled.div`
width: 70px;
height: 77px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
padding: 8px;
color: #7c7c7f;
border: 1px solid #cad3dd;
`;
Loading

0 comments on commit 1d4c2fa

Please sign in to comment.