core/indigo-core/molecule/meta_commons.h (448 lines of code) (raw):
/****************************************************************************
* Copyright (C) from 2009 to Present EPAM Systems.
*
* This file is part of Indigo toolkit.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
#ifndef __meta_commons_h__
#define __meta_commons_h__
#include <exception>
#include <functional>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <string>
#include <unordered_map>
#include "common/math/algebra.h"
#include "graph/graph.h"
#include "molecule/molecule_cip_calculator.h"
#include "molecule/parse_utils.h"
#include "molecule/query_molecule.h"
#include "reaction/base_reaction.h"
namespace indigo
{
const double KDefaultFontSize = 13;
const double KFontScaleFactor = 47;
const auto KFontBoldStr = "BOLD";
const auto KFontItalicStr = "ITALIC";
const auto KFontSuperscriptStr = "SUPERSCRIPT";
const auto KFontSubscriptStr = "SUBSCRIPT";
const auto KFontCustomSizeStr = "CUSTOM_FONT_SIZE";
const auto KImagePNG = "image/png";
const auto KImageSVG = "image/svg+xml";
const uint8_t KReactantArea = 0;
const uint8_t KReagentUpArea = 1;
const uint8_t KReagentDownArea = 2;
const uint8_t KProductArea = 3;
const Vec2f MIN_MOL_SIZE = {0.5, 0.5};
struct compareFunction
{
bool operator()(const std::pair<int, bool>& a, const std::pair<int, bool>& b) const
{
return a.second == b.second ? a.first < b.first : a.second < b.second;
}
};
using FONT_STYLE_SET = std::set<std::pair<int, bool>, compareFunction>;
constexpr std::uint32_t string_hash(char const* s, std::size_t count)
{
return ((count ? string_hash(s, count - 1) : 2166136261u) ^ s[count]) * 16777619u;
}
constexpr std::uint32_t operator"" _hash(char const* s, std::size_t count)
{
return string_hash(s, count);
}
uint8_t getPointSide(const Vec2f& point, const Vec2f& beg, const Vec2f& end);
CIPDesc stringToCIP(const std::string& cip_str);
std::string CIPToString(CIPDesc cip);
bool isCIPSGroup(SGroup& sgroup);
void getSGroupAtoms(BaseMolecule& mol, std::list<std::unordered_set<int>>& neighbors);
std::string convertAPToHELM(const std::string& atp_id_str);
std::string convertAPFromHELM(const std::string& atp_id_str);
class SimpleGraphicsObject : public MetaObject
{
public:
static const std::uint32_t CID = "Metadata simple object"_hash;
SimpleGraphicsObject(int mode, const std::pair<Vec2f, Vec2f>& coords) : MetaObject(CID)
{
_mode = mode;
_coordinates = coords;
};
void getBoundingBox(Rect2f& bbox) const override
{
bbox = Rect2f(_coordinates.first, _coordinates.second);
}
MetaObject* clone() const override
{
return new SimpleGraphicsObject(_mode, _coordinates);
}
void offset(const Vec2f& offset) override
{
_coordinates.first += offset;
_coordinates.second += offset;
}
enum
{
EEllipse,
ERectangle,
ELine
};
int _mode;
std::pair<Vec2f, Vec2f> _coordinates;
};
struct SimpleTextStyle
{
std::size_t offset;
std::size_t size;
std::list<std::string> styles;
};
struct SimpleTextLine
{
std::string text;
std::list<SimpleTextStyle> text_styles;
};
class SimpleTextObject : public MetaObject
{
public:
enum
{
EPlain = 0,
EBold = 1,
EItalic = 2,
ESuperScript = 3,
ESubScript = 4,
EFontSize = 5
};
const std::unordered_map<std::string, int> KTextStylesMap{
{KFontBoldStr, EBold}, {KFontItalicStr, EItalic}, {KFontSuperscriptStr, ESuperScript}, {KFontSubscriptStr, ESubScript}};
struct SimpleTextLine
{
std::string text;
std::map<std::size_t, FONT_STYLE_SET> styles;
};
static const std::uint32_t CID = "Simple text object"_hash;
SimpleTextObject(const Vec3f& pos, const Vec2f& sz, const std::string& content);
MetaObject* clone() const override
{
return new SimpleTextObject(_pos, _size, _content);
}
void getBoundingBox(Rect2f& bbox) const override
{
bbox = Rect2f(Vec2f(_pos.x, _pos.y), Vec2f(_pos.x + _size.x, _pos.y - _size.y));
}
void offset(const Vec2f& offset) override
{
_pos.x += offset.x;
_pos.y += offset.y;
}
const auto& getLines() const
{
return _block;
}
std::string _content;
std::list<SimpleTextLine> _block;
Vec3f _pos;
Vec2f _size;
};
class SimpleTextObjectBuilder
{
public:
SimpleTextObjectBuilder();
void addLine(const SimpleTextLine& line);
void finalize();
std::string getJsonString() const;
int getLineCounter() const;
private:
rapidjson::Writer<rapidjson::StringBuffer> _writer;
rapidjson::StringBuffer _buffer;
int _line_counter;
};
class ReactionArrowObject : public MetaObject
{
public:
static const std::uint32_t CID = "Reaction arrow object"_hash;
enum
{
EOpenAngle = 2,
EFilledTriangle,
EFilledBow,
EDashedOpenAngle,
EFailed,
EBothEndsFilledTriangle,
EEquilibriumFilledHalfBow,
EEquilibriumFilledTriangle,
EEquilibriumOpenAngle,
EUnbalancedEquilibriumFilledHalfBow,
EUnbalancedEquilibriumLargeFilledHalfBow,
EUnbalancedEquilibriumOpenHalfAngle,
EUnbalancedEquilibriumFilledHalfTriangle,
EEllipticalArcFilledBow,
EEllipticalArcFilledTriangle,
EEllipticalArcOpenAngle,
EEllipticalArcOpenHalfAngle,
ERetrosynthetic
};
ReactionArrowObject(int arrow_type, const Vec2f& begin, const Vec2f& end, float height = 0)
: MetaObject(CID), _arrow_type(arrow_type), _begin(begin), _end(end), _height(height){};
MetaObject* clone() const override
{
return new ReactionArrowObject(_arrow_type, _begin, _end, _height);
}
void getBoundingBox(Rect2f& bbox) const override
{
bbox = Rect2f(_begin, _end);
}
int getArrowType() const
{
return _arrow_type;
}
float getHeight() const
{
return _height;
}
const auto& getHead() const
{
return _end;
}
const auto& getTail() const
{
return _begin;
}
void offset(const Vec2f& offset) override
{
_begin += offset;
_end += offset;
}
private:
int _arrow_type;
float _height;
Vec2f _begin;
Vec2f _end;
};
class ReactionMultitailArrowObject : public MetaObject
{
static const int CORRECT_CONSTRUCTOR_PARAMETERS_SIZE = 5;
static const int CORRECT_HEAD_SIZE = 1;
static const int CORRECT_TAIL_SIZE = 2;
public:
static const std::uint32_t CID = "Reaction multitail arrow object"_hash;
static constexpr float TAIL_ARC_RADIUS = .15f;
template <typename Iterator>
ReactionMultitailArrowObject(Iterator&& begin, Iterator&& end) : MetaObject(CID)
{
auto distanceBetweenBeginAndEnd = std::distance(begin, end);
if (distanceBetweenBeginAndEnd < CORRECT_CONSTRUCTOR_PARAMETERS_SIZE)
throw Exception("ReactionMultitailArrowObject: invalid arguments");
_head = *begin++;
_tails.reserve(static_cast<int>(distanceBetweenBeginAndEnd) - CORRECT_HEAD_SIZE - CORRECT_TAIL_SIZE);
while (begin != end)
_tails.push(*begin++);
_spine_begin = _tails.pop();
_spine_end = _tails.pop();
}
ReactionMultitailArrowObject(Vec2f head, const Array<Vec2f>& tails, Vec2f spine_begin, Vec2f spine_end)
: MetaObject(CID), _head(head), _spine_begin(spine_begin), _spine_end(spine_end)
{
if (tails.size() < CORRECT_TAIL_SIZE)
throw Exception("ReactionMultitailArrowObject: invalid arguments");
_tails.copy(tails);
}
MetaObject* clone() const override
{
return new ReactionMultitailArrowObject(_head, _tails, _spine_begin, _spine_end);
}
auto getHead() const
{
return _head;
}
auto& getTails() const
{
return _tails;
}
auto getSpineBegin() const
{
return _spine_begin;
}
auto getSpineEnd() const
{
return _spine_end;
}
void getBoundingBox(Rect2f& bbox) const override
{
float min_left = 0;
for (int i = 0; i < _tails.size(); ++i)
{
if (i == 0)
min_left = _tails[i].x;
else
min_left = std::min(min_left, _tails[i].x);
}
float max_top = _spine_begin.y;
float min_bottom = _spine_end.y;
float max_right = _head.x;
bbox = Rect2f(Vec2f(min_left, max_top), Vec2f(max_right, min_bottom));
}
void offset(const Vec2f& offset) override
{
_head += offset;
_spine_begin += offset;
_spine_end += offset;
for (auto& tail : _tails)
tail += offset;
}
private:
Vec2f _head;
Array<Vec2f> _tails;
Vec2f _spine_begin, _spine_end;
};
class ReactionPlusObject : public MetaObject
{
public:
static const std::uint32_t CID = "Reaction plus object"_hash;
ReactionPlusObject(const Vec2f& pos) : MetaObject(CID), _pos(pos){};
MetaObject* clone() const override
{
return new ReactionPlusObject(_pos);
}
enum
{
EKETEllipse,
EKETRectangle,
EKETLine
};
const auto& getPos() const
{
return _pos;
}
void getBoundingBox(Rect2f& bbox) const override
{
bbox = Rect2f(_pos, _pos);
}
void offset(const Vec2f& offset) override
{
_pos += offset;
}
private:
Vec2f _pos;
};
class EmbeddedImageObject : public MetaObject
{
public:
enum ImageFormat
{
EKETPNG,
EKETSVG
};
static const std::uint32_t CID = "Embedded image object"_hash;
EmbeddedImageObject(const Rect2f& bbox, EmbeddedImageObject::ImageFormat format, const std::string& data, bool is_base64 = true);
MetaObject* clone() const override
{
return new EmbeddedImageObject(_bbox, _image_format, getBase64());
}
auto& getBoundingBox() const
{
return _bbox;
}
std::string getBase64() const;
const std::string& getData() const
{
return _image_data;
}
ImageFormat getFormat() const
{
return _image_format;
}
void getBoundingBox(Rect2f& bbox) const override
{
bbox = _bbox;
}
void offset(const Vec2f& offset) override
{
_bbox = Rect2f(_bbox.leftBottom() + offset, _bbox.rightTop() + offset);
}
private:
Rect2f _bbox;
std::string _image_data;
ImageFormat _image_format;
};
struct MolSumm
{
MolSumm() : bbox(Vec2f(0, 0), Vec2f(0, 0)), role(BaseReaction::UNDEFINED), reaction_idx(-1){};
MolSumm(const Rect2f& box) : bbox(box), role(BaseReaction::UNDEFINED), reaction_idx(-1){};
Rect2f bbox;
std::vector<int> indexes;
int role;
int reaction_idx;
std::vector<int> arrows_to;
std::vector<int> arrows_from;
};
struct PathwayComponent
{
int product_csb_idx;
std::vector<int> reactant_csb_indexes;
};
struct ReactionComponent
{
enum
{
MOLECULE = 0,
PLUS,
ARROW_BASIC,
ARROW_FILLED_TRIANGLE,
ARROW_FILLED_BOW,
ARROW_DASHED,
ARROW_FAILED,
ARROW_BOTH_ENDS_FILLED_TRIANGLE,
ARROW_EQUILIBRIUM_FILLED_HALF_BOW,
ARROW_EQUILIBRIUM_FILLED_TRIANGLE,
ARROW_EQUILIBRIUM_OPEN_ANGLE,
ARROW_UNBALANCED_EQUILIBRIUM_FILLED_HALF_BOW,
ARROW_UNBALANCED_EQUILIBRIUM_LARGE_FILLED_HALF_BOW,
ARROW_UNBALANCED_EQUILIBRIUM_OPEN_HALF_ANGLE,
ARROW_UNBALANCED_EQUILIBRIUM_FILLED_HALF_TRIANGLE,
ARROW_ELLIPTICAL_ARC_FILLED_BOW,
ARROW_ELLIPTICAL_ARC_FILLED_TRIANGLE,
ARROW_ELLIPTICAL_ARC_OPEN_ANGLE,
ARROW_ELLIPTICAL_ARC_OPEN_HALF_ANGLE,
ARROW_RETROSYNTHETIC,
ARROW_MULTITAIL
};
enum
{
NOT_CONNECTED = -2,
CONNECTED = -1
};
ReactionComponent(int ctype, const Rect2f& box, int idx, std::unique_ptr<BaseMolecule> mol)
: component_type(ctype), bbox(box), molecule(std::move(mol)), summ_block_idx(NOT_CONNECTED), index(idx){};
int component_type;
Rect2f bbox;
std::unique_ptr<BaseMolecule> molecule;
std::list<MolSumm>::iterator summ_block_it;
int summ_block_idx;
std::vector<Vec2f> coordinates;
int index;
};
// hash for pairs taken from boost library
struct pair_int_hash
{
private:
const std::hash<int> ah;
const std::hash<int> bh;
public:
pair_int_hash() : ah(), bh()
{
}
size_t operator()(const std::pair<int, int>& p) const
{
size_t seed = ah(p.first);
return bh(p.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
};
struct commutative_pair_int_hash
{
pair_int_hash pih;
public:
size_t operator()(const std::pair<int, int>& p) const
{
std::pair<int, int> sorted_pair(p);
if (sorted_pair.first > sorted_pair.second)
std::swap(sorted_pair.first, sorted_pair.second);
auto c_val = pih(sorted_pair);
return c_val;
}
};
std::string getDebugSmiles(BaseMolecule& mol);
}
#endif