void CmfSaver::_encodeAtom()

in core/indigo-core/molecule/src/cmf_saver.cpp [451:687]


void CmfSaver::_encodeAtom(Molecule& mol, int idx, const Array<int>& mapping)
{
    int number = 0;

    if (mol.isPseudoAtom(idx))
    {
        const char* str = mol.getPseudoAtom(idx);
        size_t len = strlen(str);

        if (len < 1)
            throw Error("empty pseudo-atom");
        if (len > 255)
            throw Error("pseudo-atom labels %d characters long are not supported (255 is the limit)", len);

        _encode(CMF_PSEUDOATOM);
        _encode((byte)len);

        do
        {
            _encode(*str);
        } while (*(++str) != 0);
    }
    else if (mol.isRSite(idx))
    {
        int bits = mol.getRSiteBits(idx);
        if (bits > 255)
        {
            _encode(CMF_RSITE_EXT);
            _output->writePackedUInt((unsigned int)bits);
        }
        else
        {
            _encode(CMF_RSITE);
            _encode(static_cast<byte>(bits));
        }
    }
    else
    {
        number = mol.getAtomNumber(idx);

        if (number <= 0 || number >= ELEM_MAX)
            throw Error("unexpected atom label");

        _encode(static_cast<byte>(number));
    }

    int charge = mol.getAtomCharge(idx);

    if (charge != 0)
    {
        int charge2 = charge - CMF_MIN_CHARGE;

        if (charge2 < 0 || charge2 >= CMF_NUM_OF_CHARGES)
        {
            _encode(CMF_CHARGE_EXT);
            int charge3 = charge + 128;
            if (charge3 < 0 || charge >= 256)
                throw Error("unexpected atom charge: %d", charge);
            _encode(static_cast<byte>(charge3));
        }
        else
            _encode(static_cast<byte>(charge2 + CMF_CHARGES));
    }

    int isotope = mol.getAtomIsotope(idx);

    if (isotope > 0)
    {
        int deviation = isotope - Element::getDefaultIsotope(number);

        if (deviation == 0)
            _encode(CMF_ISOTOPE_ZERO);
        else if (deviation == 1)
            _encode(CMF_ISOTOPE_PLUS1);
        else if (deviation == 2)
            _encode(CMF_ISOTOPE_PLUS2);
        else if (deviation == -1)
            _encode(CMF_ISOTOPE_MINUS1);
        else if (deviation == -2)
            _encode(CMF_ISOTOPE_MINUS2);
        else
        {
            deviation += 100;
            if (deviation < 0 || deviation > 255)
            {
                throw Error("unexpected %s isotope: %d", Element::toString(number), isotope);
            }
            _encode(CMF_ISOTOPE_OTHER);
            _encode(static_cast<byte>(deviation));
        }
    }

    int radical = 0;

    if (!mol.isPseudoAtom(idx) && !mol.isRSite(idx))
    {
        try
        {
            radical = mol.getAtomRadical(idx);
        }
        catch (Element::Error)
        {
        }
    }

    if (radical > 0)
    {
        if (radical == RADICAL_SINGLET)
            _encode(CMF_RADICAL_SINGLET);
        else if (radical == RADICAL_DOUBLET)
            _encode(CMF_RADICAL_DOUBLET);
        else if (radical == RADICAL_TRIPLET)
            _encode(CMF_RADICAL_TRIPLET);
        else
            throw Error("bad radical value: %d", radical);
    }

    MoleculeStereocenters& stereo = mol.stereocenters;

    int stereo_type = stereo.getType(idx);

    if (stereo_type == MoleculeStereocenters::ATOM_ANY)
        _encode(CMF_STEREO_ANY);
    else if (stereo_type != 0)
    {
        bool rigid;
        int code;
        const int* pyramid = stereo.getPyramid(idx);

        if (pyramid[3] == -1)
            rigid = MoleculeStereocenters::isPyramidMappingRigid(pyramid, 3, mapping);
        else
            rigid = MoleculeStereocenters::isPyramidMappingRigid(pyramid, 4, mapping);

        if (stereo_type == MoleculeStereocenters::ATOM_ABS)
            code = CMF_STEREO_ABS_0;
        else
        {
            int group = stereo.getGroup(idx);

            if (group < 1 || group > CMF_MAX_STEREOGROUPS)
                throw Error("stereogroup number %d out of range", group);

            if (stereo_type == MoleculeStereocenters::ATOM_AND)
                code = CMF_STEREO_AND_0 + group - 1;
            else // stereo_type == MoleculeStereocenters::ATOM_OR
                code = CMF_STEREO_OR_0 + group - 1;
        }

        if (!rigid)
            // CMF_STEREO_*_0 -> CMF_STEREO_*_1
            code += CMF_MAX_STEREOGROUPS * 2 + 1;

        _encode(static_cast<byte>(code));
    }

    if (mol.allene_stereo.isCenter(idx))
    {
        int left, right, parity, subst[4];

        mol.allene_stereo.getByAtomIdx(idx, left, right, subst, parity);
        if (subst[1] != -1 && mapping[subst[1]] != -1 && mapping[subst[1]] < mapping[subst[0]])
            parity = 3 - parity;
        if (subst[3] != -1 && mapping[subst[3]] != -1 && mapping[subst[3]] < mapping[subst[2]])
            parity = 3 - parity;
        if (parity == 1)
            _encode(CMF_STEREO_ALLENE_0);
        else
            _encode(CMF_STEREO_ALLENE_1);
    }

    int impl_h = 0;

    if (!mol.isPseudoAtom(idx) && !mol.isRSite(idx) && Molecule::shouldWriteHCount(mol, idx))
    {
        try
        {
            impl_h = mol.getImplicitH(idx);

            if (impl_h < 0 || impl_h > CMF_MAX_IMPLICIT_H)
                throw Error("implicit hydrogen count %d out of range", impl_h);

            _encode(static_cast<byte>(CMF_IMPLICIT_H + impl_h));
        }
        catch (Element::Error)
        {
        }
    }

    if (!mol.isRSite(idx) && !mol.isPseudoAtom(idx))
    {
        if (mol.isExplicitValenceSet(idx) || (mol.getAtomAromaticity(idx) == ATOM_AROMATIC && (charge != 0 || (number != ELEM_C && number != ELEM_O))))
        {
            try
            {
                int valence = mol.getAtomValence(idx);
                if (valence < 0 || valence > CMF_MAX_VALENCE)
                {
                    _encode(CMF_VALENCE_EXT);
                    _output->writePackedUInt(valence);
                }
                else
                    _encode(static_cast<byte>(CMF_VALENCE + valence));
            }
            catch (Element::Error)
            {
            }
        }
    }

    int i;

    for (i = 1; i <= mol.attachmentPointCount(); i++)
    {
        int j, aidx;

        for (j = 0; (aidx = mol.getAttachmentPoint(i, j)) != -1; j++)
            if (aidx == idx)
            {
                _encode(CMF_ATTACHPT);
                _encode(static_cast<byte>(i));
            }
    }

    if (atom_flags != 0)
    {
        int flags = atom_flags[idx];

        for (i = 0; i < CMF_NUM_OF_ATOM_FLAGS; i++)
            if (flags & (1 << i))
                _encode(static_cast<byte>(CMF_ATOM_FLAGS + i));
    }

    if (save_highlighting)
        if (mol.isAtomHighlighted(idx))
            _encode(CMF_HIGHLIGHTED);
}