in packages/ketcher-core/src/domain/entities/DrawingEntitiesManager.ts [1615:1806]
public applySnakeLayout(
canvasWidth: number,
isSnakeMode: boolean,
needRedrawBonds = true,
needRepositionMonomers = true,
) {
if (this.monomers.size === 0) {
return new Command();
}
const previousSnakeLayoutMatrix = this.snakeLayoutMatrix;
const command = new Command();
let chainsCollection: ChainsCollection;
command.merge(this.recalculateAntisenseChains());
if (isSnakeMode) {
const rearrangedMonomersSet: Set<number> = new Set();
let lastPosition = new Vec2({
x: MONOMER_START_X_POSITION,
y: MONOMER_START_Y_POSITION,
});
let maxVerticalDistance = 0;
chainsCollection = ChainsCollection.fromMonomers([
...this.monomers.values(),
]);
chainsCollection.rearrange();
chainsCollection.chains.forEach((chain) => {
if (chain.isAntisense) {
return;
}
const complimentaryChainsWithData =
chainsCollection.getComplimentaryChainsWithData(chain);
const antisenseChainsWithData = complimentaryChainsWithData.filter(
(complimentaryChainWithData) =>
complimentaryChainWithData.complimentaryChain.firstMonomer
?.monomerItem.isAntisense,
);
const antisenseChainsStartIndexes = antisenseChainsWithData.map(
(antisenseChainWithData) => {
const firstConnectedAntisenseNodeIndex =
antisenseChainWithData.complimentaryChain.nodes.findIndex(
(node) => {
return (
node ===
antisenseChainWithData.firstConnectedComplimentaryNode
);
},
);
const senseNodeIndex = chain.nodes.indexOf(
antisenseChainWithData.firstConnectedNode,
);
if (!isNumber(senseNodeIndex)) {
return -1;
}
return senseNodeIndex - firstConnectedAntisenseNodeIndex;
},
);
const antisenseChainsStartIndexesMap = new Map(
antisenseChainsStartIndexes.map(
(antisenseChainsStartIndex, index) => [
antisenseChainsStartIndex,
antisenseChainsWithData[index],
],
),
);
let restOfRowsWithAntisense = 0;
let isPreviousChainWithAntisense = false;
for (
let nodeIndex = Math.min(0, ...antisenseChainsStartIndexes);
nodeIndex < chain.length;
nodeIndex++
) {
const node = chain.nodes[nodeIndex];
if (node && rearrangedMonomersSet.has(node.monomer.id)) {
return;
}
const antisenseChainWithData =
antisenseChainsStartIndexesMap.get(nodeIndex);
if (antisenseChainWithData) {
const { rowsUsedByAntisense, command: rearrangedAntisenseCommand } =
this.rearrangeAntisenseChain(
antisenseChainWithData.complimentaryChain,
lastPosition,
canvasWidth,
rearrangedMonomersSet,
maxVerticalDistance,
needRepositionMonomers,
);
restOfRowsWithAntisense = rowsUsedByAntisense;
command.merge(rearrangedAntisenseCommand);
isPreviousChainWithAntisense = true;
}
if (!node) {
continue;
}
const r2PolymerBond =
node.lastMonomerInNode.attachmentPointsToBonds[
AttachmentPointName.R2
];
if (r2PolymerBond instanceof PolymerBond) {
r2PolymerBond.restOfRowsWithAntisense = restOfRowsWithAntisense;
}
if (node instanceof Nucleoside || node instanceof Nucleotide) {
const rearrangeResult = this.reArrangeRnaChain(
node,
lastPosition,
canvasWidth,
rearrangedMonomersSet,
maxVerticalDistance,
restOfRowsWithAntisense,
false,
needRepositionMonomers,
);
if (
rearrangeResult.lastPosition.y > lastPosition.y &&
lastPosition
) {
restOfRowsWithAntisense--;
}
lastPosition = rearrangeResult.lastPosition;
maxVerticalDistance = rearrangeResult.maxVerticalDistance;
command.merge(rearrangeResult.command);
} else {
node.monomers.forEach((monomer) => {
const rearrangeResult = this.reArrangeChain(
monomer,
lastPosition,
canvasWidth,
rearrangedMonomersSet,
maxVerticalDistance,
restOfRowsWithAntisense,
needRepositionMonomers,
);
if (rearrangeResult.lastPosition.y > lastPosition.y) {
restOfRowsWithAntisense--;
}
lastPosition = rearrangeResult.lastPosition;
maxVerticalDistance = rearrangeResult.maxVerticalDistance;
command.merge(rearrangeResult.command);
});
}
}
lastPosition = getFirstPosition(maxVerticalDistance, lastPosition);
maxVerticalDistance = 0;
if (isPreviousChainWithAntisense) {
lastPosition = lastPosition.add(
new Vec2(0, SNAKE_LAYOUT_Y_OFFSET_BETWEEN_CHAINS),
);
isPreviousChainWithAntisense = false;
}
});
const snakeLayoutMatrix =
this.calculateSnakeLayoutMatrix(chainsCollection);
this.snakeLayoutMatrix = snakeLayoutMatrix;
command.merge(
this.recalculateCanvasMatrix(
chainsCollection,
previousSnakeLayoutMatrix,
),
);
}
if (needRedrawBonds) {
command.merge(this.redrawBonds());
}
return command;
}