public recalculateAntisenseChains()

in packages/ketcher-core/src/domain/entities/DrawingEntitiesManager.ts [2703:2833]


  public recalculateAntisenseChains() {
    const command = new Command();
    const chainsCollection = ChainsCollection.fromMonomers([
      ...this.monomers.values(),
    ]);
    const senseToAntisenseChains = new Map<Chain, Chain[]>();
    const handledChains = new Set<Chain>();

    this.monomers.forEach((monomer) => {
      command.merge(
        this.modifyMonomerItem(monomer, {
          ...monomer.monomerItem,
          isAntisense: false,
          isSense: false,
        }),
      );
    });

    chainsCollection.chains.forEach((chain) => {
      if (handledChains.has(chain)) {
        return;
      }

      let senseChain: Chain;
      const complimentaryChainsWithData =
        chainsCollection.getComplimentaryChainsWithData(chain);
      const chainsToCheck = new Set<Chain>();

      complimentaryChainsWithData.forEach((complimentaryChainWithData) => {
        const hasHydrogenBondWithRnaBase =
          complimentaryChainWithData.complimentaryChain.monomers.some(
            (monomer) => {
              return (
                (isRnaBaseOrAmbiguousRnaBase(monomer) &&
                  Boolean(getSugarFromRnaBase(monomer)) &&
                  monomer.hydrogenBonds.length > 0) ||
                monomer.hydrogenBonds.some((hydrogenBond) => {
                  const anotherMonomer =
                    hydrogenBond.getAnotherMonomer(monomer);

                  return (
                    isRnaBaseOrAmbiguousRnaBase(anotherMonomer) &&
                    Boolean(getSugarFromRnaBase(anotherMonomer))
                  );
                })
              );
            },
          );

        if (hasHydrogenBondWithRnaBase) {
          chainsToCheck.add(complimentaryChainWithData.complimentaryChain);
        }
      });

      chainsToCheck.add(chain);

      const chainToMonomers = new Map<Chain, BaseMonomer[]>();

      chainsToCheck.forEach((chainToCheck) => {
        chainToMonomers.set(chainToCheck, chainToCheck.monomers);
      });

      const largestChainsMonomersAmount = Math.max(
        ...[...chainToMonomers.values()].map((monomers) => monomers.length),
      );

      const largestChains = [...chainToMonomers.entries()].filter(
        ([, monomers]) => monomers.length === largestChainsMonomersAmount,
      );

      if (largestChains.length === 1) {
        senseChain = largestChains[0][0];
      } else {
        const chainsToCenters = new Map<Chain, Vec2>();

        largestChains.forEach(([chainToCheck, monomers]) => {
          const chainBbox = DrawingEntitiesManager.geStructureBbox(monomers);

          chainsToCenters.set(
            chainToCheck,
            new Vec2(
              chainBbox.left + chainBbox.width / 2,
              chainBbox.top + chainBbox.height / 2,
            ),
          );
        });

        const chainsToCenterArray = [...chainsToCenters.entries()];
        const chainWithLowestCenter = chainsToCenterArray.reduce(
          ([previousChain, previousChainCenter], [chainToCheck, center]) => {
            return center.y < previousChainCenter.y
              ? [chainToCheck, center]
              : [previousChain, previousChainCenter];
          },
          chainsToCenterArray[0],
        );

        senseChain = chainWithLowestCenter[0];
      }

      chainsToCheck.forEach((chainToCheck) => {
        if (chainToCheck === senseChain) {
          handledChains.add(chainToCheck);

          return;
        }

        if (!senseToAntisenseChains.has(senseChain)) {
          senseToAntisenseChains.set(senseChain, []);
        }

        senseToAntisenseChains.get(senseChain)?.push(chainToCheck);

        handledChains.add(chainToCheck);
      });
    });

    senseToAntisenseChains.forEach((antisenseChains, senseChain) => {
      senseChain.monomers.forEach((monomer) => {
        command.merge(this.markMonomerAsSense(monomer));
      });

      antisenseChains.forEach((antisenseChain) => {
        antisenseChain.monomers.forEach((monomer) => {
          command.merge(this.markMonomerAsAntisense(monomer));
        });
      });
    });

    return command;
  }