diff --git a/api/c/indigo-renderer/src/indigo_render2d.cpp b/api/c/indigo-renderer/src/indigo_render2d.cpp index 1a08d0d727..380df53382 100644 --- a/api/c/indigo-renderer/src/indigo_render2d.cpp +++ b/api/c/indigo-renderer/src/indigo_render2d.cpp @@ -186,54 +186,6 @@ void indigoRenderGetStereoStyle(Array& value) } } -void indigoRenderSetLabelMode(const char* mode) -{ - std::string mode_string(mode); - LABEL_MODE result; - if (mode_string == "none") - { - result = LABEL_MODE_NONE; - } - else if (mode_string == "hetero") - { - result = LABEL_MODE_HETERO; - } - else if (mode_string == "terminal-hetero") - { - result = LABEL_MODE_TERMINAL_HETERO; - } - else if (mode_string == "all") - { - result = LABEL_MODE_ALL; - } - else - { - throw IndigoError("Invalid label mode, should be 'none', 'hetero', 'terminal-hetero' or 'all'"); - } - RenderParams& rp = indigoRendererGetInstance().renderParams; - rp.rOpt.labelMode = result; -} - -void indigoRenderGetLabelMode(Array& value) -{ - RenderParams& rp = indigoRendererGetInstance().renderParams; - switch (rp.rOpt.labelMode) - { - case LABEL_MODE_NONE: - value.readString("none", true); - break; - case LABEL_MODE_HETERO: - value.readString("hetero", true); - break; - case LABEL_MODE_TERMINAL_HETERO: - value.readString("terminal-hetero", true); - break; - case LABEL_MODE_ALL: - value.readString("all", true); - break; - } -} - void indigoRenderSetCatalystsPlacement(const char* mode) { int result; @@ -454,6 +406,11 @@ 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.fontSize = layout_options.fontSize; + rp.rOpt.fontSizeUnit = layout_options.fontSizeUnit; + rp.rOpt.fontSizeSub = layout_options.fontSizeSub; + rp.rOpt.fontSizeSubUnit = layout_options.fontSizeSubUnit; + rp.rOpt.labelMode = layout_options.labelMode; rp.rOpt.bond_length_px = layout_options.bondLength > EPSILON ? layout_options.getBondLengthPx() : LayoutOptions::DEFAULT_BOND_LENGTH_PX; if (rp.cnvOpt.outputSheetWidth > 0) { @@ -732,7 +689,6 @@ void IndigoRenderer::setOptionsHandlers() mgr->setOptionHandlerString("render-output-format", indigoRenderSetOutputFormat, indigoRenderGetOutputFormat); - mgr->setOptionHandlerString("render-label-mode", indigoRenderSetLabelMode, indigoRenderGetLabelMode); mgr->setOptionHandlerString("render-comment", SETTER_GETTER_STR_OPTION(rp.cnvOpt.comment)); mgr->setOptionHandlerString("render-comment-position", indigoRenderSetCommentPosition, indigoRenderGetCommentPosition); mgr->setOptionHandlerString("render-stereo-style", indigoRenderSetStereoStyle, indigoRenderGetStereoStyle); @@ -790,10 +746,6 @@ void IndigoRenderer::setOptionsHandlers() 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)); diff --git a/api/c/indigo/src/indigo_options.cpp b/api/c/indigo/src/indigo_options.cpp index 216b06243a..3f48b9f006 100644 --- a/api/c/indigo/src/indigo_options.cpp +++ b/api/c/indigo/src/indigo_options.cpp @@ -262,6 +262,54 @@ void indigoProductEnumeratorGetOneTubeMode(Array& value) value.readString("grid", true); } +void indigoRenderSetLabelMode(const char* mode) +{ + std::string mode_string(mode); + LABEL_MODE result; + if (mode_string == "none") + { + result = LABEL_MODE_NONE; + } + else if (mode_string == "hetero") + { + result = LABEL_MODE_HETERO; + } + else if (mode_string == "terminal-hetero") + { + result = LABEL_MODE_TERMINAL_HETERO; + } + else if (mode_string == "all") + { + result = LABEL_MODE_ALL; + } + else + { + throw IndigoError("Invalid label mode, should be 'none', 'hetero', 'terminal-hetero' or 'all'"); + } + LayoutOptions& layout_options = indigoGetInstance().layout_options; + layout_options.labelMode = result; +} + +void indigoRenderGetLabelMode(Array& value) +{ + LayoutOptions& layout_options = indigoGetInstance().layout_options; + switch (layout_options.labelMode) + { + case LABEL_MODE_NONE: + value.readString("none", true); + break; + case LABEL_MODE_HETERO: + value.readString("hetero", true); + break; + case LABEL_MODE_TERMINAL_HETERO: + value.readString("terminal-hetero", true); + break; + case LABEL_MODE_ALL: + value.readString("all", true); + break; + } +} + void IndigoOptionHandlerSetter::setBasicOptionHandlers(const qword id) { auto mgr = sf::xlock_safe_ptr(indigoGetOptionManager(id)); @@ -391,4 +439,9 @@ void IndigoOptionHandlerSetter::setBasicOptionHandlers(const qword id) mgr->setOptionHandlerFloat("reaction-component-margin-size", SETTER_GETTER_FLOAT_OPTION(indigo.layout_options.reactionComponentMarginSize)); 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")); + mgr->setOptionHandlerFloat("render-font-size", SETTER_GETTER_FLOAT_OPTION(indigo.layout_options.fontSize)); + mgr->setOptionHandlerString("render-font-size-unit", SETTER_GETTER_UNIT_OPTION(indigo.layout_options.fontSizeUnit)); + mgr->setOptionHandlerFloat("render-font-size-sub", SETTER_GETTER_FLOAT_OPTION(indigo.layout_options.fontSizeSub)); + mgr->setOptionHandlerString("render-font-size-sub-unit", SETTER_GETTER_UNIT_OPTION(indigo.layout_options.fontSizeSubUnit)); + mgr->setOptionHandlerString("render-label-mode", indigoRenderSetLabelMode, indigoRenderGetLabelMode); } \ No newline at end of file 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 cecbd70937..eee7b07729 100644 --- a/api/tests/integration/ref/layout/acs_style_reaction.py.out +++ b/api/tests/integration/ref/layout/acs_style_reaction.py.out @@ -16,3 +16,6 @@ *** 2389 wrong margin *** acs_issue_2389.ket:SUCCEED + +*** 2458 catalist margins margin *** +acs_issue_2458.ket:SUCCEED diff --git a/api/tests/integration/tests/layout/acs_style_reaction.py b/api/tests/integration/tests/layout/acs_style_reaction.py index 251027a434..b5b1e6a392 100644 --- a/api/tests/integration/tests/layout/acs_style_reaction.py +++ b/api/tests/integration/tests/layout/acs_style_reaction.py @@ -75,3 +75,32 @@ def find_diff(a, b): else: print(filename + ":FAILED") print(diff) + + +print("\n*** 2458 catalist margins margin ***") +filename = "acs_issue_2458.ket" +rxn = indigo.loadReactionFromFile(os.path.join(root, filename)) +indigo.resetOptions() +indigo.setOption("molfile-saving-skip-date", "1") +indigo.setOption("json-saving-pretty", "1") +indigo.setOption("json-use-native-precision", "1") +indigo.setOption("smart-layout", "true") +indigo.setOption("dearomatize-on-load", "false") +indigo.setOption("render-font-size", "10") +indigo.setOption("render-font-size-unit", "pt") +indigo.setOption("image-resolution", "600") +indigo.setOption("bond-length-unit", "pt") +indigo.setOption("bond-length", "14.4") +indigo.setOption("render-label-mode", "terminal-hetero") +rxn.layout() +# with open(os.path.join(ref, filename), "w") as file: +# file.write(rxn.json()) +with open(os.path.join(ref, filename), "r") as file: + ket_ref = file.read() +ket = rxn.json() +diff = find_diff(ket_ref, ket) +if not diff: + print(filename + ":SUCCEED") +else: + print(filename + ":FAILED") + print(diff) diff --git a/api/tests/integration/tests/layout/reactions/acs_issue_2458.ket b/api/tests/integration/tests/layout/reactions/acs_issue_2458.ket new file mode 100644 index 0000000000..c50c942501 --- /dev/null +++ b/api/tests/integration/tests/layout/reactions/acs_issue_2458.ket @@ -0,0 +1,547 @@ +{ + "root": { + "nodes": [ + { + "$ref": "mol0" + }, + { + "$ref": "mol1" + }, + { + "$ref": "mol2" + }, + { + "$ref": "mol3" + }, + { + "$ref": "mol4" + }, + { + "$ref": "mol5" + }, + { + "$ref": "mol6" + }, + { + "$ref": "mol7" + }, + { + "$ref": "mol8" + }, + { + "type": "arrow", + "data": { + "mode": "open-angle", + "pos": [ + { + "x": 21.026647328887037, + "y": -8.70290088292825, + "z": 0 + }, + { + "x": 27.75164732888704, + "y": -12.552900882928249, + "z": 0 + } + ] + } + } + ], + "connections": [], + "templates": [] + }, + "mol0": { + "type": "molecule", + "atoms": [ + { + "label": "C", + "location": [ + 13.126654540369064, + -5.303906235908338, + 0 + ] + }, + { + "label": "C", + "location": [ + 12.126658661215936, + -5.303906235908338, + 0 + ] + }, + { + "label": "C", + "location": [ + 11.626660721639372, + -6.169902667254945, + 0 + ] + }, + { + "label": "C", + "location": [ + 12.126558661628021, + -7.035899098601554, + 0 + ] + }, + { + "label": "C", + "location": [ + 13.126554540781148, + -7.035899098601554, + 0 + ] + }, + { + "label": "C", + "location": [ + 13.626652479945628, + -6.16980266766703, + 0 + ] + }, + { + "label": "C", + "location": [ + 14.626648359098755, + -6.16980266766703, + 0 + ] + }, + { + "label": "C", + "location": [ + 15.12664629867532, + -7.035899098601554, + 0 + ] + }, + { + "label": "C", + "location": [ + 16.126642177828447, + -7.035899098601554, + 0 + ] + }, + { + "label": "C", + "location": [ + 16.62664011740501, + -7.9018955299481615, + 0 + ] + }, + { + "label": "C", + "location": [ + 17.626635996558136, + -7.9018955299481615, + 0 + ] + }, + { + "label": "C", + "location": [ + 18.126633936134702, + -7.035899098601554, + 0 + ] + }, + { + "label": "C", + "location": [ + 17.626635996558136, + -6.16980266766703, + 0 + ] + }, + { + "label": "C", + "location": [ + 16.62664011740501, + -6.16980266766703, + 0 + ] + } + ], + "bonds": [ + { + "type": 1, + "atoms": [ + 0, + 1 + ] + }, + { + "type": 2, + "atoms": [ + 0, + 5 + ] + }, + { + "type": 2, + "atoms": [ + 1, + 2 + ] + }, + { + "type": 1, + "atoms": [ + 2, + 3 + ] + }, + { + "type": 2, + "atoms": [ + 3, + 4 + ] + }, + { + "type": 1, + "atoms": [ + 4, + 5 + ] + }, + { + "type": 1, + "atoms": [ + 5, + 6 + ] + }, + { + "type": 2, + "atoms": [ + 6, + 7 + ] + }, + { + "type": 1, + "atoms": [ + 7, + 8 + ] + }, + { + "type": 1, + "atoms": [ + 13, + 8 + ] + }, + { + "type": 2, + "atoms": [ + 9, + 8 + ] + }, + { + "type": 1, + "atoms": [ + 9, + 10 + ] + }, + { + "type": 2, + "atoms": [ + 10, + 11 + ] + }, + { + "type": 1, + "atoms": [ + 11, + 12 + ] + }, + { + "type": 2, + "atoms": [ + 12, + 13 + ] + } + ] + }, + "mol1": { + "type": "molecule", + "atoms": [ + { + "label": "P", + "location": [ + 25.301647328887036, + -7.75290088292825, + 0 + ] + } + ] + }, + "mol2": { + "type": "molecule", + "atoms": [ + { + "label": "P", + "location": [ + 28.276647328887037, + -10.50290088292825, + 0 + ] + } + ] + }, + "mol3": { + "type": "molecule", + "atoms": [ + { + "label": "O", + "location": [ + 18.976647328887037, + -11.32790088292825, + 0 + ] + } + ] + }, + "mol4": { + "type": "molecule", + "atoms": [ + { + "label": "O", + "location": [ + 24.826647328887038, + -14.80290088292825, + 0 + ] + } + ] + }, + "mol5": { + "type": "molecule", + "atoms": [ + { + "label": "O", + "location": [ + 21.926647328887036, + -12.87790088292825, + 0 + ] + } + ] + }, + "mol6": { + "type": "molecule", + "atoms": [ + { + "label": "N", + "location": [ + 32.303405381214695, + -15.774058314166735, + 0 + ] + }, + { + "label": "C", + "location": [ + 33.162859719951705, + -15.484780338347665, + 0 + ] + }, + { + "label": "C", + "location": [ + 31.708697468710085, + -14.908167479746094, + 0 + ] + }, + { + "label": "C", + "location": [ + 33.162859719951705, + -14.392519165166847, + 0 + ] + }, + { + "label": "N", + "location": [ + 34.0241357084105, + -15.997392570057276, + 0 + ] + }, + { + "label": "N", + "location": [ + 32.316399815896744, + -14.094618713998008, + 0 + ] + }, + { + "label": "C", + "location": [ + 34.00239735506389, + -13.903224049896053, + 0 + ] + }, + { + "label": "C", + "location": [ + 34.91200778280731, + -15.490852504086941, + 0 + ] + }, + { + "label": "N", + "location": [ + 34.91200778280731, + -14.4364816451192, + 0 + ] + }, + { + "label": "O", + "location": [ + 34.00239735506389, + -12.942607429942722, + 0 + ] + }, + { + "label": "N", + "location": [ + 35.75130253128992, + -15.981240609190804, + 0 + ] + } + ], + "bonds": [ + { + "type": 1, + "atoms": [ + 0, + 1 + ] + }, + { + "type": 1, + "atoms": [ + 0, + 2 + ] + }, + { + "type": 2, + "atoms": [ + 1, + 3 + ] + }, + { + "type": 1, + "atoms": [ + 1, + 4 + ] + }, + { + "type": 2, + "atoms": [ + 2, + 5 + ] + }, + { + "type": 1, + "atoms": [ + 3, + 6 + ] + }, + { + "type": 2, + "atoms": [ + 4, + 7 + ] + }, + { + "type": 1, + "atoms": [ + 6, + 8 + ] + }, + { + "type": 2, + "atoms": [ + 6, + 9 + ] + }, + { + "type": 1, + "atoms": [ + 3, + 5 + ] + }, + { + "type": 1, + "atoms": [ + 7, + 8 + ] + }, + { + "type": 1, + "atoms": [ + 7, + 10 + ] + } + ] + }, + "mol7": { + "type": "molecule", + "atoms": [ + { + "label": "P", + "location": [ + 28.755000000000003, + -7.445, + 0 + ] + } + ] + }, + "mol8": { + "type": "molecule", + "atoms": [ + { + "label": "O", + "location": [ + 18.755000000000003, + -14.945, + 0 + ] + } + ] + } +} \ No newline at end of file diff --git a/api/tests/integration/tests/layout/ref/acs_issue_2458.ket b/api/tests/integration/tests/layout/ref/acs_issue_2458.ket new file mode 100644 index 0000000000..89239e4592 --- /dev/null +++ b/api/tests/integration/tests/layout/ref/acs_issue_2458.ket @@ -0,0 +1,560 @@ +{ + "root": { + "nodes": [ + { + "$ref": "mol0" + }, + { + "$ref": "mol1" + }, + { + "$ref": "mol2" + }, + { + "$ref": "mol3" + }, + { + "$ref": "mol4" + }, + { + "$ref": "mol5" + }, + { + "$ref": "mol6" + }, + { + "$ref": "mol7" + }, + { + "$ref": "mol8" + }, + { + "type": "plus", + "location": [ + 7.316667, + 0.0, + 0.0 + ] + }, + { + "type": "arrow", + "data": { + "mode": "open-angle", + "pos": [ + { + "x": 9.8, + "y": 0.0, + "z": 0.0 + }, + { + "x": 20.216667, + "y": 0.0, + "z": 0.0 + } + ] + } + } + ] + }, + "mol0": { + "type": "molecule", + "atoms": [ + { + "label": "C", + "location": [ + 1.499999, + -1.299038, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 0.499999, + -1.299038, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 0.0, + -0.433012, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 0.5, + 0.433014, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 1.5, + 0.433013, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 2.0, + -0.433013, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 3.0, + -0.433013, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 3.5, + 0.433012, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 4.5, + 0.433012, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 5.0, + -0.433013, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 6.0, + -0.433013, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 6.5, + 0.433012, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 6.0, + 1.299038, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 5.0, + 1.299038, + 0.0 + ] + } + ], + "bonds": [ + { + "type": 1, + "atoms": [ + 0, + 1 + ] + }, + { + "type": 2, + "atoms": [ + 0, + 5 + ] + }, + { + "type": 2, + "atoms": [ + 1, + 2 + ] + }, + { + "type": 1, + "atoms": [ + 2, + 3 + ] + }, + { + "type": 2, + "atoms": [ + 3, + 4 + ] + }, + { + "type": 1, + "atoms": [ + 4, + 5 + ] + }, + { + "type": 1, + "atoms": [ + 5, + 6 + ] + }, + { + "type": 2, + "atoms": [ + 6, + 7 + ] + }, + { + "type": 1, + "atoms": [ + 7, + 8 + ] + }, + { + "type": 1, + "atoms": [ + 13, + 8 + ] + }, + { + "type": 2, + "atoms": [ + 9, + 8 + ] + }, + { + "type": 1, + "atoms": [ + 9, + 10 + ] + }, + { + "type": 2, + "atoms": [ + 10, + 11 + ] + }, + { + "type": 1, + "atoms": [ + 11, + 12 + ] + }, + { + "type": 2, + "atoms": [ + 12, + 13 + ] + } + ] + }, + "mol1": { + "type": "molecule", + "atoms": [ + { + "label": "P", + "location": [ + 10.425, + 1.333333, + 0.0 + ] + } + ], + "bonds": [] + }, + "mol2": { + "type": "molecule", + "atoms": [ + { + "label": "P", + "location": [ + 12.091667, + 1.333333, + 0.0 + ] + } + ], + "bonds": [] + }, + "mol3": { + "type": "molecule", + "atoms": [ + { + "label": "O", + "location": [ + 9.175, + 0.0, + 0.0 + ] + } + ], + "bonds": [] + }, + "mol4": { + "type": "molecule", + "atoms": [ + { + "label": "O", + "location": [ + 14.591668, + 1.333333, + 0.0 + ] + } + ], + "bonds": [] + }, + "mol5": { + "type": "molecule", + "atoms": [ + { + "label": "O", + "location": [ + 16.258335, + 1.333333, + 0.0 + ] + } + ], + "bonds": [] + }, + "mol6": { + "type": "molecule", + "atoms": [ + { + "label": "N", + "location": [ + 21.258333, + 1.309017, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 22.209391, + 1.0, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 20.670547, + 0.5, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 22.209391, + -0.0, + 0.0 + ] + }, + { + "label": "N", + "location": [ + 23.075417, + 1.5, + 0.0 + ] + }, + { + "label": "N", + "location": [ + 21.258333, + -0.309017, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 23.075417, + -0.5, + 0.0 + ] + }, + { + "label": "C", + "location": [ + 23.941441, + 1.0, + 0.0 + ] + }, + { + "label": "N", + "location": [ + 23.941441, + 0.0, + 0.0 + ] + }, + { + "label": "O", + "location": [ + 23.075415, + -1.5, + 0.0 + ] + }, + { + "label": "N", + "location": [ + 24.807467, + 1.5, + 0.0 + ] + } + ], + "bonds": [ + { + "type": 1, + "atoms": [ + 0, + 1 + ] + }, + { + "type": 1, + "atoms": [ + 0, + 2 + ] + }, + { + "type": 2, + "atoms": [ + 1, + 3 + ] + }, + { + "type": 1, + "atoms": [ + 1, + 4 + ] + }, + { + "type": 2, + "atoms": [ + 2, + 5 + ] + }, + { + "type": 1, + "atoms": [ + 3, + 6 + ] + }, + { + "type": 2, + "atoms": [ + 4, + 7 + ] + }, + { + "type": 1, + "atoms": [ + 6, + 8 + ] + }, + { + "type": 2, + "atoms": [ + 6, + 9 + ] + }, + { + "type": 1, + "atoms": [ + 3, + 5 + ] + }, + { + "type": 1, + "atoms": [ + 7, + 8 + ] + }, + { + "type": 1, + "atoms": [ + 7, + 10 + ] + } + ] + }, + "mol7": { + "type": "molecule", + "atoms": [ + { + "label": "P", + "location": [ + 17.091669, + 1.333333, + 0.0 + ] + } + ], + "bonds": [] + }, + "mol8": { + "type": "molecule", + "atoms": [ + { + "label": "O", + "location": [ + 19.591667, + 1.333333, + 0.0 + ] + } + ], + "bonds": [] + } +} \ No newline at end of file diff --git a/core/indigo-core/layout/metalayout.h b/core/indigo-core/layout/metalayout.h index f98e8de2fc..a0422c6e63 100644 --- a/core/indigo-core/layout/metalayout.h +++ b/core/indigo-core/layout/metalayout.h @@ -23,6 +23,7 @@ #include "base_cpp/reusable_obj_array.h" #include "math/algebra.h" +#include "molecule/elements.h" #include #ifdef _WIN32 @@ -259,6 +260,7 @@ namespace indigo 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 float DEFAULT_FONT_SIZE_PX = DEFAULT_BOND_LENGTH_PX * 0.4f; static constexpr int32_t DEFAULT_PPI = 72; float bondLength{DEFAULT_BOND_LENGTH_PX}; @@ -266,6 +268,12 @@ namespace indigo float reactionComponentMarginSize{DEFAULT_BOND_LENGTH_PX / 2}; UnitsOfMeasure::TYPE reactionComponentMarginSizeUnit{UnitsOfMeasure::TYPE::PX}; int32_t ppi{72}; + float fontSize{-1}; + UnitsOfMeasure::TYPE fontSizeUnit{UnitsOfMeasure::PT}; + float fontSizeSub{-1}; + UnitsOfMeasure::TYPE fontSizeSubUnit{UnitsOfMeasure::PT}; + LABEL_MODE labelMode{LABEL_MODE_TERMINAL_HETERO}; + void reset() { bondLength = DEFAULT_BOND_LENGTH_PX; @@ -273,27 +281,44 @@ namespace indigo reactionComponentMarginSize = DEFAULT_BOND_LENGTH_PX / 2; reactionComponentMarginSizeUnit = UnitsOfMeasure::TYPE::PX; ppi = DEFAULT_PPI; + fontSize = -1; + fontSizeUnit = UnitsOfMeasure::PT; + fontSizeSub = -1; + fontSizeSubUnit = UnitsOfMeasure::PT; + labelMode = LABEL_MODE_TERMINAL_HETERO; }; + 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; + return 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); }; + + float getFontSizeInAngstroms() const + { + auto fontSizePt = UnitsOfMeasure::convertToPt(fontSize, fontSizeUnit, ppi); + auto bondLengthPt = UnitsOfMeasure::convertToPt(bondLength, bondLengthUnit, ppi); + + return fontSizePt / bondLengthPt; + }; }; } // namespace indigo diff --git a/core/indigo-core/layout/reaction_layout.h b/core/indigo-core/layout/reaction_layout.h index 94b4ba80de..858471e2b0 100644 --- a/core/indigo-core/layout/reaction_layout.h +++ b/core/indigo-core/layout/reaction_layout.h @@ -88,6 +88,7 @@ namespace indigo LayoutOptions _options; BaseReaction& _r; Metalayout _ml; + float _font_size; }; } // namespace indigo diff --git a/core/indigo-core/layout/src/reaction_layout.cpp b/core/indigo-core/layout/src/reaction_layout.cpp index 28b9680bbe..229526bf88 100644 --- a/core/indigo-core/layout/src/reaction_layout.cpp +++ b/core/indigo-core/layout/src/reaction_layout.cpp @@ -37,17 +37,19 @@ using namespace indigo; ReactionLayout::ReactionLayout(BaseReaction& r, bool smart_layout) : bond_length(LayoutOptions::DEFAULT_BOND_LENGTH), default_plus_size(1), default_arrow_size(2), preserve_molecule_layout(false), _r(r), _smart_layout(smart_layout), reaction_margin_size(DEFAULT_HOR_INTERVAL_FACTOR), atom_label_margin(1.3f), layout_orientation(UNCPECIFIED), - max_iterations(0) + max_iterations(0), _font_size(-1) { _options.bondLength = bond_length; _options.reactionComponentMarginSize = reaction_margin_size; } ReactionLayout::ReactionLayout(BaseReaction& r, bool smart_layout, const LayoutOptions& options) - : bond_length(LayoutOptions::DEFAULT_BOND_LENGTH), default_plus_size(LayoutOptions::DEFAULT_PLUS_SIZE), + : bond_length(options.fontSize > EPSILON ? 1 : LayoutOptions::DEFAULT_BOND_LENGTH), default_plus_size(LayoutOptions::DEFAULT_PLUS_SIZE), default_arrow_size(LayoutOptions::DEFAULT_BOND_LENGTH * 2), preserve_molecule_layout(false), _r(r), _smart_layout(smart_layout), - reaction_margin_size(options.getMarginSizeInAngstroms()), atom_label_margin(LayoutOptions::DEFAULT_BOND_LENGTH / 2), layout_orientation(UNCPECIFIED), - max_iterations(0), _options(options) + reaction_margin_size(options.fontSize > EPSILON ? options.getMarginSizeInAngstroms() + : LayoutOptions::DEFAULT_BOND_LENGTH * options.getMarginSizeInAngstroms()), + atom_label_margin(LayoutOptions::DEFAULT_BOND_LENGTH / 2), layout_orientation(UNCPECIFIED), max_iterations(0), _options(options), + _font_size(options.getFontSizeInAngstroms()) { } @@ -178,8 +180,9 @@ void ReactionLayout::_updateMetadata() } else { - const float ptab = /*first_single_product ? reaction_margin_size * 2 :*/ reaction_margin_size + atom_label_margin; - const float rtab = /*last_single_reactant ? reaction_margin_size * 2 :*/ reaction_margin_size + atom_label_margin; + const float ptab = /*first_single_product ? reaction_margin_size * 2 :*/ reaction_margin_size + (_font_size < EPSILON ? atom_label_margin : 0); + const float rtab = /*last_single_reactant ? reaction_margin_size * 2 :*/ reaction_margin_size + (_font_size < EPSILON ? atom_label_margin : 0); + ; arrow_head.y = product_box.middleY(); arrow_tail.y = react_box.middleY(); @@ -208,7 +211,11 @@ void ReactionLayout::processSideBoxes(std::vector& pluses, Rect2f& type_b BaseMolecule& mol = _r.getBaseMolecule(i); Rect2f box; - mol.getBoundingBox(box); + // If have font size calc bounding box with labes + if (_font_size > EPSILON) + mol.getBoundingBox(_font_size, _options.labelMode, box); + else + mol.getBoundingBox(box); if (i == begin) type_box.copy(box); else @@ -310,9 +317,6 @@ void ReactionLayout::make() _pushSpace(line, reaction_margin_size); for (int i = _r.catalystBegin(); i < _r.catalystEnd(); i = _r.catalystNext(i)) { - auto& mol = _getMol(i); - Rect2f bbox; - mol.getBoundingBox(bbox, Vec2f(bond_length, bond_length)); if (i != _r.catalystBegin()) _pushSpace(line, reaction_margin_size); _pushMol(line, i, true); @@ -344,7 +348,8 @@ void ReactionLayout::_pushMol(Metalayout::LayoutLine& line, int id, bool is_cata { // Molecule label alligned to atom center by non-hydrogen // Hydrogen may be at left or at right H2O, PH3 - so add space before and after molecule - _pushSpace(line, atom_label_margin); + if (_font_size < EPSILON) + _pushSpace(line, atom_label_margin); Metalayout::LayoutItem& item = line.items.push(); item.type = Metalayout::LayoutItem::Type::EMolecule; item.isMoleculeFragment = true; @@ -354,12 +359,16 @@ void ReactionLayout::_pushMol(Metalayout::LayoutLine& line, int id, bool is_cata { item.verticalAlign = Metalayout::LayoutItem::ItemVerticalAlign::ETop; } - Rect2f bbox; - mol.getBoundingBox(bbox); + // If have font size calc bounding box with labes + if (_font_size > EPSILON) + mol.getBoundingBox(_font_size, _options.labelMode, bbox); + else + mol.getBoundingBox(bbox); item.min.copy(bbox.leftBottom()); item.max.copy(bbox.rightTop()); - _pushSpace(line, atom_label_margin); + if (_font_size < EPSILON) + _pushSpace(line, atom_label_margin); } void ReactionLayout::_pushSpace(Metalayout::LayoutLine& line, float size) diff --git a/core/indigo-core/molecule/base_molecule.h b/core/indigo-core/molecule/base_molecule.h index 7da05ba606..22d448f2fa 100644 --- a/core/indigo-core/molecule/base_molecule.h +++ b/core/indigo-core/molecule/base_molecule.h @@ -28,6 +28,7 @@ #include "base_cpp/red_black.h" #include "graph/graph.h" #include "math/algebra.h" +#include "molecule/elements.h" #include "molecule/metadata_storage.h" #include "molecule/molecule_allene_stereo.h" #include "molecule/molecule_arom.h" @@ -539,6 +540,9 @@ namespace indigo void getBoundingBox(Rect2f& bbox, const Vec2f& minbox) const; void getBoundingBox(Vec2f& a, Vec2f& b) const; void offsetCoordinates(const Vec3f& offset); + void getAtomBoundingBox(int atom_idx, float font_size, LABEL_MODE label_mode, Vec2f& bottom_left, Vec2f& top_right); + void getBoundingBox(float font_size, LABEL_MODE label_mode, Vec2f& bottom_left, Vec2f& top_right); + void getBoundingBox(float font_size, LABEL_MODE label_mode, Rect2f& bbox); // aliases bool isAlias(int atom_idx) const; diff --git a/core/indigo-core/molecule/elements.h b/core/indigo-core/molecule/elements.h index 895a18e97f..d4b70d5759 100644 --- a/core/indigo-core/molecule/elements.h +++ b/core/indigo-core/molecule/elements.h @@ -164,6 +164,21 @@ namespace indigo ELEM_ATOMLIST }; + inline bool ElementHygrodenOnLeft(int el) + { + if (el == ELEM_O || el == ELEM_F || el == ELEM_S || el == ELEM_Cl || el == ELEM_Se || el == ELEM_Br || el == ELEM_I) + return true; + return false; + }; + + enum LABEL_MODE + { + LABEL_MODE_NONE, + LABEL_MODE_HETERO, + LABEL_MODE_TERMINAL_HETERO, + LABEL_MODE_ALL + }; + enum { RADICAL_SINGLET = 1, diff --git a/core/indigo-core/molecule/src/base_molecule.cpp b/core/indigo-core/molecule/src/base_molecule.cpp index 5760f7737a..258a41f0f6 100644 --- a/core/indigo-core/molecule/src/base_molecule.cpp +++ b/core/indigo-core/molecule/src/base_molecule.cpp @@ -4528,6 +4528,229 @@ void BaseMolecule::offsetCoordinates(const Vec3f& offset) _xyz[i].add(offset); } +void BaseMolecule::getAtomBoundingBox(int atom_idx, float font_size, LABEL_MODE label_mode, Vec2f& bottom_left, Vec2f& top_right) +{ + Vec2f vec = _xyz[atom_idx].projectZ(); + bottom_left = top_right = vec; + if (font_size <= EPSILON) + return; + + float symbol_size = font_size * 0.6f; + + if (isPseudoAtom(atom_idx) || isTemplateAtom(atom_idx)) + { + const char* str = isPseudoAtom(atom_idx) ? getPseudoAtom(atom_idx) : getTemplateAtom(atom_idx); + size_t len = strlen(str); + Vec2f shift(len * symbol_size / 2.0f, symbol_size); // TODO: Add pseudoatom parsing + bottom_left.sub(shift); + top_right.add(shift); + } + else + { + + int charge = getAtomCharge(atom_idx); + int isotope = getAtomIsotope(atom_idx); + int radical = -1; + int valence = getExplicitValence(atom_idx); + bool query = isQueryMolecule(); + int implicit_h = 0; + const Vertex& vertex = getVertex(atom_idx); + int atomNumber = getAtomNumber(atom_idx); + int label = 0; + bool atom_regular = !query || QueryMolecule::queryAtomIsRegular(asQueryMolecule(), atom_idx); + + if (!isRSite(atom_idx)) + { + if (atom_regular) + label = atomNumber; + radical = getAtomRadical_NoThrow(atom_idx, -1); + if (!query) + implicit_h = asMolecule().getImplicitH_NoThrow(atom_idx, 0); + } + + bool plainCarbon = label == ELEM_C && charge == (query ? CHARGE_UNKNOWN : 0) && isotope == (query ? -1 : 0) && radical <= 0 && valence == -1; + bool showLabel = true; + if (label_mode == LABEL_MODE_ALL || vertex.degree() == 0) + ; + else if (label_mode == LABEL_MODE_NONE) + showLabel = false; + else if (plainCarbon && (label_mode == LABEL_MODE_HETERO || vertex.degree() > 1)) + { + showLabel = false; + if (vertex.degree() == 2) + { + int k1 = vertex.neiBegin(); + int k2 = vertex.neiNext(k1); + if (getBondOrder(vertex.neiEdge(k1)) == getBondOrder(vertex.neiEdge(k2))) + { + int a1 = vertex.neiVertex(k1); + int a2 = vertex.neiVertex(k2); + Vec2f vk1(_xyz[a1].x, _xyz[a1].y); + Vec2f vk2(_xyz[a2].x, _xyz[a2].y); + Vec2f dir_k1, dir_k2; + dir_k1.diff(vec, vk1); + dir_k1.normalize(); + dir_k2.diff(vec, vk2); + dir_k2.normalize(); + float dot = Vec2f::dot(dir_k1, dir_k2); + if (dot < -0.97) + showLabel = true; + } + } + } + if (showLabel) + { + // calc label size + size_t len = 0; + if (query && !atom_regular) + { + Array list; + int atom_type = QueryMolecule::parseQueryAtom(asQueryMolecule(), atom_idx, list); + switch (atom_type) + { + case QueryMolecule::QUERY_ATOM_A: + case QueryMolecule::QUERY_ATOM_X: + case QueryMolecule::QUERY_ATOM_Q: + case QueryMolecule::QUERY_ATOM_M: + len = 1; + break; + case QueryMolecule::QUERY_ATOM_AH: + case QueryMolecule::QUERY_ATOM_XH: + case QueryMolecule::QUERY_ATOM_QH: + case QueryMolecule::QUERY_ATOM_MH: + case QueryMolecule::QUERY_ATOM_SINGLE: + len = 2; + break; + case QueryMolecule::QUERY_ATOM_LIST: + case QueryMolecule::QUERY_ATOM_NOTLIST: + len = 1 + list.size() / 2; + for (int i = 0; i < list.size(); i++) + { + len += strlen(Element::toString(list[i])); + } + break; + } + } + else + { + len = strlen(Element::toString(label)); + } + Vec2f shift(len * symbol_size / 2.0f, symbol_size); + bottom_left.sub(shift); + top_right.add(shift); + // Add isotope at left + if (isotope > 0 && !(label = ELEM_H && (isotope == DEUTERIUM || isotope == TRITIUM))) + { + if (isotope > 99) + len = 3; + else if (isotope > 9) + len = 2; + else + len = 1; + bottom_left.x -= len * symbol_size; + } + // Add valence at right + if (valence > 0) + { + static constexpr int count[] = { + 1, // 0 + 1, // I + 2, // II + 3, // III + 2, // IV + 1, // V + 2, // VI + 3, // VII + 4, // VIII + 2, // IX + 1, // X + + }; + top_right.x += count[valence] * symbol_size; + } + // Add charge at right + if (charge != 0) + { + if (abs(charge) > 9) + len = 3; + else if (abs(charge) > 1) + len = 2; + else + len = 1; + top_right.x += len * symbol_size; + } + if (implicit_h > 0) + { + // add implicit H size + if (implicit_h > 1) + len = 2; + else + len = 1; + bool h_at_right = true; + if (vertex.degree() == 0) + { + if (ElementHygrodenOnLeft(label)) + h_at_right = false; + } + else + { + constexpr float min_sin = 0.49f; + float right_weight = 0.3f; + float left_weight = 0.2f; + float left_sin = 0, right_sin = 0; + for (int j = vertex.neiBegin(); j < vertex.neiEnd(); j = vertex.neiNext(j)) + { + Vec2f d = _xyz[vertex.neiVertex(j)].projectZ(); + d.sub(vec); + d.normalize(); + if (d.x > 0) + right_sin = std::max(right_sin, d.x); + else + left_sin = std::max(left_sin, -d.x); + } + if (left_sin > min_sin) + left_weight -= left_sin; + if (right_sin > min_sin) + right_weight -= right_sin; + if (left_weight > right_weight) + h_at_right = false; + } + if (h_at_right) + top_right.x += len * symbol_size; + else + bottom_left.x -= len * symbol_size; + } + } + } + // process AAM +} + +void BaseMolecule::getBoundingBox(float font_size, LABEL_MODE label_mode, Vec2f& bottom_left, Vec2f& top_right) +{ + Vec2f atom_bottom_left, atom_top_right; + for (int atom_idx = 0; atom_idx < vertexCount(); ++atom_idx) + { + getAtomBoundingBox(atom_idx, font_size, label_mode, atom_bottom_left, atom_top_right); + if (!atom_idx) + { + bottom_left = atom_bottom_left; + top_right = atom_top_right; + } + else + { + bottom_left.min(atom_bottom_left); + top_right.max(atom_top_right); + } + } +} + +void BaseMolecule::getBoundingBox(float font_size, LABEL_MODE label_mode, Rect2f& bbox) +{ + Vec2f a, b; + getBoundingBox(font_size, label_mode, a, b); + bbox = Rect2f(a, b); +} + void BaseMolecule::getBoundingBox(Vec2f& a, Vec2f& b) const { for (int atom_idx = 0; atom_idx < vertexCount(); ++atom_idx) diff --git a/core/render2d/render_common.h b/core/render2d/render_common.h index 7f061df6ee..9a880f6fed 100644 --- a/core/render2d/render_common.h +++ b/core/render2d/render_common.h @@ -54,13 +54,6 @@ namespace indigo MODE_PRN, MODE_CDXML }; - enum LABEL_MODE - { - LABEL_MODE_NONE, - LABEL_MODE_HETERO, - LABEL_MODE_TERMINAL_HETERO, - LABEL_MODE_ALL - }; enum STEREO_STYLE { STEREO_STYLE_EXT, diff --git a/core/render2d/src/render_internal.cpp b/core/render2d/src/render_internal.cpp index 878c850400..794ebacfe9 100644 --- a/core/render2d/src/render_internal.cpp +++ b/core/render2d/src/render_internal.cpp @@ -37,113 +37,6 @@ using namespace indigo; #define BOND_STEREO_BOLD 10001 -static bool ElementHygrodenOnLeft[] = { - false, // filler - false, // ELEM_H - false, // ELEM_He - false, // ELEM_Li - false, // ELEM_Be - false, // ELEM_B - false, // ELEM_C - false, // ELEM_N - true, // ELEM_O - true, // ELEM_F - false, // ELEM_Ne - false, // ELEM_Na - false, // ELEM_Mg - false, // ELEM_Al - false, // ELEM_Si - false, // ELEM_P - true, // ELEM_S - true, // ELEM_Cl - false, // ELEM_Ar - false, // ELEM_K - false, // ELEM_Ca - false, // ELEM_Sc - false, // ELEM_Ti - false, // ELEM_V - false, // ELEM_Cr - false, // ELEM_Mn - false, // ELEM_Fe - false, // ELEM_Co - false, // ELEM_Ni - false, // ELEM_Cu - false, // ELEM_Zn - false, // ELEM_Ga - false, // ELEM_Ge - false, // ELEM_As - true, // ELEM_Se - true, // ELEM_Br - false, // ELEM_Kr - false, // ELEM_Rb - false, // ELEM_Sr - false, // ELEM_Y - false, // ELEM_Zr - false, // ELEM_Nb - false, // ELEM_Mo - false, // ELEM_Tc - false, // ELEM_Ru - false, // ELEM_Rh - false, // ELEM_Pd - false, // ELEM_Ag - false, // ELEM_Cd - false, // ELEM_In - false, // ELEM_Sn - false, // ELEM_Sb - false, // ELEM_Te - true, // ELEM_I - false, // ELEM_Xe - false, // ELEM_Cs - false, // ELEM_Ba - false, // ELEM_La - false, // ELEM_Ce - false, // ELEM_Pr - false, // ELEM_Nd - false, // ELEM_Pm - false, // ELEM_Sm - false, // ELEM_Eu - false, // ELEM_Gd - false, // ELEM_Tb - false, // ELEM_Dy - false, // ELEM_Ho - false, // ELEM_Er - false, // ELEM_Tm - false, // ELEM_Yb - false, // ELEM_Lu - false, // ELEM_Hf - false, // ELEM_Ta - false, // ELEM_W - false, // ELEM_Re - false, // ELEM_Os - false, // ELEM_Ir - false, // ELEM_Pt - false, // ELEM_Au - false, // ELEM_Hg - false, // ELEM_Tl - false, // ELEM_Pb - false, // ELEM_Bi - false, // ELEM_Po - false, // ELEM_At - false, // ELEM_Rn - false, // ELEM_Fr - false, // ELEM_Ra - false, // ELEM_Ac - false, // ELEM_Th - false, // ELEM_Pa - false, // ELEM_U - false, // ELEM_Np - false, // ELEM_Pu - false, // ELEM_Am - false, // ELEM_Cm - false, // ELEM_Bk - false, // ELEM_Cf - false, // ELEM_Es - false, // ELEM_Fm - false, // ELEM_Md - false, // ELEM_No - false // ELEM_Lr -}; - static bool _isBondWide(const BondDescr& bd) { return bd.type == BOND_DOUBLE || bd.type == BOND_TRIPLE || bd.queryType == _BOND_DOUBLE_OR_AROMATIC || bd.queryType == _BOND_SINGLE_OR_AROMATIC || @@ -1618,7 +1511,7 @@ void MoleculeRenderInternal::_initHydroPos(int aid) { AtomDesc& ad = _ad(aid); const Vertex& v = _mol->getVertex(aid); - if (v.degree() == 0 && ElementHygrodenOnLeft[ad.label]) + if (v.degree() == 0 && ElementHygrodenOnLeft(ad.label)) { ad.implHPosWeights[HYDRO_POS_RIGHT] = 0.2f; // weights are relative, absoute values don't matter ad.implHPosWeights[HYDRO_POS_LEFT] = 0.3f;