Skip to content

Commit

Permalink
#2176: add ACS style reaction layout (#2307)
Browse files Browse the repository at this point in the history
Co-authored-by: Aliaksandr Dziarkach <[email protected]>
Co-authored-by: Aliakasndr Dziarkach <[email protected]>
  • Loading branch information
3 people authored Sep 18, 2024
1 parent a42e973 commit b97fa59
Show file tree
Hide file tree
Showing 45 changed files with 3,587 additions and 439 deletions.
18 changes: 8 additions & 10 deletions api/c/indigo-renderer/src/indigo_render2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,6 @@ IndigoRenderer::~IndigoRenderer()
{
}

#define SET_POSITIVE_FLOAT_OPTION(option, error) \
[](float value) { \
if (value <= 0.0f) \
throw IndigoError(error); \
option = value; \
}, \
[](float& value) { value = option; }

#define CHECK_AND_SETTER_GETTER_COLOR_OPTION(option) \
[](float r, float g, float b) { \
CHECKRGB(r, g, b); \
Expand Down Expand Up @@ -689,9 +681,17 @@ void IndigoRenderer::setOptionsHandlers()
if (!options_set)
{
auto mgr = sf::xlock_safe_ptr(indigoGetOptionManager(indigo_id));
options_set = true;

#define rp indigoRendererGetInstance().renderParams
#define cdxmlContext getCdxmlContext()
#define indigo indigoGetInstance()

rp.cnvOpt.bondLength = indigo.layout_options.bondLength;
rp.cnvOpt.bondLengthUnit = indigo.layout_options.bondLengthUnit;
rp.rOpt.reactionComponentMarginSize = indigo.layout_options.reactionComponentMarginSize;
rp.rOpt.reactionComponentMarginSizeUnit = indigo.layout_options.reactionComponentMarginSizeUnit;
rp.rOpt.ppi = indigo.layout_options.ppi;

mgr->setOptionHandlerInt("render-comment-offset", SETTER_GETTER_INT_OPTION(rp.cnvOpt.commentOffset));
mgr->setOptionHandlerInt("render-image-width", SETTER_GETTER_INT_OPTION(rp.cnvOpt.width));
Expand Down Expand Up @@ -757,7 +757,5 @@ void IndigoRenderer::setOptionsHandlers()
mgr->setOptionHandlerString("render-cdxml-title-face", SETTER_GETTER_STR_OPTION(cdxmlContext.titleFace));

mgr->setOptionHandlerVoid("reset-render-options", indigoRenderResetOptions);

options_set = true;
}
}
2 changes: 1 addition & 1 deletion api/c/indigo-renderer/src/indigo_renderer_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class IndigoRenderer : public IndigoPluginContext
private:
void setOptionsHandlers();

bool options_set = false;
std::atomic<bool> options_set = false;
};

class IndigoHDCOutput : public IndigoObject
Expand Down
3 changes: 2 additions & 1 deletion api/c/indigo/src/indigo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,11 @@ void Indigo::initMoleculeJsonSaver(MoleculeJsonSaver& saver)
saver.use_native_precision = json_use_native_precision;
}

void Indigo::initReactionJsonSaver(ReactionJsonSaver& saver)
void Indigo::initReactionJsonSaver(ReactionJsonSaver& saver) const
{
saver.add_stereo_desc = json_saving_add_stereo_desc;
saver.pretty_json = json_saving_pretty;
saver.layout_options = layout_options;
saver.use_native_precision = json_use_native_precision;
}

Expand Down
6 changes: 4 additions & 2 deletions api/c/indigo/src/indigo_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ class DLLEXPORT Indigo
bool embedding_edges_uniqueness, find_unique_embeddings;
int max_embeddings;

