diff --git a/api/c/indigo/src/indigo_reaction.cpp b/api/c/indigo/src/indigo_reaction.cpp index d7707937fe..8c45094e58 100644 --- a/api/c/indigo/src/indigo_reaction.cpp +++ b/api/c/indigo/src/indigo_reaction.cpp @@ -142,6 +142,7 @@ IndigoReaction::~IndigoReaction() void IndigoReaction::init(std::unique_ptr&& reaction) { rxn = reaction ? std::move(reaction) : std::make_unique(); + _properties.copy(rxn->properties()); } Reaction& IndigoReaction::getReaction() diff --git a/core/indigo-core/layout/pathway_layout.h b/core/indigo-core/layout/pathway_layout.h index ab150c0d44..20f613fa9c 100644 --- a/core/indigo-core/layout/pathway_layout.h +++ b/core/indigo-core/layout/pathway_layout.h @@ -64,7 +64,7 @@ namespace indigo : _reaction(reaction), _depths(MAX_DEPTHS, 0), _maxDepth(0), _bond_length(options.DEFAULT_BOND_LENGTH), _default_arrow_size((float)options.DEFAULT_BOND_LENGTH * ARROW_LENGTH_FACTOR), _reaction_margin_size(options.reactionComponentMarginSize / options.ppi), _preserve_molecule_layout(true), - _text_height((float)options.DEFAULT_BOND_LENGTH * TEXT_LINE_HEIGHT) + _text_line_height((float)options.DEFAULT_BOND_LENGTH * TEXT_LINE_HEIGHT) { } @@ -108,7 +108,7 @@ namespace indigo mol.scale(center, bondLength / mean); } Rect2f boundingBox; - mol.getBoundingBox(boundingBox); + mol.getBoundingBox(boundingBox, Vec2f(bondLength, bondLength)); molecules.push_back(std::make_pair(reactantIdx, boundingBox)); width = boundingBox.width(); height = boundingBox.height(); @@ -263,7 +263,7 @@ namespace indigo std::vector _layoutRootItems; const float _bond_length; - const float _text_height; + const float _text_line_height; const float _default_arrow_size; const float _reaction_margin_size; bool _preserve_molecule_layout; diff --git a/core/indigo-core/layout/src/pathway_layout.cpp b/core/indigo-core/layout/src/pathway_layout.cpp index 5a4efc4ec5..734df6d1c4 100644 --- a/core/indigo-core/layout/src/pathway_layout.cpp +++ b/core/indigo-core/layout/src/pathway_layout.cpp @@ -152,15 +152,18 @@ void PathwayLayout::buildLayoutTree() auto totalHeight = std::accumulate(currentLayoutItem.children.begin(), currentLayoutItem.children.end(), 0.0f, [](float summ, const PathwayLayoutItem* item) { return summ + item->height; }); + auto totalSpacing = (currentLayoutItem.children.size() - 1) * VERTICAL_SPACING; totalHeight -= currentLayoutItem.children.front()->height / 2; totalHeight -= currentLayoutItem.children.back()->height / 2; totalHeight /= 2.0f; - float targetHeight = totalLines * _text_height + _reaction_margin_size; + float targetHeight = totalLines * _text_line_height + _reaction_margin_size; if (totalHeight < targetHeight) { - auto adjustHeight = (targetHeight - totalHeight) / (currentLayoutItem.children.size() - 1); for (auto& child : currentLayoutItem.children) - child->height += adjustHeight * TEXT_ADJUSTMENT; // should be 2.0f + { + child->height *= (targetHeight - totalSpacing / 2); + child->height /= totalHeight; + } } } } @@ -197,7 +200,7 @@ void PathwayLayout::copyTextPropertiesToNode(const PathwayReaction::SimpleReacti } } - if (node.name_text.size()) + if (node.name_text.size() && node.conditions_text.size()) node.name_text.push().readString("", true); } @@ -306,7 +309,7 @@ void PathwayLayout::applyLayout() arrows.insert(arrows.end(), tails.begin(), tails.end()); // add spines - float text_height_limit = MIN_LINES_COUNT * _text_height; + float text_height_limit = MIN_LINES_COUNT * _text_line_height; auto& node = _reaction.getReactionNode(layoutItem->reactionIndex); Vec2f textPos_bl(0, head.y); if (tails.size() > 1) @@ -341,12 +344,12 @@ void PathwayLayout::generateTextBlocks(SimpleTextObjectBuilder& tob, const ObjAr { for (int i = 0; i < props.size(); ++i) { - if (height > _text_height) + if (std::ceil(height * 1000) >= std::ceil(_text_line_height * 1000)) { - height -= _text_height; + height -= _text_line_height; SimpleTextLine textLine; textLine.text = props[i].ptr(); - if (height < _text_height && props.size() - i > 1) + if (std::ceil(height * 1000) < std::ceil(_text_line_height * 1000) && props.size() - i > 1) textLine.text += "..."; auto& ts = textLine.text_styles.emplace_back(); ts.offset = 0; @@ -365,8 +368,9 @@ void PathwayLayout::addMetaText(PathwayReaction::ReactionNode& node, const Vec2f generateTextBlocks(tob, node.name_text, KFontBoldStr, height_limit); generateTextBlocks(tob, node.conditions_text, KFontItalicStr, height_limit); tob.finalize(); - Vec3f text_pos_lr(text_pos_bl.x, text_pos_bl.y + _text_height / 2 + (text_height_limit - height_limit), 0.0f); - _reaction.meta().addMetaObject(new SimpleTextObject(text_pos_lr, tob.getJsonString()), true); + auto text_height = _text_line_height / 2 + (text_height_limit - height_limit); + Vec3f text_pos_tr(text_pos_bl.x, text_pos_bl.y + text_height, 0.0f); + _reaction.meta().addMetaObject(new SimpleTextObject(text_pos_tr, Vec2f(node.text_width, text_height), tob.getJsonString()), true); } std::vector PathwayLayout::splitText(const std::string& text, float max_width, std::function symbol_width) diff --git a/core/indigo-core/molecule/meta_commons.h b/core/indigo-core/molecule/meta_commons.h index f1b6abf999..79b1b20692 100644 --- a/core/indigo-core/molecule/meta_commons.h +++ b/core/indigo-core/molecule/meta_commons.h @@ -159,16 +159,16 @@ namespace indigo static const std::uint32_t CID = "Simple text object"_hash; - SimpleTextObject(const Vec3f& pos, const std::string& content); + SimpleTextObject(const Vec3f& pos, const Vec2f& sz, const std::string& content); MetaObject* clone() const override { - return new SimpleTextObject(_pos, _content); + return new SimpleTextObject(_pos, _size, _content); } void getBoundingBox(Rect2f& bbox) const override { - bbox = Rect2f(Vec2f(_pos.x, _pos.y), Vec2f(_pos.x, _pos.y)); + bbox = Rect2f(Vec2f(_pos.x, _pos.y), Vec2f(_pos.x + _size.x, _pos.y - _size.y)); } void offset(const Vec2f& offset) override @@ -185,6 +185,7 @@ namespace indigo std::string _content; std::list _block; Vec3f _pos; + Vec2f _size; }; class SimpleTextObjectBuilder diff --git a/core/indigo-core/molecule/molecule_cdxml_loader.h b/core/indigo-core/molecule/molecule_cdxml_loader.h index 6de550cae4..cf199214fa 100644 --- a/core/indigo-core/molecule/molecule_cdxml_loader.h +++ b/core/indigo-core/molecule/molecule_cdxml_loader.h @@ -176,6 +176,16 @@ namespace indigo bool is_superatom; }; + struct CdxmlText + { + CdxmlText(const Vec3f& pos, const Vec2f& size, const std::string& text) : pos(pos), size(size), text(text) + { + } + std::string text; + Vec3f pos; + Vec2f size; + }; + class BaseCDXProperty { public: @@ -805,7 +815,7 @@ namespace indigo std::vector nodes; std::vector bonds; std::vector brackets; - std::vector> text_objects; + std::vector text_objects; static const int SCALE = 30; @@ -820,7 +830,7 @@ namespace indigo void _parseBond(CdxmlBond& bond, BaseCDXProperty& prop); void _parseBracket(CdxmlBracket& bracket, BaseCDXProperty& prop); - void _parseText(BaseCDXElement& elem, std::vector>& text_parsed); + void _parseText(BaseCDXElement& elem, std::vector& text_parsed); void _parseLabel(BaseCDXElement& elem, std::string& label); void _parseGraphic(BaseCDXElement& elem); diff --git a/core/indigo-core/molecule/molecule_json_saver.h b/core/indigo-core/molecule/molecule_json_saver.h index 2d3d16431a..0dfd664d23 100644 --- a/core/indigo-core/molecule/molecule_json_saver.h +++ b/core/indigo-core/molecule/molecule_json_saver.h @@ -66,6 +66,8 @@ namespace indigo int getMonomerNumber(int mon_idx); void writeFloat(JsonWriter& writer, float f_value); + void writePos(JsonWriter& writer, const Vec3f& pos); + void saveAtoms(BaseMolecule& mol, JsonWriter& writer); void saveBonds(BaseMolecule& mol, JsonWriter& writer); void saveRGroup(PtrPool& fragments, int rgnum, JsonWriter& writer); diff --git a/core/indigo-core/molecule/src/meta_commons.cpp b/core/indigo-core/molecule/src/meta_commons.cpp index 0f1e270182..d09d41adc0 100644 --- a/core/indigo-core/molecule/src/meta_commons.cpp +++ b/core/indigo-core/molecule/src/meta_commons.cpp @@ -162,10 +162,11 @@ namespace indigo return base64::encode(_image_data.c_str(), _image_data.size()); } - SimpleTextObject::SimpleTextObject(const Vec3f& pos, const std::string& content) : MetaObject(CID) + SimpleTextObject::SimpleTextObject(const Vec3f& pos, const Vec2f& sz, const std::string& content) : MetaObject(CID) { using namespace rapidjson; _pos = pos; + _size = sz; _content = content; Document data; data.Parse(content.c_str()); diff --git a/core/indigo-core/molecule/src/molecule_cdxml_loader.cpp b/core/indigo-core/molecule/src/molecule_cdxml_loader.cpp index d22d0b5f00..a6ad869d81 100644 --- a/core/indigo-core/molecule/src/molecule_cdxml_loader.cpp +++ b/core/indigo-core/molecule/src/molecule_cdxml_loader.cpp @@ -509,7 +509,7 @@ void MoleculeCdxmlLoader::_parseCollections(BaseMolecule& mol) _addBracket(mol, brk); for (const auto& to : text_objects) - mol.meta().addMetaObject(new SimpleTextObject(to.first, to.second)); + mol.meta().addMetaObject(new SimpleTextObject(to.pos, to.size, to.text)); for (const auto& plus : _pluses) mol.meta().addMetaObject(new ReactionPlusObject(plus)); @@ -1683,7 +1683,7 @@ void MoleculeCdxmlLoader::_parseLabel(BaseCDXElement& elem, std::string& label) } } -void MoleculeCdxmlLoader::_parseText(BaseCDXElement& elem, std::vector>& text_parsed) +void MoleculeCdxmlLoader::_parseText(BaseCDXElement& elem, std::vector& text_parsed) { Vec3f text_pos; auto text_coordinates_lambda = [&text_pos, this](const std::string& data) { this->parsePos(data, text_pos); }; @@ -1816,8 +1816,7 @@ void MoleculeCdxmlLoader::_parseText(BaseCDXElement& elem, std::vector, buf); @@ -1609,8 +1621,8 @@ void MoleculeJsonSaver::saveMolecule(BaseMolecule& bmol, JsonWriter& writer) } // location writer.Key("position"); - writer.StartObject(); const auto& pos = mol->getAtomXyz(i); + writer.StartObject(); writer.Key("x"); writeFloat(writer, pos.x); writer.Key("y"); @@ -1964,14 +1976,19 @@ void MoleculeJsonSaver::saveMetaData(JsonWriter& writer, MetaDataStorage& meta) writer.Key("content"); writer.String(simple_obj->_content.c_str()); writer.Key("position"); - writer.StartObject(); - writer.Key("x"); - writeFloat(writer, simple_obj->_pos.x); - writer.Key("y"); - writeFloat(writer, simple_obj->_pos.y); - writer.Key("z"); - writeFloat(writer, simple_obj->_pos.z); - writer.EndObject(); // end position + writePos(writer, simple_obj->_pos); + + writer.Key("pos"); + writer.StartArray(); + Vec2f pos_bbox(simple_obj->_pos.x, simple_obj->_pos.y); + writePos(writer, pos_bbox); + pos_bbox.y -= simple_obj->_size.y; + writePos(writer, pos_bbox); + pos_bbox.x += simple_obj->_size.x; + writePos(writer, pos_bbox); + pos_bbox.y += simple_obj->_size.y; + writePos(writer, pos_bbox); + writer.EndArray(); writer.EndObject(); // end data writer.EndObject(); // end node break; diff --git a/core/indigo-core/reaction/pathway_reaction.h b/core/indigo-core/reaction/pathway_reaction.h index fc8685b929..58beb2b4c1 100644 --- a/core/indigo-core/reaction/pathway_reaction.h +++ b/core/indigo-core/reaction/pathway_reaction.h @@ -106,6 +106,7 @@ namespace indigo reaction->addProductCopy(*_molecules[pidx], 0, 0); for (auto ridx : sr.reactantIndexes) reaction->addReactantCopy(*_molecules[ridx], 0, 0); + reaction->properties().copy(sr.properties); return reaction; } @@ -131,7 +132,7 @@ namespace indigo return static_cast(_molecules.size()); } - const auto& getReaction(int reaction_idx) + auto& getReaction(int reaction_idx) { return _reactions[reaction_idx]; } diff --git a/core/indigo-core/reaction/reaction_multistep_detector.h b/core/indigo-core/reaction/reaction_multistep_detector.h index d86afc29c1..4a47c2e917 100644 --- a/core/indigo-core/reaction/reaction_multistep_detector.h +++ b/core/indigo-core/reaction/reaction_multistep_detector.h @@ -56,8 +56,8 @@ namespace indigo void constructPathwayReaction(PathwayReaction& rxn); void detectPathwayMetadata(PathwayReaction& rxn); - void collectMetadata(PathwayReaction& rxn, const Rect2f& bbox); - void collectProperties(PathwayReaction& rxn, const SimpleTextObject& text_obj); + void collectMetadata(int reaction_idx, PathwayReaction& rxn, const Rect2f& bbox); + void collectProperties(PathwayReaction::SimpleReaction& sr, const SimpleTextObject& text_obj); typedef std::pair FLOAT_INT_PAIR; typedef std::vector FLOAT_INT_PAIRS; diff --git a/core/indigo-core/reaction/src/reaction_multistep_detector.cpp b/core/indigo-core/reaction/src/reaction_multistep_detector.cpp index cdd1c3d17d..0b7e5e7257 100644 --- a/core/indigo-core/reaction/src/reaction_multistep_detector.cpp +++ b/core/indigo-core/reaction/src/reaction_multistep_detector.cpp @@ -17,6 +17,7 @@ ***************************************************************************/ #include +#include "layout/pathway_layout.h" #include "reaction/pathway_reaction.h" #include "reaction/reaction.h" #include "reaction/reaction_multistep_detector.h" @@ -651,48 +652,78 @@ void ReactionMultistepDetector::constructPathwayReaction(PathwayReaction& rxn) void ReactionMultistepDetector::detectPathwayMetadata(PathwayReaction& rxn) { + int ridx = 0; for (int i = 0; i < rxn.meta().getMetaCount(ReactionArrowObject::CID); ++i) { auto& arrow = static_cast(rxn.meta().getMetaObject(ReactionArrowObject::CID, i)); Vec2f box_rt = arrow.getHead(); box_rt.y += (arrow.getHead() - arrow.getTail()).length() / 2; Rect2f lookup_box(arrow.getTail(), box_rt); + auto& sr = rxn.getReaction(ridx); + collectMetadata(ridx, rxn, lookup_box); + ridx++; } for (int i = 0; i < rxn.meta().getMetaCount(ReactionMultitailArrowObject::CID); ++i) { auto& multi_arrow = static_cast(rxn.meta().getMetaObject(ReactionMultitailArrowObject::CID, i)); Rect2f lookup_box(multi_arrow.getHead(), multi_arrow.getSpineBegin()); + collectMetadata(ridx, rxn, lookup_box); + ridx++; } } -void ReactionMultistepDetector::collectMetadata(PathwayReaction& rxn, const Rect2f& bbox) +void ReactionMultistepDetector::collectMetadata(int reaction_idx, PathwayReaction& rxn, const Rect2f& bbox) { + auto& sr = rxn.getReaction(reaction_idx); + float min_dist; + int text_idx = -1; for (int i = 0; i < rxn.meta().getMetaCount(SimpleTextObject::CID); ++i) { auto& text = static_cast(rxn.meta().getMetaObject(SimpleTextObject::CID, i)); Rect2f text_bbox; text.getBoundingBox(text_bbox); if (bbox.intersects(text_bbox)) - collectProperties(rxn, text); + { + auto dist = Vec2f::dist(bbox.leftBottom(), text_bbox.leftBottom()); + if (text_idx < 0 || dist < min_dist) + { + text_idx = i; + min_dist = dist; + } + } } + if (text_idx >= 0) + collectProperties(sr, static_cast(rxn.meta().getMetaObject(SimpleTextObject::CID, text_idx))); } -void ReactionMultistepDetector::collectProperties(PathwayReaction& rxn, const SimpleTextObject& text_obj) +void ReactionMultistepDetector::collectProperties(PathwayReaction::SimpleReaction& sr, const SimpleTextObject& text_obj) { + std::string name, condition; + bool is_condition = false; for (const auto& line : text_obj.getLines()) { - for (const auto& [key, value] : line.styles) + if (line.text.size()) { - if (value.count(std::make_pair(SimpleTextObject::EBold, true))) - { - rxn.properties().insert(""); - } - - if (value.count(std::make_pair(SimpleTextObject::EItalic, true))) - { - } + if (is_condition) + condition += line.text; + else + name += line.text; } + else + is_condition = true; + } + + if (name.size()) + { + int id = sr.properties.insert(PathwayLayout::REACTION_NAME); + sr.properties.value(id).readString(name.c_str(), true); + } + + if (condition.size()) + { + int id = sr.properties.insert(PathwayLayout::REACTION_CONDITIONS); + sr.properties.value(id).readString(condition.c_str(), true); } } diff --git a/utils/indigo-depict/main.c b/utils/indigo-depict/main.c index d3586919bd..970a0723cf 100644 --- a/utils/indigo-depict/main.c +++ b/utils/indigo-depict/main.c @@ -1058,7 +1058,7 @@ int main(int argc, char* argv[]) _prepare(obj, p.aromatization); if (p.action == ACTION_LAYOUT) { - //indigoLayout(obj); + // indigoLayout(obj); if (p.out_ext == OEXT_CML) indigoSaveCmlToFile(obj, p.outfile); else if (p.out_ext == OEXT_RXN)