Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#6272 - Cursor position after adding preset in sequence mode causes an incorrect sequence formation #6368

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/ketcher-core/src/application/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,13 @@ import { BaseSequenceItemRenderer } from 'application/render/renderers/sequence/
import { SequenceRenderer } from 'application/render/renderers/sequence/SequenceRenderer';
import { ketcherProvider } from 'application/utils';
import assert from 'assert';
import { SequenceType, Struct, Vec2 } from 'domain/entities';
import { CELL_WIDTH, SequenceType, Struct, Vec2 } from 'domain/entities';
import { BaseMonomer } from 'domain/entities/BaseMonomer';
import { Command } from 'domain/entities/Command';
import {
DrawingEntitiesManager,
MONOMER_START_X_POSITION,
MONOMER_START_Y_POSITION,
CELL_WIDTH,
} from 'domain/entities/DrawingEntitiesManager';
import { PolymerBond } from 'domain/entities/PolymerBond';
import { AttachmentPointName, MonomerItemType } from 'domain/types';
Expand Down
189 changes: 130 additions & 59 deletions packages/ketcher-core/src/application/editor/modes/SequenceMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ export interface StartNewSequenceEventData {
indexOfRowBefore: number;
}

function nodeToString(currentNode: SubChainNode | undefined) {
if (currentNode === undefined) return 'undefined';
if (currentNode === null) return 'null';

let content;
const typeName = currentNode.constructor.name;
switch (typeName) {
case 'MonomerSequenceNode': {
const monomer = currentNode.monomer;
content = `${monomer.constructor.name}: ${monomer.label}`;
}
}

return `${typeName}${content ? `{ ${content} }` : ''}`;
}

