From 45da8109096c392598bee2b2f92a06132ac15a39 Mon Sep 17 00:00:00 2001 From: Aliaksandr Dziarkach <18146690+AliaksandrDziarkach@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:52:40 +0300 Subject: [PATCH] #2175 - Adjust the rendering of PNG and SVG formats for ACS style (#2434) Co-authored-by: Aliakasndr Dziarkach --- api/c/indigo-renderer/src/indigo_render2d.cpp | 57 +++++++++++-- api/c/indigo/src/indigo_internal.h | 1 - api/c/indigo/src/indigo_layout.cpp | 14 ++-- api/c/indigo/src/indigo_options.cpp | 67 ++-------------- api/c/indigo/src/option_manager.h | 31 ++++++++ .../integration/ref/basic/options.py.out | 2 +- .../ref/layout/acs_style_reaction.py.out | 2 +- .../ref/rendering/acs_style.py.out | 4 + .../tests/layout/acs_style_reaction.py | 4 +- .../tests/layout/ref/932-agents.ket | 34 ++++---- .../integration/tests/rendering/acs_style.py | 75 ++++++++++++++++++ .../rendering/ref/linux/acs_style_changed.png | Bin 0 -> 10055 bytes .../rendering/ref/linux/acs_style_default.png | Bin 0 -> 13639 bytes .../rendering/ref/mac/acs_style_changed.png | Bin 0 -> 10055 bytes .../rendering/ref/mac/acs_style_default.png | Bin 0 -> 13639 bytes .../rendering/ref/win/acs_style_changed.png | Bin 0 -> 10055 bytes .../rendering/ref/win/acs_style_default.png | Bin 0 -> 13639 bytes core/indigo-core/common/math/algebra.h | 23 ++++++ core/indigo-core/layout/metalayout.h | 59 ++++++++++++-- core/indigo-core/layout/reaction_layout.h | 2 +- core/indigo-core/layout/src/metalayout.cpp | 8 ++ core/render2d/render.h | 11 ++- core/render2d/render_common.h | 39 +++++++-- core/render2d/render_context.h | 3 +- core/render2d/render_grid.h | 2 +- core/render2d/render_single.h | 2 +- core/render2d/src/render.cpp | 7 +- core/render2d/src/render_cdxml.cpp | 3 +- core/render2d/src/render_common.cpp | 59 +++++++++++--- core/render2d/src/render_context.cpp | 54 ++++++++++++- core/render2d/src/render_grid.cpp | 4 +- core/render2d/src/render_internal.cpp | 75 ++++++++++++++---- core/render2d/src/render_params.cpp | 12 +-- core/render2d/src/render_single.cpp | 4 +- 34 files changed, 499 insertions(+), 159 deletions(-) create mode 100644 api/tests/integration/ref/rendering/acs_style.py.out create mode 100644 api/tests/integration/tests/rendering/acs_style.py create mode 100644 api/tests/integration/tests/rendering/ref/linux/acs_style_changed.png create mode 100644 api/tests/integration/tests/rendering/ref/linux/acs_style_default.png create mode 100644 api/tests/integration/tests/rendering/ref/mac/acs_style_changed.png create mode 100644 api/tests/integration/tests/rendering/ref/mac/acs_style_default.png create mode 100644 api/tests/integration/tests/rendering/ref/win/acs_style_changed.png create mode 100644 api/tests/integration/tests/rendering/ref/win/acs_style_default.png diff --git a/api/c/indigo-renderer/src/indigo_render2d.cpp b/api/c/indigo-renderer/src/indigo_render2d.cpp index f40519a07e..1a08d0d727 100644 --- a/api/c/indigo-renderer/src/indigo_render2d.cpp +++ b/api/c/indigo-renderer/src/indigo_render2d.cpp @@ -376,6 +376,18 @@ void indigoRenderGetCommentPosition(Array& value) value.readString("bottom", true); } +static void indigoSetBondLength(float value) +{ + Indigo& self = indigoGetInstance(); + self.layout_options.setBondLengthPx(value); +} + +static void indigoGetBondLength(float& value) +{ + Indigo& self = indigoGetInstance(); + value = self.layout_options.getBondLengthPx(); +} + RenderCdxmlContext& getCdxmlContext() { RenderParams& rp = indigoRendererGetInstance().renderParams; @@ -437,6 +449,24 @@ CEXPORT int indigoRendererDispose(const qword id) INDIGO_END(-1); } +static void setParams(RenderParams& rp, LayoutOptions& layout_options) +{ + rp.cnvOpt.bondLength = layout_options.bondLength; + rp.cnvOpt.bondLengthUnit = layout_options.bondLengthUnit; + rp.rOpt.ppi = layout_options.ppi; + rp.rOpt.bond_length_px = layout_options.bondLength > EPSILON ? layout_options.getBondLengthPx() : LayoutOptions::DEFAULT_BOND_LENGTH_PX; + if (rp.cnvOpt.outputSheetWidth > 0) + { + rp.cnvOpt.maxHeight = -1; + rp.cnvOpt.maxWidth = UnitsOfMeasure::convertInchesToPx(rp.cnvOpt.outputSheetWidth, layout_options.ppi); + } + else if (rp.cnvOpt.outputSheetHeight > 0) + { + rp.cnvOpt.maxHeight = UnitsOfMeasure::convertInchesToPx(rp.cnvOpt.outputSheetHeight, layout_options.ppi); + rp.cnvOpt.maxWidth = -1; + } +} + CEXPORT int indigoRender(int object, int output) { INDIGO_BEGIN @@ -447,6 +477,8 @@ CEXPORT int indigoRender(int object, int output) rp.clearArrays(); rp.smart_layout = self.smart_layout; + setParams(rp, indigoGetInstance().layout_options); + IndigoObject& obj = self.getObject(object); if (IndigoBaseMolecule::is(obj)) @@ -502,6 +534,8 @@ CEXPORT int indigoRenderGrid(int objects, int* refAtoms, int nColumns, int outpu RenderParams& rp = indigoRendererGetInstance().renderParams; rp.clearArrays(); + setParams(rp, indigoGetInstance().layout_options); + PtrArray& objs = IndigoArray::cast(self.getObject(objects)).objects; if (rp.rOpt.cdxml_context.get() != NULL) { @@ -690,12 +724,6 @@ void IndigoRenderer::setOptionsHandlers() #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)); mgr->setOptionHandlerInt("render-image-height", SETTER_GETTER_INT_OPTION(rp.cnvOpt.height)); @@ -723,7 +751,7 @@ void IndigoRenderer::setOptionsHandlers() mgr->setOptionHandlerBool("render-highlighted-labels-visible", SETTER_GETTER_BOOL_OPTION(rp.rOpt.highlightedLabelsVisible)); mgr->setOptionHandlerBool("render-bold-bond-detection", SETTER_GETTER_BOOL_OPTION(rp.rOpt.boldBondDetection)); - mgr->setOptionHandlerFloat("render-bond-length", SETTER_GETTER_FLOAT_OPTION(rp.cnvOpt.bondLength)); + mgr->setOptionHandlerFloat("render-bond-length", indigoSetBondLength, indigoGetBondLength); mgr->setOptionHandlerFloat("render-relative-thickness", SET_POSITIVE_FLOAT_OPTION(rp.relativeThickness, "relative thickness must be positive")); mgr->setOptionHandlerFloat("render-bond-line-width", SET_POSITIVE_FLOAT_OPTION(rp.bondLineWidthFactor, "bond line width factor must be positive")); mgr->setOptionHandlerFloat("render-comment-font-size", SETTER_GETTER_FLOAT_OPTION(rp.rOpt.commentFontFactor)); @@ -760,5 +788,20 @@ void IndigoRenderer::setOptionsHandlers() mgr->setOptionHandlerString("render-cdxml-title-face", SETTER_GETTER_STR_OPTION(cdxmlContext.titleFace)); mgr->setOptionHandlerVoid("reset-render-options", indigoRenderResetOptions); + + // ACS style options + mgr->setOptionHandlerFloat("render-font-size", SETTER_GETTER_FLOAT_OPTION(rp.rOpt.fontSize)); + mgr->setOptionHandlerString("render-font-size-unit", SETTER_GETTER_UNIT_OPTION(rp.rOpt.fontSizeUnit)); + mgr->setOptionHandlerFloat("render-font-size-sub", SETTER_GETTER_FLOAT_OPTION(rp.rOpt.fontSizeSub)); + mgr->setOptionHandlerString("render-font-size-sub-unit", SETTER_GETTER_UNIT_OPTION(rp.rOpt.fontSizeSubUnit)); + mgr->setOptionHandlerFloat("render-bond-thickness", SETTER_GETTER_FLOAT_OPTION(rp.rOpt.bondThickness)); + mgr->setOptionHandlerString("render-bond-thickness-unit", SETTER_GETTER_UNIT_OPTION(rp.rOpt.bondThicknessUnit)); + mgr->setOptionHandlerFloat("render-bond-spacing", SETTER_GETTER_FLOAT_OPTION(rp.rOpt.bondSpacing)); + mgr->setOptionHandlerFloat("render-stereo-bond-width", SETTER_GETTER_FLOAT_OPTION(rp.rOpt.stereoBondWidth)); + mgr->setOptionHandlerString("render-stereo-bond-width-unit", SETTER_GETTER_UNIT_OPTION(rp.rOpt.stereoBondWidthUnit)); + mgr->setOptionHandlerFloat("render-hash-spacing", SETTER_GETTER_FLOAT_OPTION(rp.rOpt.hashSpacing)); + mgr->setOptionHandlerString("render-hash-spacing-unit", SETTER_GETTER_UNIT_OPTION(rp.rOpt.hashSpacingUnit)); + mgr->setOptionHandlerFloat("render-output-sheet-width", SETTER_GETTER_FLOAT_OPTION(rp.cnvOpt.outputSheetWidth)); + mgr->setOptionHandlerFloat("render-output-sheet-height", SETTER_GETTER_FLOAT_OPTION(rp.cnvOpt.outputSheetHeight)); } } diff --git a/api/c/indigo/src/indigo_internal.h b/api/c/indigo/src/indigo_internal.h index bab466f132..3cb1550bb8 100644 --- a/api/c/indigo/src/indigo_internal.h +++ b/api/c/indigo/src/indigo_internal.h @@ -339,7 +339,6 @@ class DLLEXPORT Indigo 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; int layout_orientation = 0; diff --git a/api/c/indigo/src/indigo_layout.cpp b/api/c/indigo/src/indigo_layout.cpp index ed27684f50..fc35c2c9ba 100644 --- a/api/c/indigo/src/indigo_layout.cpp +++ b/api/c/indigo/src/indigo_layout.cpp @@ -28,12 +28,15 @@ #include "layout/reaction_layout.h" #include "reaction/base_reaction.h" +#ifdef _WIN32 +#pragma warning(push, 4) +#endif + CEXPORT int indigoLayout(int object) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(object); - int i; if (IndigoBaseMolecule::is(obj)) { @@ -79,7 +82,7 @@ CEXPORT int indigoLayout(int object) catch (Exception e) { } - for (i = 1; i <= mol->rgroups.getRGroupCount(); i++) + for (int i = 1; i <= mol->rgroups.getRGroupCount(); i++) { RGroup& rgp = mol->rgroups.getRGroup(i); @@ -112,9 +115,6 @@ CEXPORT int indigoLayout(int object) ReactionLayout rl(rxn, self.smart_layout, self.layout_options); rl.setMaxIterations(self.layout_max_iterations); rl.setLayoutOrientation((LAYOUT_ORIENTATION)self.layout_orientation); - // TODO::ACS Why removed? - // rl.bond_length = LayoutOptions::DEFAULT_BOND_LENGTH; - // rl.reaction_margin_size = self.layout_horintervalfactor; if (self.layout_preserve_existing) rl.setPreserveMoleculeLayout(true); rl.make(); @@ -197,3 +197,7 @@ CEXPORT int indigoClean2d(int object) } INDIGO_END(-1); } + +#ifdef _WIN32 +#pragma warning(pop) +#endif diff --git a/api/c/indigo/src/indigo_options.cpp b/api/c/indigo/src/indigo_options.cpp index 41e610c7d7..216b06243a 100644 --- a/api/c/indigo/src/indigo_options.cpp +++ b/api/c/indigo/src/indigo_options.cpp @@ -138,13 +138,13 @@ static void indigoGetEmbeddingUniqueness(Array& value) static void indigoSetLayoutHorIntervalFactor(float value) { Indigo& self = indigoGetInstance(); - self.layout_horintervalfactor = value; + self.layout_options.setMarginSizeInAngstroms(value); } static void indigoGetLayoutHorIntervalFactor(float& value) { Indigo& self = indigoGetInstance(); - value = self.layout_horintervalfactor; + value = self.layout_options.getMarginSizeInAngstroms(); } static void indigoSetAromaticityModel(const char* model) @@ -238,6 +238,7 @@ static void indigoResetBasicOptions() Indigo& self = indigoGetInstance(); self.standardize_options.reset(); self.ionize_options = IonizeOptions(); + self.layout_options.reset(); self.init(); } @@ -261,62 +262,6 @@ void indigoProductEnumeratorGetOneTubeMode(Array& 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& 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& res) -> void { return func(res); }; -} - void IndigoOptionHandlerSetter::setBasicOptionHandlers(const qword id) { auto mgr = sf::xlock_safe_ptr(indigoGetOptionManager(id)); @@ -442,10 +387,8 @@ void IndigoOptionHandlerSetter::setBasicOptionHandlers(const qword id) 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->setOptionHandlerString("bond-length-unit", SETTER_GETTER_UNIT_OPTION(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->setOptionHandlerString("reaction-component-margin-size-unit", SETTER_GETTER_UNIT_OPTION(indigo.layout_options.reactionComponentMarginSizeUnit)); mgr->setOptionHandlerInt("image-resolution", SET_POSITIVE_INT_OPTION(indigo.layout_options.ppi, "image resolution ppi must be positive")); } \ No newline at end of file diff --git a/api/c/indigo/src/option_manager.h b/api/c/indigo/src/option_manager.h index adac3d48bc..ce896d1857 100644 --- a/api/c/indigo/src/option_manager.h +++ b/api/c/indigo/src/option_manager.h @@ -106,6 +106,37 @@ using namespace indigo; }, \ [](int32_t& value) { value = option; } +#define SETTER_GETTER_UNIT_OPTION(option) \ + [](const char* mode) { \ + if (strcmp(mode, "pt") == 0) \ + option = UnitsOfMeasure::TYPE::PT; \ + else if (strcmp(mode, "px") == 0) \ + option = UnitsOfMeasure::TYPE::PX; \ + else if (strcmp(mode, "inch") == 0) \ + option = UnitsOfMeasure::TYPE::INCH; \ + else if (strcmp(mode, "cm") == 0) \ + option = UnitsOfMeasure::TYPE::CM; \ + else \ + throw IndigoError("Invalid size unit, should be 'px', 'pt', 'inch' or 'all'"); \ + }, \ + [](Array& result) { \ + switch (option) \ + { \ + 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; \ + } \ + } + class DLLEXPORT IndigoOptionManager { public: diff --git a/api/tests/integration/ref/basic/options.py.out b/api/tests/integration/ref/basic/options.py.out index b52b2c6d5a..4ae6b4d518 100644 --- a/api/tests/integration/ref/basic/options.py.out +++ b/api/tests/integration/ref/basic/options.py.out @@ -14,7 +14,7 @@ bool True true ***** Float ***** -1.400 +0.800 float 21.333 21.333 diff --git a/api/tests/integration/ref/layout/acs_style_reaction.py.out b/api/tests/integration/ref/layout/acs_style_reaction.py.out index e9a1468b1b..cecbd70937 100644 --- a/api/tests/integration/ref/layout/acs_style_reaction.py.out +++ b/api/tests/integration/ref/layout/acs_style_reaction.py.out @@ -15,4 +15,4 @@ Molecule #4: Success *** 2389 wrong margin *** -acs_issue_2389.ket.ket:SUCCEED +acs_issue_2389.ket:SUCCEED diff --git a/api/tests/integration/ref/rendering/acs_style.py.out b/api/tests/integration/ref/rendering/acs_style.py.out new file mode 100644 index 0000000000..dd300aca25 --- /dev/null +++ b/api/tests/integration/ref/rendering/acs_style.py.out @@ -0,0 +1,4 @@ +****** Default rendering settings ***** +acs_style_default.png rendering status: OK +****** Changed ACS settings ***** +acs_style_changed.png rendering status: OK diff --git a/api/tests/integration/tests/layout/acs_style_reaction.py b/api/tests/integration/tests/layout/acs_style_reaction.py index 8fd295f4b2..251027a434 100644 --- a/api/tests/integration/tests/layout/acs_style_reaction.py +++ b/api/tests/integration/tests/layout/acs_style_reaction.py @@ -71,7 +71,7 @@ def find_diff(a, b): ket = rxn.json() diff = find_diff(ket_ref, ket) if not diff: - print(filename + ".ket:SUCCEED") + print(filename + ":SUCCEED") else: - print(filename + ".ket:FAILED") + print(filename + ":FAILED") print(diff) diff --git a/api/tests/integration/tests/layout/ref/932-agents.ket b/api/tests/integration/tests/layout/ref/932-agents.ket index 7f3ee63b8c..3fa3fa661f 100644 --- a/api/tests/integration/tests/layout/ref/932-agents.ket +++ b/api/tests/integration/tests/layout/ref/932-agents.ket @@ -51,7 +51,7 @@ { "type": "plus", "location": [ - 8.400002, + 8.400001, 0.0, 0.0 ] @@ -78,7 +78,7 @@ "mode": "open-angle", "pos": [ { - "x": 13.600001, + "x": 13.6, "y": 0.0, "z": 0.0 }, @@ -150,7 +150,7 @@ { "label": "S", "location": [ - 6.400001, + 6.4, 0.0, 0.0 ] @@ -164,7 +164,7 @@ { "label": "C", "location": [ - 10.400002, + 10.400001, 0.8, 0.0 ] @@ -172,7 +172,7 @@ { "label": "C", "location": [ - 12.000001, + 12.0, 0.8, 0.0 ] @@ -180,7 +180,7 @@ { "label": "C", "location": [ - 12.000001, + 12.0, -0.8, 0.0 ] @@ -188,7 +188,7 @@ { "label": "C", "location": [ - 10.400002, + 10.400001, -0.8, 0.0 ] @@ -231,7 +231,7 @@ { "label": "C", "location": [ - 15.200002, + 15.200001, 2.69282, 0.0 ] @@ -247,7 +247,7 @@ { "label": "C", "location": [ - 17.971283, + 17.971281, 2.69282, 0.0 ] @@ -276,7 +276,7 @@ { "label": "C", "location": [ - 20.371281, + 20.371279, 2.29282, 0.0 ] @@ -290,7 +290,7 @@ { "label": "P", "location": [ - 22.771278, + 22.771276, 2.29282, 0.0 ] @@ -304,7 +304,7 @@ { "label": "C", "location": [ - 25.171276, + 25.171274, 2.69282, 0.0 ] @@ -312,7 +312,7 @@ { "label": "C", "location": [ - 26.556917, + 26.556915, 1.89282, 0.0 ] @@ -320,7 +320,7 @@ { "label": "C", "location": [ - 27.942556, + 27.942554, 2.69282, 0.0 ] @@ -349,7 +349,7 @@ { "label": "C", "location": [ - 30.342554, + 30.342552, 2.985641, 0.0 ] @@ -357,7 +357,7 @@ { "label": "C", "location": [ - 31.942554, + 31.942553, 2.985641, 0.0 ] @@ -365,7 +365,7 @@ { "label": "C", "location": [ - 31.142553, + 31.142551, 1.6, 0.0 ] diff --git a/api/tests/integration/tests/rendering/acs_style.py b/api/tests/integration/tests/rendering/acs_style.py new file mode 100644 index 0000000000..b01501a4e9 --- /dev/null +++ b/api/tests/integration/tests/rendering/acs_style.py @@ -0,0 +1,75 @@ +import errno +import os +import sys + +sys.path.append( + os.path.normpath( + os.path.join(os.path.abspath(__file__), "..", "..", "..", "common") + ) +) +from env_indigo import Indigo, IndigoRenderer, isIronPython, joinPathPy # noqa +from rendering import checkImageSimilarity + +if not os.path.exists(joinPathPy("out", __file__)): + try: + os.makedirs(joinPathPy("out", __file__)) + except OSError as e: + if e.errno != errno.EEXIST: + raise + +mol_data = """ + Ketcher 9262423222D 1 1.00000 0.00000 0 + + 12 11 0 0 0 0 0 0 0 0999 V2000 + 21.7000 -12.0410 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 22.2000 -11.1750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 23.2000 -11.1750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 23.7000 -10.3090 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 23.7000 -12.0410 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 21.7000 -10.3090 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.7000 -10.3090 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 24.7000 -10.3090 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.7000 -12.0410 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 24.7000 -12.0410 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.2000 -9.4429 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 25.2000 -9.4429 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 + 2 3 2 0 0 0 + 3 4 1 0 0 0 + 3 5 1 0 0 0 + 2 6 1 0 0 0 + 6 7 1 1 0 0 + 4 8 1 6 0 0 + 1 9 1 0 0 0 + 5 10 1 0 0 0 + 7 11 1 0 0 0 + 8 12 1 0 0 0 +M END + +""" + +indigo = Indigo() +renderer = IndigoRenderer(indigo) + +print("****** Default rendering settings *****") + +indigo.setOption("ignore-stereochemistry-errors", "true") +indigo.setOption("render-background-color", "255, 255, 255") +indigo.setOption("render-output-format", "png") +mol = indigo.loadMolecule(mol_data) +renderer.renderToFile(mol, joinPathPy("out/acs_style_default.png", __file__)) +print(checkImageSimilarity("acs_style_default.png")) + +print("****** Changed ACS settings *****") +indigo.setOption("bond-length", "1.2") +indigo.setOption("bond-length-unit", "inch") +indigo.setOption("render-bond-spacing", "0.5") +indigo.setOption("render-hash-spacing", "15") +indigo.setOption("render-stereo-bond-width", "30") +indigo.setOption("render-font-size", "20") +renderer.renderToFile(mol, joinPathPy("out/acs_style_changed.png", __file__)) +print(checkImageSimilarity("acs_style_changed.png")) + +if isIronPython(): + renderer.Dispose() + indigo.Dispose() diff --git a/api/tests/integration/tests/rendering/ref/linux/acs_style_changed.png b/api/tests/integration/tests/rendering/ref/linux/acs_style_changed.png new file mode 100644 index 0000000000000000000000000000000000000000..2c1b7402c40d1a93a9519f70ffb171e4f8a89953 GIT binary patch literal 10055 zcmaia2{@F0+x9(8Q9_2wE{#GeOA&^cG)a<>Y*A#5vX6Zk38O-i>|r9i$d)ZagoNyS zC3}|aS>J2?-}iZ*_xQf=_@?9FKANBV{w>#aUgvpUcc7Z;Wd=G9Is`!&t|$>S5MXUBC3bqiRp*BG8tt4UiC0K4HI?@< zcHa}a!-V7)_de-mQHbah!o-Zy-A^Ase(3%w8P}<2qWFOQ^vx;bj$EAa11kdy8S;D5 zzQ>QR`UweIbSuoS4}T3wJREwwzP@~A{qL04-Br5l%`q#n?#-d&jyRZ6?ipiln2?SF zJAyDtyg(s{`o(`=W)#ya9b*n8Xbg#T7+70dYiNASF{(bsd__Y;Ls3zYZ-@yYIZw(l zb2zHJYXC=a!RNT3=tT zuCBhQpdf5elGHZgh#LqKxE{O#oU7+m96PS$Q{Y~-Un zd-klXtc>+TzQdR*wzt^9yn2uwA&GE^GILm{jOwuE{fd?Ij3&Ri7PCC|Qb1fhKQ;Ap zM@L8G&aK)N6<)vevFggkgccR8#8azcq+cl35)# zDCkbp=g%%{i)msD2*MVh7WBwX82x-(+xX~n+O_k}GqDPZyp-D1$v=NC`n9WR3;|A*Ck5zWqNgqthZ#1$s>oVEF2&Enc${(*z3T?o?+M zw~Uj@)vLyFdys^qXU?3#IgT?OIdVr!s}|RIHOiWOb^LY6FMC%bqoYMXXb@!f^T<4f zLOFC&>)5elIbY{B-jqfW)YU^(_}y3Mj$*i_Yz-I3TQgZuNPD#pnpWZB#lX}DeT72d z={Ga9GFdvlIF_Pr-RfIib(z88aJ`FTT?mr?;^j+W{rAsGZik4_> zXqaqIW0@i#$Xna(Ezc;G<&D1%*tMrfF$uiO0iHTJ`4N z)6#nRMK#BU6=x^F4bg>p59*S_&M6Ah=n>TCg>v^i0+GP_osq8mw zQDY-DFbGMl7OPV1w8$FG(+Oh5OaH%GM{?=cPc931rOd1u;Rpf4SpEHB*o$F4grwXkH(lye^pR22lN54d64vW);K(?f)#{Hb( zc$b^2o~8-OWDdu(TUcKHj!?Lq=h_SMht1`_3%)eDs_lXsGc;t2N)oC+9dBp%XMUtn z7tZ|0trEu*+Gqs@h1J_=B?Q8Lpv<)m;w3Zl%a<>|$Kt)07Z!}EH$8d&d^sdhczF2B zmoK%m^+T1DGg%}K&z7RF_C#XbS#w6ry@KBK;UNT}zaSi;t)<1>TIRa2TMezm2Dd%L z%6c{VTfl)M%<9U@$~ro1)J?*lIC0{~j~|3z-@hloF&Eyi$A$9jP2}Xoa&pFmpVYSR zqVrS8(#mT>BULw`A`-vVM@v{eQb#L^nv%$GRHCY~==w{YZH8-uGKUdT3xHUEu|oja zZE5mToQw$luBd}QW97Z&L`Bt7+H`N-GB-6P&h$q?CNq)US7wS!OXa<{V#!_`f9etv zcsm0Z6{VWq}?x zbYpcm#HQ>I4XmiromE)hWpk;kILiOa7Zp}kR=DXEwSUmWfzxw*2R^d28X=tLZY{~Q z!f?SH4ty2dGBqiwrn=hqR((%_Rm3wbd!%yJX}VX!wBaCjvO9NpX{swm(&j5J^wqu` zdZyM7p6ggFmYix(>ZF{kA-Y0DkwRgQ8%^E~d94SLm#6#c&WrPkD#RT82~@!$V;2-M zC=bSv8LO5{lO6gD<6Uoux^s%{gNK^QwiI{QSJ%e%5!8$Qvx&+)Z)jOCM>E>@A>q zMuN})bOBK7_sS7ajddA|bJh{qg$zo0t~uJ;ieh@Yy9L7=Ak%YHO#~l8YEW16gjHlT zJ3}>?260JlXizTJ|7uWITTAzz=2=c7g1L0*l3m8GsHiBb&dgQ|aVmx%@K~Ejfq*