int layout_max_iterations; // default is zero -- no limit
int layout_max_iterations = 0; // default is zero -- no limit
bool smart_layout = false;
float layout_horintervalfactor = ReactionLayout::DEFAULT_HOR_INTERVAL_FACTOR;
bool layout_preserve_existing = false;
Expand All @@ -351,7 +351,7 @@ class DLLEXPORT Indigo
void initMolfileSaver(MolfileSaver& saver);
void initRxnfileSaver(RxnfileSaver& saver);
void initMoleculeJsonSaver(MoleculeJsonSaver& saver);
void initReactionJsonSaver(ReactionJsonSaver& saver);
void initReactionJsonSaver(ReactionJsonSaver& saver) const;
void initReactionJsonSaver(PathwayReactionJsonSaver& saver);

bool preserve_ordering_in_serialize;
Expand All @@ -366,6 +366,8 @@ class DLLEXPORT Indigo

bool scsr_ignore_chem_templates;

indigo::LayoutOptions layout_options;

static const Array<char>& getErrorMessage();
static void clearErrorMessage();
static void setErrorMessage(const char* message);
Expand Down
9 changes: 5 additions & 4 deletions api/c/indigo/src/indigo_layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ CEXPORT int indigoLayout(int object)
}

ml.max_iterations = self.layout_max_iterations;
ml.bond_length = MoleculeLayout::DEFAULT_BOND_LENGTH;
ml.bond_length = LayoutOptions::DEFAULT_BOND_LENGTH;
ml.layout_orientation = (layout_orientation_value)self.layout_orientation;
if (self.layout_preserve_existing || mol->hasAtropoStereoBonds())
ml.respect_existing_layout = true;
Expand Down Expand Up @@ -105,11 +105,12 @@ CEXPORT int indigoLayout(int object)
obj.type == IndigoObject::PATHWAY_REACTION || rxn.multitaleCount();
if (!no_layout)
{
ReactionLayout rl(rxn, self.smart_layout);
ReactionLayout rl(rxn, self.smart_layout, self.layout_options);
rl.max_iterations = self.layout_max_iterations;
rl.layout_orientation = (layout_orientation_value)self.layout_orientation;
rl.bond_length = MoleculeLayout::DEFAULT_BOND_LENGTH;
rl.horizontal_interval_factor = self.layout_horintervalfactor;
// TODO::ACS Why removed?
// rl.bond_length = LayoutOptions::DEFAULT_BOND_LENGTH;
// rl.reaction_margin_size = self.layout_horintervalfactor;
if (self.layout_preserve_existing)
rl.preserve_molecule_layout = true;
rl.make();
Expand Down
64 changes: 64 additions & 0 deletions api/c/indigo/src/indigo_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,62 @@ void indigoProductEnumeratorGetOneTubeMode(Array<char>& value)
value.readString("grid", true);
}

bool isEqual(const char* l, const char* r)
{
return strcmp(l, r) != 0;
}

IndigoOptionManager::optf_string_t indigoSetUnitsOfMeasure(UnitsOfMeasure::TYPE& result)
{
static auto func = [&result](const char* mode) {
if (isEqual(mode, "pt"))
{
result = UnitsOfMeasure::TYPE::PT;
}
else if (isEqual(mode, "px"))
{
result = UnitsOfMeasure::TYPE::PX;
}
else if (isEqual(mode, "inch"))
{
result = UnitsOfMeasure::TYPE::INCH;
}
else if (isEqual(mode, "cm"))
{
result = UnitsOfMeasure::TYPE::CM;
}
else
{
throw IndigoError("Invalid size unit, should be 'px', 'pt', 'inch' or 'all'");
}
};

return [](const char* mode) -> void { return func(mode); };
}

IndigoOptionManager::get_optf_string_t indigoGetUnitsOfMeasure(const UnitsOfMeasure::TYPE input)
{
static auto func = [input](Array<char>& result) {
switch (input)
{
case UnitsOfMeasure::TYPE::PT:
result.readString("pt", true);
break;
case UnitsOfMeasure::TYPE::PX:
result.readString("px", true);
break;
case UnitsOfMeasure::TYPE::INCH:
result.readString("inch", true);
break;
case UnitsOfMeasure::TYPE::CM:
result.readString("cm", true);
break;
}
};

return [](Array<char>& res) -> void { return func(res); };
}

