in packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts [236:438]
private appendSideConnectionBond(rootElement, cells: Cell[]) {
const firstCell = cells[0];
const firstCellConnection = firstCell.connections.find(
(connection: Connection): boolean => {
return connection.polymerBond === this.polymerBond;
},
) as Connection;
const isVerticalConnection = firstCellConnection.isVertical;
const isStraightVerticalConnection =
(cells.length === 2 ||
cells.reduce(
(isStraight: boolean, cell: Cell, index: number): boolean => {
if (!isStraight || index === 0 || index === cells.length - 1) {
return isStraight;
}
return cell.x === firstCell.x && !cell.monomer;
},
true,
)) &&
isVerticalConnection;
const isFirstMonomerOfBondInFirstCell = firstCell.node?.monomers.includes(
this.polymerBond.firstMonomer,
);
const isTwoNeighborRowsConnection = cells.every(
(cell) => cell.y === firstCell.y || cell.y === firstCell.y + 1,
);
const startPosition = isFirstMonomerOfBondInFirstCell
? this.scaledPosition.startPosition
: this.scaledPosition.endPosition;
const endPosition = isFirstMonomerOfBondInFirstCell
? this.scaledPosition.endPosition
: this.scaledPosition.startPosition;
const xDirection =
startPosition.x >= (this.sideConnectionBondTurnPoint || endPosition.x)
? 180
: 0;
let dAttributeForPath = `M ${startPosition.x},${startPosition.y} `;
const cos = Math.cos((xDirection * Math.PI) / 180);
let previousConnection: Connection;
let previousCell: Cell;
const horizontalPartIntersectionsOffset = firstCellConnection.xOffset;
const areCellsOnSameRow = cells.every((cell) => {
return cell.y === firstCell.y;
});
const isSecondCellEmpty = cells[1].node === null;
if (areCellsOnSameRow) {
dAttributeForPath += `L ${startPosition.x},${
startPosition.y -
BOND_END_LENGTH -
horizontalPartIntersectionsOffset * 3
} `;
dAttributeForPath += generateBend(0, -1, cos, -1);
} else {
dAttributeForPath += `L ${startPosition.x},${
startPosition.y +
BOND_END_LENGTH +
horizontalPartIntersectionsOffset * 3
} `;
if (
!isStraightVerticalConnection &&
!isSecondCellEmpty &&
!isTwoNeighborRowsConnection
) {
dAttributeForPath += generateBend(0, 1, cos, 1);
}
}
if (isVerticalConnection && !isStraightVerticalConnection) {
dAttributeForPath += this.drawPartOfSideConnection(
true,
firstCellConnection,
firstCell,
this.sideConnectionBondTurnPoint &&
startPosition.x < this.sideConnectionBondTurnPoint
? 0
: 180,
);
}
let maxHorizontalOffset = 0;
cells.forEach((cell: Cell, cellIndex: number): void => {
const cellConnection = cell.connections.find(
(connection: Connection): boolean => {
return connection.polymerBond === this.polymerBond;
},
) as Connection;
const isLastCell = cellIndex === cells.length - 1;
const _xDirection = this.sideConnectionBondTurnPoint
? endPosition.x < this.sideConnectionBondTurnPoint
? 180
: 0
: xDirection;
const maxXOffset = cell.connections.reduce(
(max: number, connection: Connection): number => {
return connection.isVertical || max > connection.xOffset
? max
: connection.xOffset;
},
0,
);
maxHorizontalOffset =
maxHorizontalOffset > maxXOffset ? maxHorizontalOffset : maxXOffset;
if (isLastCell) {
if (isStraightVerticalConnection) {
return;
}
const directionObject =
cellConnection.direction as ConnectionDirectionOfLastCell;
const yDirection = isVerticalConnection ? 90 : directionObject.y;
const sin = Math.sin((yDirection * Math.PI) / 180);
const cos = Math.cos((_xDirection * Math.PI) / 180);
if (!areCellsOnSameRow) {
dAttributeForPath += `V ${
endPosition.y -
CELL_HEIGHT / 2 -
SMOOTH_CORNER_SIZE -
sin * (cellConnection.yOffset || 0) * 3 -
(isTwoNeighborRowsConnection
? maxHorizontalOffset - cellConnection.xOffset
: cellConnection.xOffset) *
3
} `;
dAttributeForPath += generateBend(0, sin, cos, 1);
}
dAttributeForPath += `H ${endPosition.x - SMOOTH_CORNER_SIZE * cos} `;
dAttributeForPath += generateBend(cos, 0, cos, 1);
return;
}
// empty cells
if (cell.node === null) {
return;
}
// other cells
if (
previousConnection &&
previousConnection.direction !== cellConnection.direction
) {
const isHorizontal =
previousConnection.direction === 0 ||
previousConnection.direction === 180;
dAttributeForPath += this.drawPartOfSideConnection(
isHorizontal,
previousConnection,
previousCell,
// FIXME: Check. Is it correct to use `as ConnectionDirectionInDegrees` here?
isHorizontal
? xDirection
: (previousConnection.direction as ConnectionDirectionInDegrees),
);
}
previousCell = cell;
previousConnection = cellConnection;
});
dAttributeForPath += `L ${endPosition.x},${endPosition.y} `;
this.bodyElement = rootElement
.append('path')
.attr('class', `${SIDE_CONNECTION_BODY_ELEMENT_CLASS}`)
.attr('stroke', this.isHydrogenBond ? '#333333' : '#43B5C0')
.attr('stroke-width', 1)
.attr('d', dAttributeForPath)
.attr('fill', 'none')
.attr('stroke-dasharray', this.isHydrogenBond ? '2' : '0')
.attr('pointer-events', 'all')
.attr('data-testid', 'bond')
.attr('data-bondtype', this.isHydrogenBond ? 'hydrogen' : 'covalent')
.attr('data-bondid', this.polymerBond.id)
.attr('data-frommonomerid', this.polymerBond.firstMonomer.id)
.attr('data-tomonomerid', this.polymerBond.secondMonomer?.id);
if (!this.isHydrogenBond && this.bodyElement) {
this.bodyElement
.attr(
'data-fromconnectionpoint',
this.polymerBond.firstMonomer.getAttachmentPointByBond(
this.polymerBond,
) || '',
)
.attr(
'data-toconnectionpoint',
this.polymerBond.secondMonomer?.getAttachmentPointByBond(
this.polymerBond,
) || '',
);
}
this.path = dAttributeForPath;
return this.bodyElement;
}