h*5VDLnY@IN4rbRaIqi6je$15+}0~c3ti$WovWY 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 U9HTt~`}4?+Yf9*>tA_sn3y@n9NdN!< literal 0 HcmV?d00001 diff --git a/api/tests/integration/tests/rendering/ref/mac/acs_style_changed.png b/api/tests/integration/tests/rendering/ref/mac/acs_style_changed.png new file mode 100644 index 0000000000000000000000000000000000000000..2c1b7402c40d1a93a9519f70ffb171e4f8a89953 GIT binary patch literal 10055 zcmaia2{@F0+x9(8Q9_2wE{#GeOA&^cG)a<>Y*A#5vX6Zk38O-i>|r9i$d)ZagoNyS zC3}|aS>J2?-}iZ*_xQf=_@?9FKANBV{w>#aUgvpUcc7Z;Wd=G9Is`!&t|$>S5MXUBC3bqiRp*BG8tt4UiC0K4HI?@< zcHa}a!-V7)_de-mQHbah!o-Zy-A^Ase(3%w8P}<2qWFOQ^vx;bj$EAa11kdy8S;D5 zzQ>QR`UweIbSuoS4}T3wJREwwzP@~A{qL04-Br5l%`q#n?#-d&jyRZ6?ipiln2?SF zJAyDtyg(s{`o(`=W)#ya9b*n8Xbg#T7+70dYiNASF{(bsd__Y;Ls3zYZ-@yYIZw(l zb2zHJYXC=a!RNT3=tT zuCBhQpdf5elGHZgh#LqKxE{O#oU7+m96PS$Q{Y~-Un zd-klXtc>+TzQdR*wzt^9yn2uwA&GE^GILm{jOwuE{fd?Ij3&Ri7PCC|Qb1fhKQ;Ap zM@L8G&aK)N6<)vevFggkgccR8#8azcq+cl35)# zDCkbp=g%%{i)msD2*MVh7WBwX82x-(+xX~n+O_k}GqDPZyp-D1$v=NC`n9WR3;|A*Ck5zWqNgqthZ#1$s>oVEF2&Enc${(*z3T?o?+M zw~Uj@)vLyFdys^qXU?3#IgT?OIdVr!s}|RIHOiWOb^LY6FMC%bqoYMXXb@!f^T<4f zLOFC&>)5elIbY{B-jqfW)YU^(_}y3Mj$*i_Yz-I3TQgZuNPD#pnpWZB#lX}DeT72d z={Ga9GFdvlIF_Pr-RfIib(z88aJ`FTT?mr?;^j+W{rAsGZik4_> zXqaqIW0@i#$Xna(Ezc;G<&D1%*tMrfF$uiO0iHTJ`4N z)6#nRMK#BU6=x^F4bg>p59*S_&M6Ah=n>TCg>v^i0+GP_osq8mw zQDY-DFbGMl7OPV1w8$FG(+Oh5OaH%GM{?=cPc931rOd1u;Rpf4SpEHB*o$F4grwXkH(lye^pR22lN54d64vW);K(?f)#{Hb( zc$b^2o~8-OWDdu(TUcKHj!?Lq=h_SMht1`_3%)eDs_lXsGc;t2N)oC+9dBp%XMUtn z7tZ|0trEu*+Gqs@h1J_=B?Q8Lpv<)m;w3Zl%a<>|$Kt)07Z!}EH$8d&d^sdhczF2B zmoK%m^+T1DGg%}K&z7RF_C#XbS#w6ry@KBK;UNT}zaSi;t)<1>TIRa2TMezm2Dd%L z%6c{VTfl)M%<9U@$~ro1)J?*lIC0{~j~|3z-@hloF&Eyi$A$9jP2}Xoa&pFmpVYSR zqVrS8(#mT>BULw`A`-vVM@v{eQb#L^nv%$GRHCY~==w{YZH8-uGKUdT3xHUEu|oja zZE5mToQw$luBd}QW97Z&L`Bt7+H`N-GB-6P&h$q?CNq)US7wS!OXa<{V#!_`f9etv zcsm0Z6{VWq}?x zbYpcm#HQ>I4XmiromE)hWpk;kILiOa7Zp}kR=DXEwSUmWfzxw*2R^d28X=tLZY{~Q z!f?SH4ty2dGBqiwrn=hqR((%_Rm3wbd!%yJX}VX!wBaCjvO9NpX{swm(&j5J^wqu` zdZyM7p6ggFmYix(>ZF{kA-Y0DkwRgQ8%^E~d94SLm#6#c&WrPkD#RT82~@!$V;2-M zC=bSv8LO5{lO6gD<6Uoux^s%{gNK^QwiI{QSJ%e%5!8$Qvx&+)Z)jOCM>E>@A>q zMuN})bOBK7_sS7ajddA|bJh{qg$zo0t~uJ;ieh@Yy9L7=Ak%YHO#~l8YEW16gjHlT zJ3}>?260JlXizTJ|7uWITTAzz=2=c7g1L0*l3m8GsHiBb&dgQ|aVmx%@K~Ejfq*