void IndigoOptionHandlerSetter::setBasicOptionHandlers(const qword id)
{
auto mgr = sf::xlock_safe_ptr(indigoGetOptionManager(id));
Expand Down Expand Up @@ -384,4 +440,12 @@ void IndigoOptionHandlerSetter::setBasicOptionHandlers(const qword id)
mgr->setOptionHandlerInt("rpe-max-products-count", SETTER_GETTER_INT_OPTION(indigo.rpe_params.max_product_count));
mgr->setOptionHandlerBool("rpe-layout", SETTER_GETTER_BOOL_OPTION(indigo.rpe_params.is_layout));
mgr->setOptionHandlerBool("transform-layout", SETTER_GETTER_BOOL_OPTION(indigo.rpe_params.transform_is_layout));

mgr->setOptionHandlerFloat("bond-length", SET_POSITIVE_FLOAT_OPTION(indigo.layout_options.bondLength, "bond length must be positive"));
mgr->setOptionHandlerString("bond-length-unit", indigoSetUnitsOfMeasure(indigo.layout_options.bondLengthUnit),
indigoGetUnitsOfMeasure(indigo.layout_options.bondLengthUnit));
mgr->setOptionHandlerFloat("reaction-component-margin-size", SETTER_GETTER_FLOAT_OPTION(indigo.layout_options.reactionComponentMarginSize));
mgr->setOptionHandlerString("reaction-component-margin-size-unit", indigoSetUnitsOfMeasure(indigo.layout_options.reactionComponentMarginSizeUnit),
indigoGetUnitsOfMeasure(indigo.layout_options.reactionComponentMarginSizeUnit));
mgr->setOptionHandlerInt("image-resolution", SET_POSITIVE_INT_OPTION(indigo.layout_options.ppi, "image resolution ppi must be positive"));
}
17 changes: 17 additions & 0 deletions api/c/indigo/src/option_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "base_cpp/os_sync_wrapper.h"
#include "base_cpp/red_black.h"

#include <cstdint>
#include <sstream>

using namespace indigo;
Expand Down Expand Up @@ -89,6 +90,22 @@ using namespace indigo;
value.push(0); \
}

#define SET_POSITIVE_FLOAT_OPTION(option, error) \
[](float value) { \
if (value <= 0.0f) \
throw IndigoError(error); \
option = value; \
}, \
[](float& value) { value = option; }

#define SET_POSITIVE_INT_OPTION(option, error) \
[](int32_t value) { \
if (value <= 0) \
throw IndigoError(error); \
option = value; \
}, \
[](int32_t& value) { value = option; }

