Skip to content

Commit

Permalink
#4231 - Macro: Maximum call stack size exceeded error appears during …
Browse files Browse the repository at this point in the history
…snake layout for large chains

- replaced recursion with stack implementation
  • Loading branch information
rrodionov91 committed Mar 13, 2024
1 parent c52b20f commit d964f85
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -141,75 +141,89 @@ export class RenderersManager {

private recalculatePeptideChainEnumeration(
peptideRenderer: PeptideRenderer,
currentEnumeration = 1,
_currentEnumeration = 1,
) {
peptideRenderer.setEnumeration(currentEnumeration);
peptideRenderer.redrawEnumeration();
let currentEnumeration = _currentEnumeration;
const stack = [{ monomerRenderer: peptideRenderer }];

const nextMonomer = getNextMonomerInChain(peptideRenderer.monomer);
while (stack.length > 0) {
const stackItem = stack.pop();
assert(stackItem);
const { monomerRenderer } = stackItem;

if (!(nextMonomer instanceof Peptide)) {
return;
}
monomerRenderer.setEnumeration(currentEnumeration);
monomerRenderer.redrawEnumeration();

const isR2R1Connection = checkIsR2R1Connection(
peptideRenderer.monomer,
nextMonomer,
);
const nextMonomer = getNextMonomerInChain(monomerRenderer.monomer);

if (!isR2R1Connection) {
return;
}
if (!(nextMonomer instanceof Peptide)) {
return;
}

const isR2R1Connection = checkIsR2R1Connection(
monomerRenderer.monomer,
nextMonomer,
);

assert(nextMonomer.renderer);
if (!isR2R1Connection) {
return;
}

assert(nextMonomer.renderer);

this.recalculatePeptideChainEnumeration(
nextMonomer.renderer as PeptideRenderer,
currentEnumeration + 1,
);
stack.push({ monomerRenderer: nextMonomer.renderer as PeptideRenderer });
currentEnumeration++;
}
}

private recalculateRnaChainEnumeration(
rnaComponentRenderer: BaseMonomerRenderer,
_currentEnumeration = 1,
) {
let currentEnumeration = _currentEnumeration;
if (rnaComponentRenderer instanceof SugarRenderer) {
const rnaBaseMonomer = getRnaBaseFromSugar(
rnaComponentRenderer.monomer as Sugar,
);
if (rnaBaseMonomer instanceof RNABase) {
rnaBaseMonomer.renderer?.setEnumeration(currentEnumeration);
rnaBaseMonomer.renderer?.redrawEnumeration();
currentEnumeration++;
const stack = [{ monomerRenderer: rnaComponentRenderer }];

while (stack.length > 0) {
const stackItem = stack.pop();
assert(stackItem);
const { monomerRenderer } = stackItem;

if (monomerRenderer instanceof SugarRenderer) {
const rnaBaseMonomer = getRnaBaseFromSugar(
monomerRenderer.monomer as Sugar,
);
if (rnaBaseMonomer instanceof RNABase) {
rnaBaseMonomer.renderer?.setEnumeration(currentEnumeration);
rnaBaseMonomer.renderer?.redrawEnumeration();
currentEnumeration++;
}
}
}

const nextMonomer = getNextMonomerInChain(rnaComponentRenderer.monomer);
const nextMonomer = getNextMonomerInChain(monomerRenderer.monomer);

if (
!(nextMonomer instanceof Sugar) &&
!(nextMonomer instanceof Phosphate)
) {
return;
}
if (
!(nextMonomer instanceof Sugar) &&
!(nextMonomer instanceof Phosphate)
) {
return;
}

const isR2R1Connection = checkIsR2R1Connection(
rnaComponentRenderer.monomer,
nextMonomer,
);
const isR2R1Connection = checkIsR2R1Connection(
monomerRenderer.monomer,
nextMonomer,
);

if (
!isR2R1Connection ||
!(nextMonomer.renderer instanceof BaseMonomerRenderer)
) {
return;
}
if (
!isR2R1Connection ||
!(nextMonomer.renderer instanceof BaseMonomerRenderer)
) {
return;
}

this.recalculateRnaChainEnumeration(
nextMonomer.renderer,
currentEnumeration,
);
stack.push({
monomerRenderer: nextMonomer.renderer,
});
}
}

private recalculatePeptideEnumeration(peptideRenderer: PeptideRenderer) {
Expand Down
158 changes: 93 additions & 65 deletions packages/ketcher-core/src/domain/entities/DrawingEntitiesManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -921,16 +921,23 @@ export class DrawingEntitiesManager {
command.addOperation(operation);
rearrangedMonomersSet.add(monomer.id);

return this.reArrangeNextMonomer(
monomer,
monomerWidth,
lastPosition,
canvasWidth,
rearrangedMonomersSet,
monomersWithSideChain,
maxVerticalDistance,
const nextPositionAndVerticalDistance =
this.getNextMonomerPositionForSnakeLayout(
monomer,
monomerWidth,
lastPosition,
canvasWidth,
rearrangedMonomersSet,
monomersWithSideChain,
maxVerticalDistance,
);

return {
lastPosition: nextPositionAndVerticalDistance?.lastPosition,
maxVerticalDistance: nextPositionAndVerticalDistance?.maxVerticalDistance,
nextMonomer: getNextMonomerInChain(monomer),
command,
);
};
}

private reArrangeRnaChain(
Expand Down Expand Up @@ -988,40 +995,48 @@ export class DrawingEntitiesManager {
);
rearrangedMonomersSet.add(nucleotide.phosphate?.id);
}
const nextMonomer =
const lastMonomerInNucleotide =
nucleotide.baseMonomer === nucleotide.sugar && nucleotide.phosphate
? nucleotide.phosphate
: nucleotide.sugar;
const nextMonomerResult = this.reArrangeNextMonomer(
nextMonomer,
width,
lastPosition,
canvasWidth,
rearrangedMonomersSet,
monomersWithSideChain,
maxVerticalDistance,
command,
);
({ lastPosition, maxVerticalDistance } = nextMonomerResult);

const nextMonomerPositionAndVerticalDistance =
this.getNextMonomerPositionForSnakeLayout(
lastMonomerInNucleotide,
width,
lastPosition,
canvasWidth,
rearrangedMonomersSet,
monomersWithSideChain,
maxVerticalDistance,
);

this.setRnaBaseSideChainMonomers(
nucleotide.rnaBase,
rearrangedMonomersSet,
monomersWithSideChain,
);
return { command, lastPosition, maxVerticalDistance };
const nextMonomer = getNextMonomerInChain(lastMonomerInNucleotide);

return {
command,
lastPosition: nextMonomerPositionAndVerticalDistance?.lastPosition,
maxVerticalDistance:
nextMonomerPositionAndVerticalDistance?.maxVerticalDistance,
nextMonomer,
};
}

private reArrangeNextMonomer(
private getNextMonomerPositionForSnakeLayout(
monomer: BaseMonomer,
width: number,
lastPosition: Vec2,
canvasWidth: number,
rearrangedMonomersSet: Set<number>,
monomersWithSideChain: Array<BaseMonomer>,
maxVerticalDistance: number,
command: Command,
) {
let nextPositionAndDistance;
for (const attachmentPointName in monomer.attachmentPointsToBonds) {
const polymerBond = monomer.attachmentPointsToBonds[attachmentPointName];
const nextMonomer = polymerBond?.getAnotherMonomer(monomer);
Expand All @@ -1034,28 +1049,17 @@ export class DrawingEntitiesManager {
(attachmentPointName === 'R1' &&
nextMonomer.getAttachmentPointByBond(polymerBond) === 'R2')
) {
({ lastPosition, maxVerticalDistance } =
this.getNextPositionAndDistance(
lastPosition,
width,
maxVerticalDistance,
canvasWidth,
));
const rearrangeResult = this.reArrangeChainInRecursive(
nextMonomer,
nextPositionAndDistance = this.getNextPositionAndDistance(
lastPosition,
canvasWidth,
rearrangedMonomersSet,
monomersWithSideChain,
width,
maxVerticalDistance,
canvasWidth,
);
({ lastPosition, maxVerticalDistance } = rearrangeResult);
command.merge(rearrangeResult.command);
} else {
monomersWithSideChain.push(nextMonomer);
}
}
return { command, lastPosition, maxVerticalDistance };
return nextPositionAndDistance;
}

private setRnaBaseSideChainMonomers(
Expand Down Expand Up @@ -1210,47 +1214,71 @@ export class DrawingEntitiesManager {
}

public reArrangeChainInRecursive(
monomer: BaseMonomer,
lastPosition: Vec2,
_monomer: BaseMonomer,
_lastPosition: Vec2,
canvasWidth: number,
rearrangedMonomersSet: Set<number>,
monomersWithSideChain: Array<BaseMonomer>,
maxVerticalDistance: number,
_maxVerticalDistance: number,
) {
const command = new Command();
if (monomer instanceof Sugar || monomer instanceof Phosphate) {
const nucleotideOrNucleoside =
getNucleotideOrNucleoSideFromFirstMonomer(monomer);
if (nucleotideOrNucleoside) {
const rearrangeRnaResult = this.reArrangeRnaChain(
nucleotideOrNucleoside,
const stack = [
{
monomer: _monomer,
lastPosition: _lastPosition,
maxVerticalDistance: _maxVerticalDistance,
},
];
let lastRearrangeResult;
while (stack.length > 0) {
const stackItem = stack.pop();
assert(stackItem);
const { lastPosition, maxVerticalDistance, monomer } = stackItem;
let rearrangeResult;

if (monomer instanceof Sugar || monomer instanceof Phosphate) {
const nucleotideOrNucleoside =
getNucleotideOrNucleoSideFromFirstMonomer(monomer);
if (nucleotideOrNucleoside) {
rearrangeResult = this.reArrangeRnaChain(
nucleotideOrNucleoside,
lastPosition,
canvasWidth,
rearrangedMonomersSet,
monomersWithSideChain,
maxVerticalDistance,
);
}
} else {
rearrangeResult = this.reArrangeChain(
monomer,
lastPosition,
canvasWidth,
rearrangedMonomersSet,
monomersWithSideChain,
maxVerticalDistance,
);
command.merge(rearrangeRnaResult.command);
return {
command,
lastPosition: rearrangeRnaResult.lastPosition,
maxVerticalDistance: rearrangeRnaResult.maxVerticalDistance,
};
}

command.merge(rearrangeResult.command);

if (rearrangeResult?.lastPosition) {
lastRearrangeResult = rearrangeResult;
}

if (rearrangeResult?.nextMonomer) {
stack.push({
monomer: rearrangeResult.nextMonomer,
lastPosition: rearrangeResult.lastPosition,
maxVerticalDistance: rearrangeResult.maxVerticalDistance,
});
}
}
const rearrangeResult = this.reArrangeChain(
monomer,
lastPosition,
canvasWidth,
rearrangedMonomersSet,
monomersWithSideChain,
maxVerticalDistance,
);
command.merge(rearrangeResult.command);

return {
command,
lastPosition: rearrangeResult.lastPosition,
maxVerticalDistance: rearrangeResult.maxVerticalDistance,
lastPosition: lastRearrangeResult.lastPosition,
maxVerticalDistance: lastRearrangeResult.maxVerticalDistance,
};
}

Expand Down

0 comments on commit d964f85

Please sign in to comment.