h*5VDLnY@IN4rbRaIqi6je$15+}0~c3ti$WovWY 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 U9HTt~`}4?+Yf9*>tA_sn3y@n9NdN!< literal 0 HcmV?d00001 diff --git a/api/tests/integration/tests/rendering/ref/win/acs_style_changed.png b/api/tests/integration/tests/rendering/ref/win/acs_style_changed.png new file mode 100644 index 0000000000000000000000000000000000000000..2c1b7402c40d1a93a9519f70ffb171e4f8a89953 GIT binary patch literal 10055 zcmaia2{@F0+x9(8Q9_2wE{#GeOA&^cG)a<>Y*A#5vX6Zk38O-i>|r9i$d)ZagoNyS zC3}|aS>J2?-}iZ*_xQf=_@?9FKANBV{w>#aUgvpUcc7Z;Wd=G9Is`!&t|$>S5MXUBC3bqiRp*BG8tt4UiC0K4HI?@< zcHa}a!-V7)_de-mQHbah!o-Zy-A^Ase(3%w8P}<2qWFOQ^vx;bj$EAa11kdy8S;D5 zzQ>QR`UweIbSuoS4}T3wJREwwzP@~A{qL04-Br5l%`q#n?#-d&jyRZ6?ipiln2?SF zJAyDtyg(s{`o(`=W)#ya9b*n8Xbg#T7+70dYiNASF{(bsd__Y;Ls3zYZ-@yYIZw(l zb2zHJYXC=a!RNT3=tT zuCBhQpdf5elGHZgh#LqKxE{O#oU7+m96PS$Q{Y~-Un zd-klXtc>+TzQdR*wzt^9yn2uwA&GE^GILm{jOwuE{fd?Ij3&Ri7PCC|Qb1fhKQ;Ap zM@L8G&aK)N6<)vevFggkgccR8#8azcq+cl35)# zDCkbp=g%%{i)msD2*MVh7WBwX82x-(+xX~n+O_k}GqDPZyp-D1$v=NC`n9WR3;|A*Ck5zWqNgqthZ#1$s>oVEF2&Enc${(*z3T?o?+M zw~Uj@)vLyFdys^qXU?3#IgT?OIdVr!s}|RIHOiWOb^LY6FMC%bqoYMXXb@!f^T<4f zLOFC&>)5elIbY{B-jqfW)YU^(_}y3Mj$*i_Yz-I3TQgZuNPD#pnpWZB#lX}DeT72d z={Ga9GFdvlIF_Pr-RfIib(z88aJ`FTT?mr?;^j+W{rAsGZik4_> zXqaqIW0@i#$Xna(Ezc;G<&D1%*tMrfF$uiO0iHTJ`4N z)6#nRMK#BU6=x^F4bg>p59*S_&M6Ah=n>TCg>v^i0+GP_osq8mw zQDY-DFbGMl7OPV1w8$FG(+Oh5OaH%GM{?=cPc931rOd1u;Rpf4SpEHB*o$F4grwXkH(lye^pR22lN54d64vW);K(?f)#{Hb( zc$b^2o~8-OWDdu(TUcKHj!?Lq=h_SMht1`_3%)eDs_lXsGc;t2N)oC+9dBp%XMUtn z7tZ|0trEu*+Gqs@h1J_=B?Q8Lpv<)m;w3Zl%a<>|$Kt)07Z!}EH$8d&d^sdhczF2B zmoK%m^+T1DGg%}K&z7RF_C#XbS#w6ry@KBK;UNT}zaSi;t)<1>TIRa2TMezm2Dd%L z%6c{VTfl)M%<9U@$~ro1)J?*lIC0{~j~|3z-@hloF&Eyi$A$9jP2}Xoa&pFmpVYSR zqVrS8(#mT>BULw`A`-vVM@v{eQb#L^nv%$GRHCY~==w{YZH8-uGKUdT3xHUEu|oja zZE5mToQw$luBd}QW97Z&L`Bt7+H`N-GB-6P&h$q?CNq)US7wS!OXa<{V#!_`f9etv zcsm0Z6{VWq}?x zbYpcm#HQ>I4XmiromE)hWpk;kILiOa7Zp}kR=DXEwSUmWfzxw*2R^d28X=tLZY{~Q z!f?SH4ty2dGBqiwrn=hqR((%_Rm3wbd!%yJX}VX!wBaCjvO9NpX{swm(&j5J^wqu` zdZyM7p6ggFmYix(>ZF{kA-Y0DkwRgQ8%^E~d94SLm#6#c&WrPkD#RT82~@!$V;2-M zC=bSv8LO5{lO6gD<6Uoux^s%{gNK^QwiI{QSJ%e%5!8$Qvx&+)Z)jOCM>E>@A>q zMuN})bOBK7_sS7ajddA|bJh{qg$zo0t~uJ;ieh@Yy9L7=Ak%YHO#~l8YEW16gjHlT zJ3}>?260JlXizTJ|7uWITTAzz=2=c7g1L0*l3m8GsHiBb&dgQ|aVmx%@K~Ejfq*