class DLLEXPORT IndigoOptionManager
{
public:
Expand Down
103 changes: 103 additions & 0 deletions api/c/tests/unit/tests/layout.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/****************************************************************************
* 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.
***************************************************************************/

#include <thread>

#include <gtest/gtest.h>

#include <base_cpp/exception.h>

#include <algorithm>
#include <fstream>
#include <indigo-renderer.h>
#include <indigo.h>

#include "common.h"

using namespace indigo;

class IndigoApiLayoutTest : public IndigoApiTest
{
protected:
void SetUp() final
{
IndigoApiTest::SetUp();
indigoRendererInit(session);
}

void TearDown() final
{
indigoRendererDispose(session);
IndigoApiTest::TearDown();
}

struct TestCaseResult
{
std::string_view result;
std::string_view expected;
};

TestCaseResult applyLayoutAndGetResult(int reactionId, std::string_view expectedResultFilename)
{
indigoSetOptionBool("json-saving-pretty", true);
indigoSetOptionBool("json-use-native-precision", true);
indigoSetOptionBool("json-saving-add-stereo-desc", true);

indigoLayout(reactionId);
std::string path_to_file = "molecules/basic/" + std::string(expectedResultFilename);
// indigoSaveJsonToFile(reactionId, dataPath(path_to_file.data()).c_str());
std::ifstream is(dataPath(path_to_file.data()), std::ios::binary | std::ios::ate);
auto size = is.tellg();
stringBuffer = std::string(size, '\0'); // construct string to stream size
is.seekg(0);
is.read(&stringBuffer[0], size);
stringBuffer.erase(std::remove(stringBuffer.begin(), stringBuffer.end(), '\r'), stringBuffer.end());

const char* res = indigoJson(reactionId);
return {res, stringBuffer};
}
std::string stringBuffer;
};

// TEST_F(IndigoApiLayoutTest, check_reaction_margin_size)
// {
// indigoSetErrorHandler(errorHandler, nullptr);
// indigoSetOptionBool("json-saving-pretty", true);
// indigoSetOptionBool("json-use-native-precision", true);
// indigoSetOptionBool("json-saving-add-stereo-desc", true);
// try
// {
// auto reactionId = indigoLoadReactionFromFile(dataPath("molecules/basic/before_layout.ket").c_str());
// {
// indigoSetOption("reaction-component-margin-size", "0.0");
// auto files = applyLayoutAndGetResult(reactionId, "after_layout_zero_margin.ket");
// EXPECT_STREQ(files.result.data(), files.expected.data());
// }
// {
// indigoSetOption("bond-length", "40.0");
// indigoSetOption("reaction-component-margin-size", "20.0");
// auto files = applyLayoutAndGetResult(reactionId, "after_layout_default_margin.ket");
// EXPECT_STREQ(files.result.data(), files.expected.data());
// }
// indigoFree(reactionId);
// }
// catch (Exception& e)
// {
// ASSERT_STREQ("", e.message());
// }
// }
45 changes: 43 additions & 2 deletions api/tests/integration/common/env_indigo.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,31 @@ def dist_vec(a, b):
return sqrt(sum((a - b) ** 2 for a, b in zip(a, b)))


def moleculeLayoutDiff(indigo, mol, ref, delta=0.01, ref_is_file=True):
def moleculeLayoutDiff(
indigo,
mol,
ref,
delta=0.01,
ref_is_file=True,
update=False,
update_format="mol",
):
if ref_is_file:
ref_name = getRefFilepath2(ref)
if update:
if update_format == "mol":
txt = mol.molfile()
elif update_format == "ket":
txt = mol.json()
with open(ref_name, "w") as file:
file.write(txt)
m2 = indigo.loadMoleculeFromFile(ref_name)
else:
if update:
if update_format == "mol":
print(mol.molfile())
elif update_format == "ket":
print(mol.json())
m2 = indigo.loadMolecule(ref)

error_buf = []
Expand Down Expand Up @@ -242,11 +262,32 @@ def moleculeLayoutDiff(indigo, mol, ref, delta=0.01, ref_is_file=True):
return "Success"


def reactionLayoutDiff(indigo, rxn, ref, delta=0.001, ref_is_file=True):
def reactionLayoutDiff(
indigo,
rxn,
ref,
delta=0.001,
ref_is_file=True,
update=False,
update_format="mol",
):
if ref_is_file:
ref_name = getRefFilepath2(ref)
if update:
if update_format == "mol":
txt = rxn.rxnfile()
elif update_format == "ket":
txt = rxn.json()
with open(ref_name, "w") as file:
file.write(txt)
r2 = indigo.loadReactionFromFile(ref_name)
else:
if update:
if update_format == "mol":
print(rxn.rxnfile())
elif update_format == "ket":
print(rxn.json())
print("ref=\n", ref)
r2 = indigo.loadReaction(ref)
error_buf = []
for m in rxn.iterateMolecules():
Expand Down
Loading

0 comments on commit b97fa59

Please sign in to comment.