export class SequenceMode extends BaseMode {
private _isEditMode = false;
private _isEditInRNABuilderMode = false;
Expand Down Expand Up @@ -95,11 +111,19 @@ export class SequenceMode extends BaseMode {
this._isEditInRNABuilderMode = isEditInRNABuilderMode;
}

private static objCounter = -1;
private objId = ++SequenceMode.objCounter;

private toLog(): string {
return `${this.constructor.name}<${this.objId}>`;
}

public initialize(
needScroll = true,
needRemoveSelection = true,
needReArrangeChains = true,
) {
console.log(`${this.toLog()}.initialize()`);
const command = super.initialize(needRemoveSelection);
const editor = CoreEditor.provideEditorInstance();

Expand Down Expand Up @@ -245,7 +269,7 @@ export class SequenceMode extends BaseMode {
const currentNode = SequenceRenderer.getNodeByPointer(nodeIndexOverall);

// Update Sugar monomerItem object
if (currentNode.sugar && sugarMonomerItem) {
if (currentNode instanceof Nucleoside && sugarMonomerItem) {
modelChanges.merge(
editor.drawingEntitiesManager.modifyMonomerItem(
currentNode.sugar,
Expand All @@ -254,7 +278,7 @@ export class SequenceMode extends BaseMode {
);
}
// Update Base monomerItem object
if (currentNode.rnaBase && baseMonomerItem) {
if (currentNode instanceof Nucleoside && baseMonomerItem) {
modelChanges.merge(
editor.drawingEntitiesManager.modifyMonomerItem(
currentNode.rnaBase,
Expand Down Expand Up @@ -299,7 +323,10 @@ export class SequenceMode extends BaseMode {
),
);
// Update Phosphate monomerItem object
} else if (currentNode.monomer instanceof Phosphate) {
} else if (
currentNode instanceof MonomerSequenceNode &&
currentNode.monomer instanceof Phosphate
) {
modelChanges.merge(
editor.drawingEntitiesManager.modifyMonomerItem(
currentNode.monomer,
Expand Down Expand Up @@ -1652,74 +1679,98 @@ export class SequenceMode extends BaseMode {

public insertPresetFromLibrary(preset: IRnaPreset) {
const editor = CoreEditor.provideEditorInstance();
const history = new EditorHistory(editor);
const modelChanges = new Command();
const selections = SequenceRenderer.selections;

if (selections.length > 0) {
if (!this.presetHasNeededAttachmentPoints(preset)) {
this.showMergeWarningModal();
return;
}

if (this.selectionsContainLinkerNode(selections)) {
editor.events.openConfirmationDialog.dispatch({
confirmationText:
'Symbol @ can represent multiple monomers, all of them are going to be deleted. Do you want to proceed?',
onConfirm: () => {
this.replaceSelectionsWithPreset(selections, preset);
},
});
} else if (
this.selectionsCantPreserveConnectionsWithPreset(
selections,
preset,
true,
)
) {
editor.events.openConfirmationDialog.dispatch({
confirmationText:
'Side chain connections will be deleted during replacement. Do you want to proceed?',
onConfirm: () => {
this.replaceSelectionsWithPreset(selections, preset);
},
});
} else {
this.replaceSelectionsWithPreset(selections, preset);
}
this.insertPresetFromLibraryOverSelection(preset, editor, selections);
} else {
const newNodePosition = this.getNewNodePosition();
this.insertPresetFromLibraryWithoutSelection(preset, editor);
}
}

const newPresetNode = this.createRnaPresetNode(preset, newNodePosition);
private insertPresetFromLibraryWithoutSelection(
preset: IRnaPreset,
editor: CoreEditor,
) {
const history = new EditorHistory(editor);
const modelChanges = new Command();

assert(newPresetNode);
const newNodePosition = this.getNewNodePosition();

const { nodeCanBeInserted, missingAttachmentPoint } =
this.checkNodeInsertionPossibility(newPresetNode);
const newPresetNode = this.createRnaPresetNode(preset, newNodePosition);

if (!nodeCanBeInserted) {
const message =
missingAttachmentPoint &&
`The monomer lacks ${missingAttachmentPoint} attachment point and cannot be inserted at current position`;
this.showMergeWarningModal(message);
return;
}
assert(newPresetNode);

const rnaPresetAddModelChanges =
editor.drawingEntitiesManager.addRnaPresetFromNode(newPresetNode);
const { nodeCanBeInserted, missingAttachmentPoint } =
this.checkNodeInsertionPossibility(newPresetNode);

modelChanges.merge(rnaPresetAddModelChanges);
modelChanges.merge(this.insertNewSequenceFragment(newPresetNode));
if (!nodeCanBeInserted) {
const message =
missingAttachmentPoint &&
`The monomer lacks ${missingAttachmentPoint} attachment point and cannot be inserted at current position`;
this.showMergeWarningModal(message);
return;
}

modelChanges.addOperation(new ReinitializeModeOperation());
editor.renderersContainer.update(modelChanges);
SequenceRenderer.moveCaretForward();
history.update(modelChanges);
const rnaPresetAddModelChanges =
editor.drawingEntitiesManager.addRnaPresetFromNode(newPresetNode);

modelChanges.merge(rnaPresetAddModelChanges);
modelChanges.merge(this.insertNewSequenceFragment(newPresetNode));
// TODO: Adjust SequenceRenderer.caretPosition
// modelChanges.merge(
// new RestoreSequenceCaretPositionOperation(
// SequenceRenderer.caretPosition,
// SequenceRenderer.caretPosition + 1,
// ),
// );

modelChanges.addOperation(new ReinitializeModeOperation());
editor.renderersContainer.update(modelChanges);
// if (SequenceRenderer.caretPosition === 0) {
// SequenceRenderer.setCaretPosition(2);
// } else {
// SequenceRenderer.moveCaretForward();
// }
history.update(modelChanges);
}

private insertPresetFromLibraryOverSelection(
preset: IRnaPreset,
editor: CoreEditor,
selections: NodeSelection[][],
) {
if (!this.presetHasNeededAttachmentPoints(preset)) {
this.showMergeWarningModal();
return;
}

if (this.selectionsContainLinkerNode(selections)) {
editor.events.openConfirmationDialog.dispatch({
confirmationText:
'Symbol @ can represent multiple monomers, all of them are going to be deleted. Do you want to proceed?',
onConfirm: () => {
this.replaceSelectionsWithPreset(selections, preset);
},
});
} else if (
this.selectionsCantPreserveConnectionsWithPreset(selections, preset, true)
) {
editor.events.openConfirmationDialog.dispatch({
confirmationText:
'Side chain connections will be deleted during replacement. Do you want to proceed?',
onConfirm: () => {
this.replaceSelectionsWithPreset(selections, preset);
},
});
} else {
this.replaceSelectionsWithPreset(selections, preset);
}
}

private insertNewSequenceItem(editor: CoreEditor, enteredSymbol: string) {
const currentNode = SequenceRenderer.currentEdittingNode;
assert(currentNode, 'currentEdittingNode is undefined');
const newNodePosition = this.getNewNodePosition();
let modelChanges;
const previousNodeInSameChain = SequenceRenderer.previousNodeInSameChain;
Expand Down Expand Up @@ -1786,17 +1837,37 @@ export class SequenceMode extends BaseMode {
: new ChainsCollection().add(
new Chain().addNode(chainsCollectionOrNode),
);
const currentNode =
let currentNode =
nextNodeToConnect === null
? undefined
: nextNodeToConnect || SequenceRenderer.currentEdittingNode;
const previousNodeInSameChain =
let previousNodeInSameChain =
previousNodeToConnect || SequenceRenderer.previousNodeInSameChain;
let newNodePosition = this.getNewNodePosition();
if (
previousNodeInSameChain instanceof Nucleoside &&
currentNode?.monomer instanceof Phosphate &&
currentNode instanceof MonomerSequenceNode &&
currentNode.monomer.unUsedAttachmentPointsNamesList.includes(
AttachmentPointName.R2,
)
) {
[previousNodeInSameChain, currentNode] = [
currentNode,
SequenceRenderer.getNextNode(currentNode),
];
newNodePosition = newNodePosition.add(new Vec2(1.5, 0, 0)); // ??
}

console.log(
`${this.toLog()}.insertNewSequenceFragment(), ` +
`currentNode: ${nodeToString(currentNode)}, ` +
`previousNodeInSameChain: ${nodeToString(previousNodeInSameChain)}`,
);

const modelChanges = new Command();
const lastNodeOfNewFragment = chainsCollection.lastNode;
const firstNodeOfNewFragment = chainsCollection.firstNode;
const newNodePosition = this.getNewNodePosition();

if (needConnectWithPreviousNodeInChain) {
this.deleteBondToNextNodeInChain(previousNodeInSameChain, modelChanges);
this.connectNodes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
* limitations under the License.
***************************************************************************/

import { PolymerBond } from 'domain/entities/PolymerBond';
import { RenderersManager } from 'application/render/renderers/RenderersManager';
import { Operation } from 'domain/entities/Operation';
import { BaseMonomer } from 'domain/entities/BaseMonomer';
import { HydrogenBond } from 'domain/entities';
import { HydrogenBond, PolymerBond } from 'domain/entities';

export class PolymerBondAddOperation implements Operation {
public polymerBond;
public priority = 1;

constructor(
private addPolymerBondChangeModel: (
polymerBond?: PolymerBond | HydrogenBond,
Expand All @@ -45,6 +45,7 @@ export class PolymerBondAddOperation implements Operation {

export class PolymerBondDeleteOperation implements Operation {
public priority = -1;

constructor(
public polymerBond: PolymerBond | HydrogenBond,
private deletePolymerBondChangeModel: () => void,
Expand Down Expand Up @@ -107,6 +108,7 @@ export class PolymerBondCancelCreationOperation implements Operation {
export class PolymerBondFinishCreationOperation implements Operation {
public polymerBond;
public priority = 1;

constructor(
private finishPolymerBondCreationModelChange: (
polymerBond?: PolymerBond,
Expand Down Expand Up @@ -154,6 +156,7 @@ export class SelectLayoutModeOperation implements Operation {

export class ReconnectPolymerBondOperation implements Operation {
public polymerBond;

constructor(
private reconnectPolymerBondModelChange: () => PolymerBond,
private revertReconnectPolymerBondModelChange: () => PolymerBond,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
import { CELL_WIDTH, Sugar, Vec2, RNABase, Phosphate } from 'domain/entities';
import { Tool, IRnaPreset } from 'application/editor/tools/Tool';
import { Sugar } from 'domain/entities/Sugar';
import { Vec2 } from 'domain/entities';

import { CoreEditor, EditorHistory } from 'application/editor/internal';
import { BaseMonomerRenderer } from 'application/render/renderers';
import { MonomerItemType } from 'domain/types';
import { monomerFactory } from '../operations/monomer/monomerFactory';
import { RNABase } from 'domain/entities/RNABase';
import { Phosphate } from 'domain/entities/Phosphate';
import { Coordinates } from '../shared/coordinates';
import { CELL_WIDTH } from 'domain/entities/DrawingEntitiesManager';

class RnaPresetTool implements Tool {
rnaBase: MonomerItemType | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {

export interface IBaseRenderer {
show(theme): void;

remove(): void;
}

Expand Down Expand Up @@ -42,6 +43,7 @@ export abstract class BaseRenderer implements IBaseRenderer {
protected canvasWrapper: D3SvgElementSelection<SVGSVGElement, void>;

protected canvas: D3SvgElementSelection<SVGSVGElement, void>;

protected constructor(public drawingEntity: DrawingEntity) {
this.canvasWrapper =
ZoomTool.instance?.canvasWrapper || select(canvasSelector);
Expand All @@ -52,9 +54,9 @@ export abstract class BaseRenderer implements IBaseRenderer {
return provideEditorSettings();
}

public get rootBBox() {
public get rootBBox(): DOMRect | undefined {
const rootNode = this.rootElement?.node();
if (!rootNode) return;
if (!rootNode) return undefined;

return rootNode.getBBox();
}
Expand Down Expand Up @@ -83,8 +85,11 @@ export abstract class BaseRenderer implements IBaseRenderer {
}

public abstract show(theme, force?: boolean): void;

public abstract drawSelection(): void;

public abstract moveSelection(): void;

protected abstract appendHover(
hoverArea,
):
Expand All @@ -96,6 +101,7 @@ export abstract class BaseRenderer implements IBaseRenderer {
| void;

protected abstract removeHover(): void;

protected abstract appendHoverAreaElement(): void;

public remove() {
Expand Down
Loading
Loading