h*5VDLnY@IN4rbRaIqi6je$15+}0~c3ti$WovWY zdu6~mbC@kwumNvo_9i6cx@Q;7sZ*zL#K}d42ew4wgO2oYANyS-uk$W*h7;eC3BQJi zZ`2jXrT?hv@0W#40(7AH`uUY$1A=LaI%~eZJniOsGM^Fg2#t!G7#X=lxadzW?ets6 zU`x5<*T-0Chr$PaX5AidE}Sw=STl(~Dt_-vx72pwq;#gQxi%Id^Y~BQ;0}vL}q5@ z@89>a^9u_Oj*cEZjmlT4pFpKM@unH_mL>Kh%AxHD^L2J!if*3S@#DwsBz@Sg85tSr z?R=KxGjoaKomrfPm1tgy$cNRRuS4Xplou~vzovOA z506m*8aLNhj-0#S9m;oEhc=B}kCv$rb`!8Kge-LO}%ZYP=$@D=_l^)Mv;k$iFofVX^gI(9zXD)VB`Cg zlj!X1Z2Mw;q~cyaqajxW&}h418YDnm6=|~&2PpLDf1AAKQ=qP{u95bFh&5(` z@hellRmgnD*2>H!LmQA~}%IX+n z_MF^YT`20#?^Bi9yKy?Yx`0!~#HmhzcgxO9_Kf?L&-AiXRa9V0%z#Vjnj5`NN;0W` ze!PFFgg8~xBe~xrpf%#}ci#h4?8EIbhO#RZJ_)e3D-!GLe?>`&92~s7xOlMotF(l& zib|Qo7$fHI+L}G>&A?|$jijik9)Jw7<5e*7`_^?ty%?~*Hi$D2j`bD*e&%qFnEBV( znwlC2L@HjIncd|+pi67`=1zqd0Ac10wip7|&e4&co}NqI%R^sZKVHt0ifPCA`TdFF zqUWIK4|K5b@NCs`+T;XR`}_HQ3OM5BGSzvyb!)d$tQGp^BZG~Ya}OrR$5*S~<*4vm zwNfs1SiMn=o1B^1+W5Nw;RKmNvuBSOJ$vBUU=gk@i0Rz}82jD*c# zztWGVs+HUMB}_ziCjUvvK&SDR#I=^~hKjyiuxpzO&VV^gSZ2xWceq_Mm^SZ-w z#9q4SP%{u0ob!x+gwuBOG}rF_Fqlb;N;{i_ZG3TnhKFy6O@&W zB|}v?!OUO|B=chx>eJ&=Jp2<*8oQD?$LW?J$dGN!Msd&rJsLL zgd@!K;!jRaHmwWhzO(8ml5P;)*x2a4IGzZ^$^v%BU#5ipsSRA{vjd8kJ55K2haZ|E z)T-US?FEsFwQQyMg@v(9N%1SUoAL?^f32#bT%}@z;}5yfJk^>(Sd{;PBdP>ITcL1e z*%bTzw=P`p0n)m$Du&^fwg-mth=>(EN)bk@z5eJ+8!u(|kZjfz{po@D0S`$iYM|ek z)NO6&(r%;xCvSUr1`%m!XqaDK&Q47gm6WV-`EwV9g08?r#riK_7MGVhEyNEWJ`4ah zQ0ffiPi4Qz>^+q4{M=lmv;#lprk0lCrAt7vgAN@!^eK)(+I{(cJFaP_sq2=Y}%E^`kB@$BvL;Ds6Jj3tbzkd!77hCNpga&a& zaW&u*9aorvY%4I3I1=gcO=e#rf!0G&x!?27kH}>~z7})N)ZCmUG$myWGW|1-F;Y;4 z?&H-sIZvFFR3uq1-vZb?933pi4TyG_axzqR0=4*p)C5h$Pd5MpZF_68_@)mCtJ4N0 zFNj>J@t2+)wv#aY^*J;ZrM4dy5)&KCB_i_n+c%9s!t?0p$=TUv9H#w>o?diCMTJ&I zL4JOj+fsT=3@5Cm1lIDP#A&)=8lTWMf#u`-ljy_hh>Oz~?y@W(hzYLne{fS7xak2%B+$J@HbeZBni|6W z8^$zm9t*4I zk@HX@z}IZ&-Na$EK;*%PYwTyu=!=Br)@Li;>J>aDa`9pxhlGq-;tdUXol6k>Kq4o} zt?~2cxuvD7%*^}WlIlS#n4E!!QJjWr86X&-(gU*H@%sA-G%oBDJIvQ+aB%R(8X0imfS8Q+E zShL)_b?X)g_79%!oq;582&Iep5Bi*_T8`L}qAvOlZVZ@ZPnB`0s1{K{bPL{L?CS`~ zxc>LKp6gEVNC5Q*%E`~kP_)lMF2b3=g)?Vx-dOzu1`(6lF`ddXuJQdPD2w;bL!J?w zo^Ve{Jl#4>M0dANHcc7mIk-35I8B1OZ4s8`y*caR-T_2hxg*nY>HLGrmh@%EbiG8B zg(xgWr@OxLb51<%I4j)m6!=y<1kj0+bh zeCEva^_D`zkGtBD76{aY>y`z*)zy5Gk`ZKx&a-wu37vtvsel*8e+`Qj5D?dETnM@LyjZvXDd6PP0KnvBy_=76+G zxfY{H;0RI_j>rqHKG53Q3Ihq^p!nJmQE_;#Em$-fX^Ve`>j;kEEhb(Jc&BReEtuy# z?tZr@_TB%;7V0@L4j^o?<>i}TBfqyQ*1rud=DCm)Cq+cG1rPQUJv?MB5jIo z$%w{3n52?)V;Md^KG^O^*lzXz(C=V&fbP^0IJ58nv`8@Iy(K77p?_N>Kfz?bzYzKG z`Sa)Cd(OB)I;5fuO$5<~_rdfB1BZ_Sr0hZe`%og+a_8GS-eX_6v6CGctj;sCBrzk6 zy=+oY`3wycR8#T_3%`Qr+FyKjitq)jmF9tgxT(kgGDz@_Eer@6GCVOck)Wxeft_#D zJbX&0vV)XsTt}fnSjK5cLGY|A$={wohqi`2xVIpeff-c-5%Ycc5Na)CoJQI~2kqVT ziJZteE^hAW-@nO9DQ(=cF3*Tu=cvwwC!^IsNkLxT*(oVr4MB8eEfl{avMzXns;VmI zx%-CP0YN}6h?DJsWFA?U9P?%#$bcjOQhH5qH^ZS1p8cm6e&*W@eSi{J#e%M6EI}C> zcBJ3@57WcwgskjFkmMjQWn*IlvIzW(yN|4qrwlUt%aPK3s9O-u-IUj_i^I(MuSGxN zz!QM`u7SQHrYu24(l*tkm3zwtiD}#JkurtDy{T9YQO2IQjPu(hfnt}TG z@nb4Vf6w`?8b@H+f>gMZ@mQTBWBACQzkU^#ktqeK z3KPrU;goCRt$bJhjksxdGr~sTfo^J~LAQxaR(&NaZTlVH8R#;+ zIMD{HeN>nAaSoP;`czY6EAbsR<|0FC1cV*9ULdc4pPO>iKB5OOY54Snl`2mj#Qw`6 zci{(rysa&)@9`3d<*ltPs1~9Sn7%FlU8$JW9Ua9j@o2~C-tba$&5VwQ5qpojXf$jT zO)iLJFzPX(;o&!JZFBtm_CQGiuNeGs1FDe@&!RFj*aQ@`|29vaIAQ1P+}zY;%1u!F zcUyp3R+5;;hkz5Pp%-Uh;`{dP%e+BCU>fI3-B+?pOG}~upmYqHCtlM9T{;3k%bz}d zx{rk)I*cGI|Ho*=t5Itrz{c|I&~*X^gV|oKlh>)kkl_RM4Wq83qob@$2RS7u2&EP* z5clCA$@k4!z}04Vx-7fw{-ek8BjCu^d_)E4f-0W^R7=d{iUbrSHVJ$POg+8O3?ww9 z^8E$UGl+-s9?-@`R?qVC7N|z0gjFprGyxR5i<{fv2hSL?(@eiKuq?<9UYMl(<0ns& zH72S94g`*8y!}AOz+f7uMEbBcnE^1u9O~=q3+kii-Ek-@yCV zwdDl!sX)C18yj>mpcvTLJ^B@rvb)K zh1CPP4?;w`@kTTjx2Z#Me#~L0CMPEcNXZXgrakx+IL5swU`&~ymCJ!|5loDYUu&fG z7TGl8LMunc#=yu+AC2e?$glsq0mafZhg1-4KsX}+58fvmq^0&H> zBP^}3BmVU4Rk%XPxG_q;v*+dbE%Fq6c+zn8* z4j&fYsaoH~fC%E@p$mWhfME(+5#%ap_q%aQj@frAsBG!+V>-YCXdl_w*Z{GO0Ah;< zC3a7o0~-ytz9qr}22Y+@6P5x>bcbz_-crf02l(6|+4-^V?(X5r9VJAo&OkzXP7bt$ zc69rPt+gr8!=N=bd_hzIRX+ucvGjJH8G|Kq5fn1u->>XLbbFjF#qi|`^!RcpQGdX6 zBK#T|kswZL0$-k~J|O=K2sD&~9ePUg29#e~nEP=tN<~XklR(&B>#BG^7T}XLTe1DY zdwYw}PPw!nVrzW-Gn~u55>#qDxYPC$hV7~9sVLckGwSeiz#R2m0REp@MT0-J_CUL* z5L69NadTMJ`K09Jzw7H@gP%N^0X8^zCP75(>f2jI)~;@D;Djk3d%mAl7?39bbOi%d zTT8zgX&O4fcp5#d*jRU&{-sVx+~LsdT> zk%bl^e;9bgBr=$$PavM4A+@8xlGc(O6clz@lJm#NJg`m%UHc@Sz&b_ei0xt?lT?xqy-p!{^;Nra5Gk|O~{|nP%#P8XUG#rXjsl{Q&o8S$J66aYU z$!2C|%L~o&>wo`($bPd6{v>2a#~-7(E{rD9>{ThU9(W(llcn|KhEO8T5j}TjeP#B* z{{4JdT3T8VSm`%UQXM%^o}i1hNi4@}lc`s(ucn8AE3W&ps!zvUiu*#}&?#rhn?}Lju1T8=! zm)WEQBby`P?pHz^=S+atug@4P-M)RrjuXr)0@q*3rmbDH(|QPYNc?)5zrX*xue)m1 z3WS)b{q+5uOWd?-G;E?R2hNL#%$in=#>qG{w|iJDwt z6xPY9{Pf8W>_8fG^D*G?V0I{{v|UnE1mJuN?eP*7i@j{Lw`)omJ2GG14+?CW&OJW* zw}th0Z^4swSDdso7@AKZzIh7t2GVW_%oJ6)uUNwr(qRfdipN-tD9sFI_9NxB-5uLn zr$sEj$QMJO2l$cI+#Sgi96_Tg@m@a*W2qg~4muRM2HJmhL7bNffN~cuTsVAqR}`m| z$7&9c8~Ec&DZ>OzJz;MOoUZq3QQ2I4$55fzwn>4W0r5tV;o?-+EcoMP6%!1TS5n%_ zVF}RNG~^~UzmoU15f`p}bG>Mk_3YWR z)b|}ukfH!UsO?ohIglW|@}1&_T%s*x6YQhq3kj25cZRR)3xzYQD=OB;dvET%C}dvy zlpSpK)XmM@F^;`~d*yeFdvT?f_FksWG)#eEuA*QzT8@#5_hisl&>_3Cs$p+`24j?W z8u@s3R7!1~Y$7!2X%nj6{yZx<_s%lM%%5kI*AI-2j8GNVdNmC#4Z@Q)D`of|Q-3hh z;*9TBONQ!rw(1vm*mJe=T9i=)ip8M7qVl0ixplMh|K+72ZIOBuNn}!vk8x6v_7 z0=dJ}i6CO%8_CB~&%o+CLtrpP5PbW8Ty7m0)BmA___=A{~m-h%_iE4N{VV zbf+}`J$mmw=eyti{&mjt9Q8Q!#{TWS_F8Lif>o5{Nr@SV5d!sY6~gr3WVm@)pS0k7OO@1%U@F)B*Y7%caY_k zF;WlP|5fkeqJwu$bLstH2+AklbNb&xQifs z*Zrl5Ev+^0i$XU&yDrwAL=s<=w;`voiOC1;eAAX!munV9)LhnddQy2I>(0=4um3!ztO^0l?KKavd) zq&n|Iad|EW#_Ot;n71K{gOgKapYr_q^ETq!%Rjy-#=q|{=b=1~ASDe~e_u3)<~0d1 z@y_Og)lMbKbzUX2+lKXdRFtr3GhNqVx%15JcuDW(SUB*K&yG%|0#@|FR@LrnlPzhv za>>Qxm86uRED~bE!aeK0mjnfQo}WMvu_nSP*N%wcyu7@$^z!n9ow)_^<+tf??jI@C z(Nk5ucVLVl?b;u&6b-DAwd;G74+}jF4PEF+mPe7FASJy_cf!ZVXF&>1@BJ6y-xr#n zx9)QLmZ8clrje);gCawamQjuuf2|rj;<~fYW73X@b{d1vfA3(jBWh< z{34-8@bSMN1|W!Z&U@h^l9Z@@R5Np%{bE5+E3wsh$&4`H(~pxhkqBbQOcOUipw&c2 zmad)qzOk_}%_{Zg&6{`b+zIC5;d!5sP_+{dStOQnp|JUTDrTxRj%P0A#*16g_co?G zjMk?+=DBCm%w8TrkYg&eRxb;SzPZhPDC&8yeMd*<``8tWdHXdvNQLUW@BaDHmh?so zS(uI4Zb;EMQChFnpN(k_o{}|?20y5028U;K>W-P;3@s=SVpq6v!(t^N-JO>J&P@-opGlW|8W-(tfeWLT>jljLDA#D~O`x+29d0(`S4ujGtK0?88zGY$I zt;vb0&Zexu+uV_MWh-*l*7GGUb2@V)kqW66Wp*PKWcw$RJ48*JV=BCNzZ&O#`t*s9 z1D<3pyl!y#K&Nh*r<{O*AU8LcA@tFLe_U9YfKlC(p2f1h^*Gh5SFZ*QPYRVUa*K+7 z%6G>@s+!LV6k#%=_V0!iQI(BT>vYW=fiU*(Gjv7(H-o`y@X0Ix`XqtF ztH?&9XA*Mg-Cs*~E16JjiX%)CzXqKkEH>j$KKc9m2UBwj>xIV%nmpeAjF0%yU$?Wh zD~$@@DEekTg)txRGxc@AMK~CX74SAx(a>;R8LKV#{3TpYbk_5Gew*Y#@O8MSMO4fH z!QG}BO^<=T+!9g<1qlt$G@1oLa`109FZh2j>Cv};BkGWJ>ztJhN42>SRk&QT#W-qf zXUAuymR3uqJ_q>R6OvYI*I@9{_wTurk#(^!8`R#w{&=oQx`O|#4(FZODA9V+s^Rv^8>zkT)*Dn zxl|tg@?|4bx8FLmgva{+uMu)GvN!OT?(SEXLU({B;sd$Sy)(3UY=FLBLb_dr?=rz-ZCs$?^>$w>w7rUm7c#6en!40+n`N^9Pumg zUTYC~zev^bUOV6Vdk{VnmJlmq#~t}~ z#Ai1YyR$x%+&ibIr{^9tRtFbncXV~t|7cCmXi)Cq0&wiLh9K9w$7(~ghd7dIYS4GH zN{z?$_}4}Q$QMT{SGAXZ0Da!ef*ws!J=? zm&@$_-rrBD>f*A|{PL1}JsuMFDkg?b12f@RtdUu`QgV~49DS08X7jsMcJ4|P@7>M$ z{x7&!izUsBmioa10$U0DeZ2{9TU931pLSd0JtZnBP!f|;dg zAAs9OIMC} zwYMK-R-kC#IEW1h__8|D)DK7_diwMOJ1C@F2r>&?I3gwnTVmy(^-Gz>XX{72@4-v# z{KA5Zn4Z18z!Dlkl2Ur-WMyTotgM2k!{u4}3$19E(D?qHyPWRs?g2#PrrD9XM)hOX zW{6+wOM!cz7l%rN8>*{MqfmF>s;w%WWz(`hppI17*c`CSUl}g<^xc`^tZ+w=8uQ*P zJzPqZE>(n-44fz#o>aJb^AvII#$0bO7aJQJCue?&$%DJooFzkbC+$C=++8MjW?Hz(G!BblF{*Z81Q>q?I+ z6%>XK7l<(d&rNF$%-cq??RI!t4T+6!cSd{e+K-|V`8G*bB3QvQ<3*OS7M&eQFXXj_wb2t{5fqex!KO*E_bI_0b_WBbO()Vw@FxMD(rAJ*g7 zVqA3e)a>k6T!M|NsjEZ#&?|Eb$L{U#lM>28?Yad~@i~{P0e!sk#uF&;Y0*E`g68BQz3P= z=fhp}$y28y!^0n3GWz}~>lc}^VE^M&7k)Zkp;XtpXN%^AyrYAg@n-Z@@A%{RH}d#q=a9f_k2M`982&i2#Jp$DX{43+xpq~ z0;+KwckyEt2Rl|8m|$}t1sSCa53`EO-(_aXUcI`ywievb*4Ab&R8m^HMn-^CDPFr4 zc>C=h;9qct@8LmladB&FYng2Dk&@v}zz>}~rTJ_+FJAlpT=-&34jEqcocQ;zdV}1JDH?`s5nxlGl^=COa z`UeK6RTULm)@QnKSTkxg`|a)fML-}94vv+lX9T&*nW>qW_IBp7^H)CF4EXGCXyT6R zJwGq64dABr-ejyD8H;#~9ZN^Gzf;gDTG|UIA4?r<1z@2E0|(S%J%JdK%+Aey`SK+{ zKR>vktEJvZ$@nF+*0Y?PYb{3*L&Z$ZY+NdgjgPBhP~F(`sU^&Gci&%rl6p3lDHOf63ZnZAaLPAQ+4$N4A+WG?*RSg z=4M(K@#Bz?skZpv*zW>(8q}@Fu5jxXdT4>kxjgRIbn>%>%vc=50>is4`c z;klmO8}#T=PfrgObj9IZBcdhr{lLxv_wDWN)2C0*ukTzms+%7wjm9Mx$-XlnnE| z&B##UItdtbCJFBd;m^rQu`5?rAQcQvFJHbKKu9Xc5Q_deH)kcp;JH3sjf%h~&Mi=h zh=_Du(EuuXx7d+mL>xh)61pvMo#$EzP2!>}Wyx@srMda1rHb9BtWrv7TU%SgfFFyc zOTVLL{rK_Ylf>7wwdpA#s;ns2=GNAy7%m6d-T`&TV%^N{uf~Gx?=muCE}4Nn39Z%&AbH=zl>12i({kJ0qCD&V@bVpkWfvi z@RNqs{o(*6`#*wM^Bm;l2Mb5>_6U- zHswq)OIxzDvmZ?P9!zCsS6H^a0P?=SyHt4y!CY_`oE7rpZE9=dKY#u-yPe(A!TxSO z(TMw0-0wov2e6Z8aes(~W4~3pGe1)4TZ5&X?|UY75F-gPSr3&ImX!^bjnKF-NPEC#$e6`y>YhpNaP2;Gfaio}eT5P< zO_MY8f;@DFFXU3~@%&l9ZgwLtXkyJ9!cMxA|nw|QB)mA$i*q>>2IJXCMIy*-9WF>XCE|d@b&aAiS3^+ z=Bt5akdu?Mb8~TBS61f46_5RKJ1R!TkT?fkysnIEx8lS$pf1HdH+h!W7W~CXmo*Cu z3$1yhv0mQZ6VM`HTMvE;$G&kJ3vP)O$tRj>iNzf3xwGpO zm{VRT-`Lnlqkfe6?%n&Hrh>a&wNr4zGKfFZm+N$B=1t5Mi(?F^;e z?d>04b~#U9zh=_dhW@uyJjKHPE-TAtB2u$Y6Ic-o4UHU{o}Qjua*NG#`Fo)i2sm^E zuwY4{g`v`-x9hJYg-i}FSOV&#Io!X`zUXgxnyWl&6#(lQqsU^ff$s$K!}QHwi(=iy znVFd%KU6g}i!e*WetyV>nj?3^AjA+NYU=T^vCk~-lP}HIJtQbw;!q-zl5eoHv$Mk1 zeN`1ZVTwyelo~^%}ISkz-v&!6sL=VZnw8m&kAT$>OAw@ z;-->c*6#8b@~P&R$+zF$x>?51(<`8Yk~|gxx_rM@R~ZBh ztHMsp$~|S8nVxQ7i5E0cu(9DCv1W61adEkmcI%#zvT|Fo(-aV0FNIX9=w%%u$+n-&5B5qIOF}}SFZJ{J z%%_;JJHjEBIG<-EA|!mpVw|o)8BThjd4eXelLXTvcizRp!D6!;XkuxXXZgW6>!DVu zD{m|x#ASETsqJOjO+T{pe0-**XQ@f@$(FG&{Xo{8ioa;5 z$|^OyL$Ad;s0Ne`+J4V`HRTz(925bLxS|+OFpEWaf4?s_bNnn}s^+KHadC$wuLoYe zk??#a`H8Burl!WgXFIi|q@<$aAb?!*x}xGl+%dH2*NF4hn&&PzFC{;KyCg2sM5H&xS2DSUG5=lLR{q<-3N8wnwpyW z`(I-8CHppAtm|&wytxZ~;#QM0f+?^W5bn|thSX5yI_b3*6Qav^I5#j?sb4WYKEBrb zEaMD2d&TY|N?6$2=jYN=n~u{W>e>L?V0Q1A(i`!a#l^)RKYq;21h1VPc$<`T^{rZ- zP0r4*rTExbt9(0jf~=*bCAc1an=??TgaL{2&sc!N+<@Nd;Bb)=s`SRBUI@~k>;0|D zaj|087ECB$t&Kd#?ml?&;zfEo=Wb0=N=i*jOX$!fL4pmd@7@Y{0DW!~Un5mjRlU}* zPqVWD)QS649`H_w~yoVsZ*yQh?8;09@^GDp?|*!=FX`L8VQU3hHy<8&x7r$JGXCR zuk%H-N_x@VEs?c?09=pPOMy==DL=V z(Kwd!OlMbDa6@hFHP8(U1JM{RKaG1bghxMsRL2PfM!0&6OBK@M^=k-&lUPsaFR5T; zM%y?1+tooF5E6nH6#Nfy(6!)%_wL<0HEwko89ZD!xxmj)o=rg^>Ris|%*V%fGm7Wl zI50zEFDLe#irNs`e2^gwp@c5956SiTv0oa(&-8z2)zj5&eSKxExBOR?uwI3icIs^q z0#qHqr{mwed76(Wy#+THH80fpNUNo;-u;L6CT;Gnk4yB~8_tg_2Ddt(Xkj#1ORmJ#|Jl?n`Gmhxfpyg!il=+UEH zAX}j0(7@+Rfw^tYH=<5TmnO^+bS!UdTwbsMbGKq|^=5^;Up9ZmmOqZ!0Na}7!yYTq zr+D$|)hn3*Li$iS_Eq>a-3b;JNHshnXoU)?&xe@ps{HUE8PYs|;2mRy&r{3G%LA{e z1BP+@_;C=Q{9w^u8j1u%Cu!0D6Wn#>KPcj(8j>7&B%f7F$ZMP z)^J|D*a1`;YiVU=bpO6vJ)YGr7#Uhag+)cm3JPP)LA^j~p5CnBx^5F0|@} zv}qP0*XqJa>%A3uVV~WFB9J(s8mEu)hz4*)MnrI(e9S0fOA)S+D(1Z-G#4QA9oo?6 z&tNS(1vfxlgQE8I1R2zRZ`wm9LR>-uI&gg*FtB)^o%18&hRl$Q#=uUfsHh-~Qs5-D z{fEN8+Nt z8-8um&01TU={BPFNBEUH?B~cU#VqUn&;G8%A5F1cf9 zC=LpbPo|e7sTUkfPyo3rfh4rh`L`$W);i6FaFv&5(gY#Hhmf5V0=w+mHIsi6uP0-w z;SeJfOu%rXyaq0Xsp;1Vb2{nuWp={zVU5NYG~UC1(aw-rK-&NvCNJ827t+9(2)@+F zyf#E&Q8h<*mYth>Hjq}|aNK3CCpA4C>P8RqvmqRul>8MKeu#Q)*@Jp)CV&VqrSdOji(CE^AdaM>~HkElGLrI15WUjMWs4_%w-- zJJ*75ss(Zf#x8JT5pb$EioJJT4Gl*D7=kl^#t;VJan?Lm?INlByDQ<>kNYSzpFh@J zscVZ6p@Vjr+r@txP$X24{%3I~c(~JBiEU_XJkQC=$;Re~VW0N|7Vc z{EXp($2%2B;5)0&xr{WA%^nua`zyA zjlarKRa#ow);23S`3$Hhng{UuprY?VSF+aC)qNcnRwY7SU^86ij!Pr3wERm*s2;(G z7NPAzABhic7#`M7?;;=~T2ng83uVkG?q&}}jZ$K7`3ne;Gk_^H4?9Vyq5*A~m~`*l zk;S1+ZGY=$V~?PpqoZMQaUayUK|kiO0Z!Bq96egLzd2xChFQ`txPxC{)yoIka~jwi zWLcwZFG;7SzJ8V(ceY-s92z9{l`B`2l$6S3t1G*|eLDeGL>%eV__&b}gM_#^*pWFr z5Cm`N?JFxQ*01B^D=oXZvm8d1CAWW4!*#h%pT={cwZ<#a&p8en4Ja(iVeIJdRvRj( zdx`rfU@CE7();&MurRJuel1$J!MG!YLQS^RrMnHp~Jy`ST14VsBS&WyftCJ zp8z0?tLt6LcKvyQSm?=+)@K+I zim_J=D!kmmpz~VmOT$S;oLC0;X_l{#0Y2jd0~`4#bm4E_D50S_14euAtp8yC`bf9j z1Il{1#3clql$0bWD7YMq`tuEA98y3@pyA#?J$Z(Mt($-tLz(S@#J5hJ32>ztorU`SVq%6n%XqG$kcv{ZnQm zZvibOFo~U=olQ;8QMxP*4;cyoH&Yc_aS|c41|2PT*SDf1f zJs_}zM3_X6zb^~=Z&+9uFes{UP*EM`&KS%TvqBy>H+O`>1k|q*8t}WVrNzb76&w{m zatKH%6Ekx(kM`%fz`#HlB}~T3f2ZNoZGf&~A;j=aFUMrsqq+xp^#SNM4t9C@dKd^i zI6?%*vH4#tpEs&}w(?TmrBic1W@KFZSG#DtgGA zl7xhW8!yh~u8>RZRf!o2B*Rb#+ED%qD3B{|%Z9jUeeu79{3mZtPt(AFm4)R^T%02M z&6_uJ$tzi&7#$JO94qq7j>Q7!I{gcct00HB$P0DK0R`BfH%-`8Y^MQ?ETP$5!FPZx0(hkU zmq~|Qeb7m=j1#IPWMrn%)qcgIVq&IdW*1Hp56d7A@kv^d!;6MhewY#OXbkaN_rv{P zsu;XiS9?j8ITec-U`#PRQYmH2HJ}R9Sz10_u=`K58yFZQcl0I8lY{eo^e8Pq{$6lw z>;TTRImZatdbnG87{Z$+W(hN(OQy|+q1#;XTV+fAd{k;+4m;%=N&1z?N3mhydrh0#TpUg;wg2U0+& z$1k0Un`8W$hAfkw@q#g`VE~v;>X{lVq(|6{aqso0%>zXozky!>x$hTMTT_!$GO(_5 z#ysHGxx2ad2})mOynFYxAd?Ug3g{$h_kZ%_30RQ1D`1E{z7X>Lytw#Yq^AD8aZ*NM zCQ*AoD<%Y{7?-4-AR?fz03FWEn|3?&wGfX!{T*r0s2uP@C&4i)88{6yWZ+oFP5>DqZ5mq`D zzF$K-7Vm|M41+wFh`U|`=eq*UHDBV!H_Hw1PyqHRhwda?W&=74gT+oTi}2kY7{Pt^DBKv02BQQNB6LWc$K}2`uMOr1q+fPQ3OZ3`@RLIT z78rbfULF$@6ST@va#~tE3QBJn7~y(7Y^bPUT0VDmIl=Q=zwey`#?~T2?k5dLpD;Fd zKkzD;LcSE>b-?&Ms$4=cp~yiInIgIENn~_WASYaK(n! zR;A4DJO>!QzdcU)2N>h7IGqy`D!>tI7Elj110Poa7@_TKZ7vK1!#5TIu)zidMeD~I z0mul0>cfp*gJ3wCi=3Qhqg4;esBg5xVFdX5_Yrg@%Rf{4?K=2zLa$UDC~>)f%UzdQ zwQqVPGmxxAtX@@hK+?5z5JsR<)|!fniZF+UDdyh?G9229BK0u7^#y*EpMMGXkr%G1 z7J6+v;>J0R&p9$mX(lEoKZ21fEa`htHZynuVT81?R{QtC(T|ULQ9bSL6wJ(VFJIn3 z50zp>=uW`K0*qV{Cc`W|#>+r)|3#+@@r!k{x-I(^>FkEfuE4wxgu|9}FUiO0nqX?E zM=Nu4wj^48w|jtsWz_v)@Y^3?Or2qWdCL`w`remED@sQVZMgnl8a0e+3{g1^gu+uW z7sDNwY3u9Y5-St1s(_~$jh~T!nnOlc_5V*RX!!M;|Je`ufLnt5_qh&|N)IeIC^rpY zmDUZgccsp)ZeqegDY0C21kkA_{RxxA)@T5%dT_FvnVA7lWgAFAJl-FLRvq^CPI?}| z1UK#IwLg>d4(NS~@dx;Q_>d>3z>nVnkGy{is5hga@v#wc3YeYGW@hKDOt!>o6P++5 z0nRO)n3#C4FXtX8JvN-i1HjKWQyxzA`*(T&dZQZY#69O{@N02I8$d5uqX66?Z2eEPE<_H^ysKG8oC#& z&(`TBQMhv_$?cDznm}j=hvFs&FbV@Xarwc62ZvkrQhi%c>3e`jpeS>=prVXnm_UTN`I4uG)iO2!Ybi z1po%Blu?>wXl9lUW%H@5OdR3}E#M|xmI@l}DSCQs0|RBW@8Rx9k#+xP6s2?3$l<}S z4|j{GV2IV$+zjpHXWX%>X7DxqEPi}GUIaxd5#lR6M$q8PFjBbv+Pn*Qpp|Di;JzO zlg@oGYlu{su&dmcLxX((Cq*Z&vWB-yF&ARMAx(dtNk%Dq_dn%56iF^A{q+-&V^6^n zd#Cme6abZ~v9YnKX`1d+k?8x~eSJ8;d3{=y-OgQS<}hvv#KS2nibE2kambrIGCJuLIQ;wF>mO&qp#v+Xz8*}A9B7VX zK<#O0X{lB1?Q@jUzu3h5ajU#RFyr0;dbKKsqYW&tf^>CT+eH8qsH|xrC=`Xim~+bQ zw@UGn3%}X27HBd(EiEluTRut{( z>VP~!4wm<@U}F!wEtd-2uJ^{DqJ(?!nm*FJ4ds6fOW1)))g3hG@a6A^vPuiz5aT z591+5Sc93K3SNX)4_xb)&Q{$EiEnP zLa-%82u9My-y1Z42HM|6i*+mR*AlJ|`K?o7RjhPj2E03A?ySbaNoU0oeEIbi_GmZV?q0kZ*^HvohG?hoIw1<1gq8hl9= zlSmH_1fF2t3e01L7(n(Sn6qhFMKkiE%#Vj=j-Vhua3;webkx*F9xBNl;M9VH-3F5* zV)~RHUn4I7n*ZLm0Zf^0KkfCL$QKYs^nML?echSe(ZQhu5N3CCf>}YRS7)XKzF~-n3U`+s5 z6a{}-34dlPA{*815gd~jPPet;KkQ9)7xHAz?4D&txx#?3ah)ao_wMy&&sd18rka}Z zM$XuBx26HE6M+bp8wy0hZcpq*BSOY_F_&4e7{Cz75inRWo8T+GAjUkmoV8{q=q5Xjfb{L?moeOv>Q19U+4*|6i|f&0fXa2uvF97+A16>AIi|3 zXmpC0K)=2Pd;;I)Y9j0TnHhRmdALl5?E`&NR$kuk29F3#2ox^fXR$giI zU)Rb5W`^f^?`u*Bg5el<=Y5h-dXAeR+p$oW_i!7MR?&z4pV6XXxw6?TeebhP7 z*Vot3I(I`!>0mCp@6^Nxz3anoXhYp!PCR3?w=DcF=6frLV4>JY4*&aF`tZSM zTKW|3WnjVyI(XfXV{LDjYYAWv=`6@EI73n#CQ5fRxYiyk_6A}`F&Ak29CjKqii%p-_h2p^;OOjUnm+^roQj5xiidJ43j`!%(Bgj?5j%@|MwfwLf z+cpW>aPpL@u;LYBVB-;$;MP`n1i8yrzWr`tTf0HU<1U!lu)cwUpH(Q-k+}EyAx(U; z3fHaiy}iAs2Usw{AWK^1k0RzG=UX%v{@jaTzT{%+?QPhQg$?PfyG2=rk@TszEf`=7 zlPxP%YcBMZ{!$tYVFh2p=qT%EFdnjqW>{0-h4+@7vB`qvNnv4)(lLbKd1F(fDMij=0~JUuNzj(w(s$n_ei}h=@Mfu?fSj7?RocI_iT^t zUjMq$qs{p7;rSjtm?XxGu$xOGBV(H0-rlhl;a-cyQ$1OF19klYux0_f z9M&ZsxFycDKmx=(a(y!MBi!P%OF8ovnGMsxeD@7BER2JBnntb(>!*DBWuIV!B<)v| zv9c$uNuc0oMG~BNE*czn_n~2n7$)-y5`Jo9gjo`X@bPKoBv8XHpjZplT4dOi86LPT zlpUZ?O$4>AGWq0^!?8jPii{)Gy}iF+_!A@U?zrn~4qL>te27;3r^(DL9JF@cM6drJ z_Th&eN;l!{L0o~&>B7we+grir6@pEBJfh;Q3$rOo^%dg}APv)EZ=ZtINEQ&*^Ru}$ zcCR`J)c?4$ U9HTt~`}4?+Yf9*>tA_sn3y@n9NdN!< literal 0 HcmV?d00001 diff --git a/core/indigo-core/common/math/algebra.h b/core/indigo-core/common/math/algebra.h index 0c7d2ca0a0..4ad0758735 100644 --- a/core/indigo-core/common/math/algebra.h +++ b/core/indigo-core/common/math/algebra.h @@ -159,6 +159,11 @@ namespace indigo return std::make_pair(x, y) < std::make_pair(a.x, a.y); } + inline float operator*(const Vec2f& a) const + { + return x * a.x + y * a.y; + } + inline Vec2f operator+(const Vec2f& a) const { return Vec2f(x + a.x, y + a.y); @@ -207,6 +212,24 @@ namespace indigo return *this; } + inline float vcos(const Vec2f& a) const + { + float scalar = *this * a; + float ta = length() * a.length(); + if (ta < EPSILON) + ta = EPSILON; + return scalar / ta; + } + + inline float vsin(const Vec2f& a) const + { + float scalar = *this * a; + float ta = lengthSqr() * a.lengthSqr(); + if (ta < EPSILON) + ta = EPSILON; + return sqrt(1 - scalar * scalar / ta); + } + DLLEXPORT bool normalize(); DLLEXPORT bool normalization(const Vec2f& v); diff --git a/core/indigo-core/layout/metalayout.h b/core/indigo-core/layout/metalayout.h index d0a4c45331..f98e8de2fc 100644 --- a/core/indigo-core/layout/metalayout.h +++ b/core/indigo-core/layout/metalayout.h @@ -26,7 +26,7 @@ #include #ifdef _WIN32 -#pragma warning(push) +#pragma warning(push, 4) #pragma warning(disable : 4251) #endif @@ -120,9 +120,9 @@ namespace indigo // utility function to use in MoleculeLayout & ReactionLayout void adjustMol(BaseMolecule& mol, const Vec2f& min, const Vec2f& pos) const; - float reactionComponentMarginSize; + float reactionComponentMarginSize; // in angstrom float verticalIntervalFactor; - float bondLength; + float bondLength; // in angstrom DECL_ERROR; @@ -227,6 +227,30 @@ namespace indigo return input; } } + static float convertPtTo(float pt, UnitsOfMeasure::TYPE unit, int32_t ppi) + { + switch (unit) + { + case UnitsOfMeasure::CM: + return UnitsOfMeasure::convertToCm(pt, UnitsOfMeasure::PT, ppi); + break; + case UnitsOfMeasure::PT: + return pt; + break; + case UnitsOfMeasure::INCH: + return UnitsOfMeasure::convertToInches(pt, UnitsOfMeasure::PT, ppi); + break; + case UnitsOfMeasure::PX: + return UnitsOfMeasure::convertToPx(pt, UnitsOfMeasure::PT, ppi); + break; + } + throw Exception("Unknown unit of measure: %d", unit); + }; + + static float convertToAngstrom(float input, TYPE units, int32_t ppi, float bond_length_px) + { + return convertToPx(input, units, ppi) / bond_length_px; + }; }; struct LayoutOptions @@ -234,19 +258,42 @@ namespace indigo // FIXME: The value is 1.6 instead of 1.0 due to backward compatibility, needs to be refactored static constexpr float DEFAULT_BOND_LENGTH = 1.6f; // default length of inter-chemical bonds static constexpr float DEFAULT_PLUS_SIZE = DEFAULT_BOND_LENGTH / 2; + static constexpr float DEFAULT_BOND_LENGTH_PX = 100.0f; // 100 pixel + static constexpr int32_t DEFAULT_PPI = 72; - float bondLength{DEFAULT_BOND_LENGTH}; + float bondLength{DEFAULT_BOND_LENGTH_PX}; UnitsOfMeasure::TYPE bondLengthUnit{UnitsOfMeasure::TYPE::PX}; - float reactionComponentMarginSize{DEFAULT_BOND_LENGTH / 2}; + float reactionComponentMarginSize{DEFAULT_BOND_LENGTH_PX / 2}; UnitsOfMeasure::TYPE reactionComponentMarginSizeUnit{UnitsOfMeasure::TYPE::PX}; int32_t ppi{72}; + void reset() + { + bondLength = DEFAULT_BOND_LENGTH_PX; + bondLengthUnit = UnitsOfMeasure::TYPE::PX; + reactionComponentMarginSize = DEFAULT_BOND_LENGTH_PX / 2; + reactionComponentMarginSizeUnit = UnitsOfMeasure::TYPE::PX; + ppi = DEFAULT_PPI; + }; + float getBondLengthPx() + { + return UnitsOfMeasure::convertToPx(bondLength, bondLengthUnit, ppi); + }; + void setBondLengthPx(float value) + { + bondLength = UnitsOfMeasure::convertPtTo(UnitsOfMeasure::INCH_TO_PT * value / ppi, bondLengthUnit, ppi); + }; float getMarginSizeInAngstroms() const { auto marginSizePt = UnitsOfMeasure::convertToPt(reactionComponentMarginSize, reactionComponentMarginSizeUnit, ppi); auto bondLengthPt = UnitsOfMeasure::convertToPt(bondLength, bondLengthUnit, ppi); return (DEFAULT_BOND_LENGTH * marginSizePt) / bondLengthPt; - } + }; + void setMarginSizeInAngstroms(float value) + { + float angs_to_pt = UnitsOfMeasure::convertToPt(bondLength, bondLengthUnit, ppi) / DEFAULT_BOND_LENGTH; + reactionComponentMarginSize = UnitsOfMeasure::convertPtTo(value * angs_to_pt, reactionComponentMarginSizeUnit, ppi); + }; }; } // namespace indigo diff --git a/core/indigo-core/layout/reaction_layout.h b/core/indigo-core/layout/reaction_layout.h index 806b7a59f2..5a57fc18c1 100644 --- a/core/indigo-core/layout/reaction_layout.h +++ b/core/indigo-core/layout/reaction_layout.h @@ -76,7 +76,7 @@ namespace indigo ReactionLayout(const ReactionLayout& r); // no implicit copy - const float bond_length; + const float bond_length; // in angstrom const float atom_label_margin; const float default_plus_size; const float default_arrow_size; diff --git a/core/indigo-core/layout/src/metalayout.cpp b/core/indigo-core/layout/src/metalayout.cpp index dc9a72fe77..cefdc8e43c 100644 --- a/core/indigo-core/layout/src/metalayout.cpp +++ b/core/indigo-core/layout/src/metalayout.cpp @@ -20,6 +20,10 @@ #include "base_cpp/tlscont.h" #include "molecule/molecule.h" +#ifdef _WIN32 +#pragma warning(push, 4) +#endif + using namespace indigo; Metalayout::LayoutLine::LayoutLine() @@ -328,3 +332,7 @@ void Metalayout::adjustMol(BaseMolecule& mol, const Vec2f& min, const Vec2f& pos } } } + +#ifdef _WIN32 +#pragma warning(pop) +#endif diff --git a/core/render2d/render.h b/core/render2d/render.h index 6157da9a24..5369674248 100644 --- a/core/render2d/render.h +++ b/core/render2d/render.h @@ -22,13 +22,17 @@ #include "render_internal.h" #include "render_item_factory.h" +#ifdef _WIN32 +#pragma warning(push, 4) +#endif + namespace indigo { class Render { public: - Render(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet); + Render(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength); virtual ~Render() = 0; DECL_ERROR; @@ -50,9 +54,12 @@ namespace indigo const RenderOptions& _opt; RenderItemFactory& _factory; int _bondLength; - bool _bondLengthSet; }; } // namespace indigo +#ifdef _WIN32 +#pragma warning(pop) +#endif + #endif //__render_h__ diff --git a/core/render2d/render_common.h b/core/render2d/render_common.h index 1263168186..37883f66c9 100644 --- a/core/render2d/render_common.h +++ b/core/render2d/render_common.h @@ -40,6 +40,8 @@ namespace indigo class QueryMolecule; class QueryReaction; class Output; + class RenderOptions; + struct AcsOptions; enum DINGO_MODE { @@ -404,7 +406,7 @@ namespace indigo { public: RenderSettings(); - void init(float sf, float lwf); + void init(float relativeThickness, float bondLineWidthFactor, AcsOptions* acs = nullptr); CP_DECL; TL_CP_DECL(Array, bondDashAromatic); @@ -440,6 +442,8 @@ namespace indigo float graphItemDigitHeight; float graphItemSignLineWidth; float graphItemPlusEdge; + float stereoBondSpace; + float hashSpacing; float fzz[FONT_SIZE_COUNT]; @@ -518,7 +522,7 @@ namespace indigo int maxHeight; int xOffset; int yOffset; - float bondLength; + float bondLength; // in pixels UnitsOfMeasure::TYPE bondLengthUnit; int gridMarginX; int gridMarginY; @@ -531,6 +535,8 @@ namespace indigo COMMENT_POS commentPos; MultilineTextLayout commentAlign; MultilineTextLayout titleAlign; + float outputSheetWidth; + float outputSheetHeight; int gridColumnNumber; @@ -542,7 +548,7 @@ namespace indigo { public: RenderOptions(); - void clear(); + void clearRenderOptions(); Vec3f backgroundColor; Vec3f baseColor; @@ -579,14 +585,37 @@ namespace indigo bool agentsBelowArrow; Array atomColorProp; std::unique_ptr cdxml_context; - float reactionComponentMarginSize; - UnitsOfMeasure::TYPE reactionComponentMarginSizeUnit; + // ACS settings + float bond_length_px; int32_t ppi; + float fontSize; + UnitsOfMeasure::TYPE fontSizeUnit; + float fontSizeSub; + UnitsOfMeasure::TYPE fontSizeSubUnit; + float bondThickness; + UnitsOfMeasure::TYPE bondThicknessUnit; + float bondSpacing; + float stereoBondWidth; + UnitsOfMeasure::TYPE stereoBondWidthUnit; + float hashSpacing; + UnitsOfMeasure::TYPE hashSpacingUnit; private: RenderOptions(const RenderOptions&); }; + struct AcsOptions + { + AcsOptions(); + void clear(); + float bondSpacing; + float fontSizeAngstrom; + float fontSizeSubAngstrom; + float bondThicknessAngstrom; + float stereoBondWidthAngstrom; + float hashSpacingAngstrom; + }; + } // namespace indigo #define QUERY_MOL_BEGIN(mol) \ diff --git a/core/render2d/render_context.h b/core/render2d/render_context.h index 4e5d418abd..1e72edeabd 100644 --- a/core/render2d/render_context.h +++ b/core/render2d/render_context.h @@ -46,7 +46,7 @@ namespace indigo void checkPathNonEmpty() const; - RenderContext(const RenderOptions& opt, float sf, float lwf); + RenderContext(const RenderOptions& opt, float relativeThickness, float bondLineWidthFactor); void setDefaultScale(float scale); void setHDC(PVOID hdc); int getMaxPageSize() const; @@ -82,6 +82,7 @@ namespace indigo void fillHex(const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3, const Vec2f& v4, const Vec2f& v5); void fillQuad(const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3); void fillQuadStripes(const Vec2f& v0r, const Vec2f& v0l, const Vec2f& v1r, const Vec2f& v1l, int cnt); + void fillQuadStripesSpacing(const Vec2f& v0r, const Vec2f& v0l, const Vec2f& v1r, const Vec2f& v1l, float spacing); void fillPentagon(const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3, const Vec2f& v4); void drawQuad(const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3); void drawTriangleZigzag(const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, int cnt); diff --git a/core/render2d/render_grid.h b/core/render2d/render_grid.h index d3ba7eb99f..3677ddf031 100644 --- a/core/render2d/render_grid.h +++ b/core/render2d/render_grid.h @@ -27,7 +27,7 @@ namespace indigo class RenderGrid : Render { public: - RenderGrid(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet); + RenderGrid(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength); ~RenderGrid() override; void draw(); diff --git a/core/render2d/render_single.h b/core/render2d/render_single.h index 841e8d5b2c..6925e847a6 100644 --- a/core/render2d/render_single.h +++ b/core/render2d/render_single.h @@ -27,7 +27,7 @@ namespace indigo class RenderSingle : Render { public: - RenderSingle(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet); + RenderSingle(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength); ~RenderSingle() override; void draw(); diff --git a/core/render2d/src/render.cpp b/core/render2d/src/render.cpp index 7222764f85..9d291a6927 100644 --- a/core/render2d/src/render.cpp +++ b/core/render2d/src/render.cpp @@ -33,9 +33,8 @@ using namespace indigo; IMPL_ERROR(Render, "Render"); -Render::Render(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet) - : minMarg(2), _rc(rc), _settings(rc.getRenderSettings()), _cnvOpt(cnvOpt), _opt(rc.opt), _factory(factory), _bondLength(bondLength), - _bondLengthSet(bondLengthSet) +Render::Render(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength) + : minMarg(2), _rc(rc), _settings(rc.getRenderSettings()), _cnvOpt(cnvOpt), _opt(rc.opt), _factory(factory), _bondLength(bondLength) { } @@ -86,7 +85,7 @@ float Render::_getScale(int w, int h) float Render::_getMaxScale(int w, int h) { - float s = (float)(_bondLength > 0 ? _bondLength : 100); + float s = (float)_bondLength > 0 ? _bondLength : LayoutOptions::DEFAULT_BOND_LENGTH_PX; int maxWidth = _getMaxWidth(); int maxHeight = _getMaxHeight(); int defaultWidth = _getDefaultWidth(s); diff --git a/core/render2d/src/render_cdxml.cpp b/core/render2d/src/render_cdxml.cpp index dd4b9e4378..361a32f1d6 100644 --- a/core/render2d/src/render_cdxml.cpp +++ b/core/render2d/src/render_cdxml.cpp @@ -64,7 +64,8 @@ void _getBounds(RenderParams& params, BaseMolecule& mol, Vec2f& min, Vec2f& max, float bond_length = 1; if (params.cnvOpt.bondLength > 0) - bond_length = params.cnvOpt.bondLength / 100.0f; + bond_length = + UnitsOfMeasure::convertToPx(params.cnvOpt.bondLength, params.cnvOpt.bondLengthUnit, params.rOpt.ppi) / LayoutOptions::DEFAULT_BOND_LENGTH_PX; scale = bond_length / avg_bond_length; diff --git a/core/render2d/src/render_common.cpp b/core/render2d/src/render_common.cpp index ca9d8da45d..976470d79c 100644 --- a/core/render2d/src/render_common.cpp +++ b/core/render2d/src/render_common.cpp @@ -19,12 +19,17 @@ #include "render_common.h" #include "base_cpp/array.h" #include "base_cpp/obj_array.h" +#include "layout/molecule_layout.h" #include "math/algebra.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "reaction/query_reaction.h" #include "reaction/reaction.h" +#ifdef _WIN32 +#pragma warning(push, 4) +#endif + using namespace indigo; namespace indigo @@ -282,22 +287,44 @@ RenderSettings::RenderSettings() init(1.0f, 1.0f); } -void RenderSettings::init(float sf, float lwf) +void RenderSettings::init(float relativeThickness, float bondLineWidthFactor, AcsOptions* acs) { - unit = sf / 30; - bondLineWidth = lwf * unit; + unit = relativeThickness / 30; // 1/30 + bondLineWidth = bondLineWidthFactor * unit; bondSpace = 2.5f * unit; + stereoBondSpace = bondSpace; - fzz[FONT_SIZE_LABEL] = unit * 12; - fzz[FONT_SIZE_ATTR] = unit * 8; - fzz[FONT_SIZE_RGROUP_LOGIC] = unit * 12; - fzz[FONT_SIZE_RGROUP_LOGIC_INDEX] = unit * 8; - fzz[FONT_SIZE_INDICES] = unit * 6; - fzz[FONT_SIZE_ATTACHMENT_POINT_INDEX] = unit * 6; - fzz[FONT_SIZE_RSITE_ATTACHMENT_INDEX] = unit * 6; - fzz[FONT_SIZE_COMMENT] = 0; // not used, value taken from RenderOptions.commentFontFactor - fzz[FONT_SIZE_TITLE] = 0; // not used, value taken from RenderOptions.titleFontFactor - fzz[FONT_SIZE_DATA_SGROUP] = unit * 8; + float label_font_size = unit * 12; + if (acs != nullptr) + { + if (acs->bondThicknessAngstrom > 0) + bondLineWidth = acs->bondThicknessAngstrom * bondLineWidthFactor * relativeThickness; + if (acs->fontSizeAngstrom > 0) + label_font_size = acs->fontSizeAngstrom; + if (acs->bondSpacing > 0) + { + bondSpace = acs->bondSpacing / 2.0f; + stereoBondSpace = bondSpace; + } + if (acs->stereoBondWidthAngstrom > 0) + stereoBondSpace = acs->stereoBondWidthAngstrom / 2.0f; + if (acs->hashSpacingAngstrom > 0) + hashSpacing = acs->hashSpacingAngstrom; + } + static constexpr float TWO_DIV_THREE = 2.0f / 3.0f; + fzz[FONT_SIZE_LABEL] = label_font_size; + if (acs != nullptr && acs->fontSizeSubAngstrom > 0) + fzz[FONT_SIZE_ATTR] = acs->fontSizeAngstrom; + else + fzz[FONT_SIZE_ATTR] = label_font_size * TWO_DIV_THREE; // unit * 8; // Subscript + fzz[FONT_SIZE_RGROUP_LOGIC] = label_font_size; + fzz[FONT_SIZE_RGROUP_LOGIC_INDEX] = label_font_size * TWO_DIV_THREE; // unit * 8; + fzz[FONT_SIZE_INDICES] = label_font_size / 2.0f; // unit * 6; + fzz[FONT_SIZE_ATTACHMENT_POINT_INDEX] = label_font_size / 2.0f; // unit * 6; + fzz[FONT_SIZE_RSITE_ATTACHMENT_INDEX] = label_font_size / 2.0f; // unit * 6; + fzz[FONT_SIZE_COMMENT] = 0; // not used, value taken from RenderOptions.commentFontFactor + fzz[FONT_SIZE_TITLE] = 0; // not used, value taken from RenderOptions.titleFontFactor + fzz[FONT_SIZE_DATA_SGROUP] = label_font_size * TWO_DIV_THREE; // unit * 8; upperIndexShift = -0.4f; lowerIndexShift = 0.4f; @@ -384,6 +411,8 @@ void CanvasOptions::clear() comment.clear(); titleProp.clear(); titleProp.appendString("^NAME", true); + outputSheetWidth = -1; + outputSheetHeight = -1; } // @@ -431,3 +460,7 @@ float MultilineTextLayout::getAnchorPoint(float area_x, float area_width, float float bbox_x = area_x + (area_width - text_width) * getBboxRelativeOffset(); return bbox_x + text_width * getInboxRelativeOffset(); } + +#ifdef _WIN32 +#pragma warning(pop) +#endif diff --git a/core/render2d/src/render_context.cpp b/core/render2d/src/render_context.cpp index 354d835ff7..ee5d13d3d3 100644 --- a/core/render2d/src/render_context.cpp +++ b/core/render2d/src/render_context.cpp @@ -119,11 +119,26 @@ void RenderContext::storeAndDestroyMetafile(bool discard) CP_DEF(RenderContext); -RenderContext::RenderContext(const RenderOptions& ropt, float sf, float lwf) +RenderContext::RenderContext(const RenderOptions& ropt, float relativeThickness, float bondLineWidthFactor) : CP_INIT, TL_CP_GET(_fontfamily), TL_CP_GET(transforms), metafileFontsToCurves(false), _cr(NULL), _surface(NULL), _meta_hdc(NULL), opt(ropt), - _pattern(NULL) -{ - _settings.init(sf, lwf); + _pattern(NULL), _settings() +{ + AcsOptions acs; + if (ropt.fontSize > 0) + acs.fontSizeAngstrom = UnitsOfMeasure::convertToPx(ropt.fontSize, ropt.fontSizeUnit, ropt.ppi) / ropt.bond_length_px; + if (ropt.fontSizeSub > 0) + acs.fontSizeSubAngstrom = UnitsOfMeasure::convertToPx(ropt.fontSizeSub, ropt.fontSizeSubUnit, ropt.ppi) / ropt.bond_length_px; + if (ropt.bondThickness > 0) + acs.bondThicknessAngstrom = UnitsOfMeasure::convertToPx(ropt.bondThickness, ropt.bondThicknessUnit, ropt.ppi) / LayoutOptions::DEFAULT_BOND_LENGTH_PX; + if (ropt.stereoBondWidth > 0) + acs.stereoBondWidthAngstrom = + UnitsOfMeasure::convertToPx(ropt.stereoBondWidth, ropt.stereoBondWidthUnit, ropt.ppi) / LayoutOptions::DEFAULT_BOND_LENGTH_PX; + if (ropt.hashSpacing > 0) + acs.hashSpacingAngstrom = UnitsOfMeasure::convertToPx(ropt.hashSpacing, ropt.hashSpacingUnit, ropt.ppi) / LayoutOptions::DEFAULT_BOND_LENGTH_PX; + if (ropt.bondSpacing > 0) + acs.bondSpacing = ropt.bondSpacing; + _settings.init(relativeThickness, bondLineWidthFactor, &acs); + bprintf(_fontfamily, "Arial"); bbmin.x = bbmin.y = 1; bbmax.x = bbmax.y = -1; @@ -642,6 +657,37 @@ void RenderContext::fillQuadStripes(const Vec2f& v0r, const Vec2f& v0l, const Ve cairoCheckStatus(); } +void RenderContext::fillQuadStripesSpacing(const Vec2f& v0r, const Vec2f& v0l, const Vec2f& v1r, const Vec2f& v1l, float spacing) +{ + Vec2f r(v0r), dr; + Vec2f l(v0l), dl; + Vec2f v; + dr.diff(v1r, v0r); + dl.diff(v1l, v0l); + v.diff(v1l, v1r); + float dr_len = dr.lengthSqr(); + float dl_len = dl.lengthSqr(); + + dr.normalize(); + dr.scale(std::fabs(dr.vsin(v)) * spacing); + dl.normalize(); + dl.scale(std::fabs(dl.vsin(v)) * spacing); + + while (true) + { + r.add(dr); + l.add(dl); + if (Vec2f::distSqr(v0r, r) > dr_len || Vec2f::distSqr(v0l, l) > dl_len) + break; + moveTo(r); + lineTo(l); + } + checkPathNonEmpty(); + bbIncludePath(true); + cairo_stroke(_cr); + cairoCheckStatus(); +} + void RenderContext::fillPentagon(const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3, const Vec2f& v4) { moveTo(v0); diff --git a/core/render2d/src/render_grid.cpp b/core/render2d/src/render_grid.cpp index 98c0a08503..129e8bb58b 100644 --- a/core/render2d/src/render_grid.cpp +++ b/core/render2d/src/render_grid.cpp @@ -31,8 +31,8 @@ using namespace indigo; IMPL_ERROR(RenderGrid, "RenderGrid"); -RenderGrid::RenderGrid(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet) - : Render(rc, factory, cnvOpt, bondLength, bondLengthSet), nColumns(cnvOpt.gridColumnNumber), comment(-1) +RenderGrid::RenderGrid(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength) + : Render(rc, factory, cnvOpt, bondLength), nColumns(cnvOpt.gridColumnNumber), comment(-1) { } diff --git a/core/render2d/src/render_internal.cpp b/core/render2d/src/render_internal.cpp index 424732885a..671d16ca88 100644 --- a/core/render2d/src/render_internal.cpp +++ b/core/render2d/src/render_internal.cpp @@ -29,6 +29,10 @@ #include "reaction/reaction.h" #include "render_context.h" +#ifdef _WIN32 +#pragma warning(push, 4) +#endif + using namespace indigo; #define BOND_STEREO_BOLD 10001 @@ -148,10 +152,10 @@ static bool _isBondWide(const BondDescr& bd) RenderOptions::RenderOptions() { - clear(); + clearRenderOptions(); } -void RenderOptions::clear() +void RenderOptions::clearRenderOptions() { baseColor.set(0, 0, 0); backgroundColor.set(-1, -1, -1); @@ -187,6 +191,33 @@ void RenderOptions::clear() showCycles = false; agentsBelowArrow = true; atomColorProp.clear(); + ppi = LayoutOptions::DEFAULT_PPI; + fontSize = -1; + fontSizeUnit = UnitsOfMeasure::PT; + fontSizeSub = -1; + fontSizeSubUnit = UnitsOfMeasure::PT; + bondThickness = -1; + bondThicknessUnit = UnitsOfMeasure::PT; + bondSpacing = -1; + stereoBondWidth = -1; + stereoBondWidthUnit = UnitsOfMeasure::PT; + hashSpacing = -1; + hashSpacingUnit = UnitsOfMeasure::PT; +} + +AcsOptions::AcsOptions() +{ + clear(); +} + +void AcsOptions::clear() +{ + bondSpacing = -1; + fontSizeAngstrom = -1; + fontSizeSubAngstrom = -1; + bondThicknessAngstrom = -1; + stereoBondWidthAngstrom = -1; + hashSpacingAngstrom = -1; } IMPL_ERROR(MoleculeRenderInternal, "molecule render internal"); @@ -3859,7 +3890,7 @@ void MoleculeRenderInternal::_adjustAngle(Vec2f& l, const BondEnd& be1, const Bo const Vec2f& p1 = _ad(be1.aid).pos; const Vec2f& p2 = _ad(be2.aid).pos; const double len = Vec2f::dist(p1, p2); - double w = _settings.bondSpace; + double w = _settings.stereoBondSpace; double tgb = w / len; double csb = sqrt(1 / (1 + tgb * tgb)); double snb = tgb * csb; @@ -3878,7 +3909,7 @@ void MoleculeRenderInternal::_adjustAngle(Vec2f& l, const BondEnd& be1, const Bo void MoleculeRenderInternal::_bondBoldStereo(BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { Vec2f r0(be1.p), l0(be1.p), r1(be2.p), l1(be2.p); - float w = _settings.bondSpace; + float w = _settings.stereoBondSpace; l0.addScaled(bd.norm, -w); r0.addScaled(bd.norm, w); l1.addScaled(bd.norm, -w); @@ -3944,23 +3975,27 @@ void MoleculeRenderInternal::_bondSingle(BondDescr& bd, const BondEnd& be1, cons _bondBoldStereo(bd, be1, be2); return; } + + float lw = _cw.currentLineWidth(); + if (bd.stereodir == 0) + { + _cw.drawLine(be1.p, be2.p); + bd.extP = bd.extN = lw / 2; + return; + } + + // stereo bonds Vec2f l(be2.p), r(be2.p); - float w = _settings.bondSpace; + float w = _settings.stereoBondSpace; l.addScaled(bd.norm, -w); r.addScaled(bd.norm, w); bd.extP = bd.extN = w; - float lw = _cw.currentLineWidth(); Vec2f r0(be1.p), l0(be1.p); l0.addScaled(bd.norm, -lw / 2); r0.addScaled(bd.norm, lw / 2); - if (bd.stereodir == 0) - { - _cw.drawLine(be1.p, be2.p); - bd.extP = bd.extN = lw / 2; - } - else if (bd.stereodir == BOND_UP) + if (bd.stereodir == BOND_UP) { if (_ad(be2.aid).showLabel == false && !bd.isShort) { @@ -3975,8 +4010,16 @@ void MoleculeRenderInternal::_bondSingle(BondDescr& bd, const BondEnd& be1, cons } else if (bd.stereodir == BOND_DOWN) { - int stripeCnt = std::max((int)((len) / lw / 2), 4); - _cw.fillQuadStripes(r0, l0, r, l, stripeCnt); + int constexpr min_count = 4; + if (_settings.hashSpacing > 0 && (int)(len / _settings.hashSpacing) > min_count) + { + _cw.fillQuadStripesSpacing(r0, l0, r, l, _settings.hashSpacing); + } + else + { + int stripeCnt = std::max((int)((len) / lw / 2), min_count); + _cw.fillQuadStripes(r0, l0, r, l, stripeCnt); + } } else if (bd.stereodir == BOND_EITHER) { @@ -4334,3 +4377,7 @@ void MoleculeRenderInternal::_precalcScale() } _scale = std::max(_scale, float(max_output_length) / ((float)10.0 * scale_modificator)); } + +#ifdef _WIN32 +#pragma warning(pop) +#endif diff --git a/core/render2d/src/render_params.cpp b/core/render2d/src/render_params.cpp index 3d757aa436..7c31156d07 100644 --- a/core/render2d/src/render_params.cpp +++ b/core/render2d/src/render_params.cpp @@ -69,7 +69,7 @@ void RenderParams::clear() relativeThickness = 1.0f; bondLineWidthFactor = 1.0f; rmode = RENDER_NONE; - rOpt.clear(); + rOpt.clearRenderOptions(); cnvOpt.clear(); clearArrays(); } @@ -189,9 +189,9 @@ void RenderParamInterface::render(RenderParams& params) RenderContext rc(params.rOpt, params.relativeThickness, params.bondLineWidthFactor); - bool bondLengthSet = params.cnvOpt.bondLength > 0; - int bondLength = (int)(bondLengthSet ? params.cnvOpt.bondLength : 100); - rc.setDefaultScale((float)bondLength); // TODO: fix bondLength type + int bondLength_px = (int)(params.rOpt.bond_length_px > EPSILON ? params.rOpt.bond_length_px : LayoutOptions::DEFAULT_BOND_LENGTH_PX); + + rc.setDefaultScale((float)bondLength_px); // TODO: fix bondLength type RenderItemFactory factory(rc); int obj = -1; @@ -274,14 +274,14 @@ void RenderParamInterface::render(RenderParams& params) if (obj >= 0) { - RenderSingle render(rc, factory, params.cnvOpt, bondLength, bondLengthSet); + RenderSingle render(rc, factory, params.cnvOpt, bondLength_px); render.obj = obj; render.comment = comment; render.draw(); } else { - RenderGrid render(rc, factory, params.cnvOpt, bondLength, bondLengthSet); + RenderGrid render(rc, factory, params.cnvOpt, bondLength_px); render.objs.copy(objs); render.comment = comment; render.titles.copy(titles); diff --git a/core/render2d/src/render_single.cpp b/core/render2d/src/render_single.cpp index 6181b655cb..4881842ea4 100644 --- a/core/render2d/src/render_single.cpp +++ b/core/render2d/src/render_single.cpp @@ -35,8 +35,8 @@ using namespace indigo; IMPL_ERROR(RenderSingle, "RenderSingle"); -RenderSingle::RenderSingle(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet) - : comment(-1), Render(rc, factory, cnvOpt, bondLength, bondLengthSet) +RenderSingle::RenderSingle(RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength) + : comment(-1), Render(rc, factory, cnvOpt, bondLength) { }