MoleditPy-linux 4.8.1__tar.gz → 4.8.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/PKG-INFO +4 -4
  2. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/README.md +3 -3
  3. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/pyproject.toml +1 -1
  4. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/MoleditPy_linux.egg-info/PKG-INFO +4 -4
  5. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/calculation_worker.py +87 -10
  6. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/LICENSE +0 -0
  7. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/setup.cfg +0 -0
  8. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
  9. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
  10. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
  11. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
  12. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
  13. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/__init__.py +0 -0
  14. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/__main__.py +0 -0
  15. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/assets/file_icon.ico +0 -0
  16. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/assets/icon.icns +0 -0
  17. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/assets/icon.ico +0 -0
  18. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/assets/icon.png +0 -0
  19. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/core/__init__.py +0 -0
  20. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/core/mol_geometry.py +0 -0
  21. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/core/molecular_data.py +0 -0
  22. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/main.py +0 -0
  23. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/plugins/__init__.py +0 -0
  24. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/plugins/plugin_interface.py +0 -0
  25. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/plugins/plugin_manager.py +0 -0
  26. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/plugins/plugin_manager_window.py +0 -0
  27. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/__init__.py +0 -0
  28. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/about_dialog.py +0 -0
  29. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/align_plane_dialog.py +0 -0
  30. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/alignment_dialog.py +0 -0
  31. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/analysis_window.py +0 -0
  32. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/angle_dialog.py +0 -0
  33. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/app_state.py +0 -0
  34. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/atom_item.py +0 -0
  35. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/atom_picking.py +0 -0
  36. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
  37. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/bond_item.py +0 -0
  38. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/bond_length_dialog.py +0 -0
  39. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/chain_mixin.py +0 -0
  40. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
  41. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/compute_logic.py +0 -0
  42. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +0 -0
  43. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/custom_interactor_style.py +0 -0
  44. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -0
  45. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +0 -0
  46. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/dialog_logic.py +0 -0
  47. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/dihedral_dialog.py +0 -0
  48. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
  49. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/edit_actions_logic.py +0 -0
  50. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/export_logic.py +0 -0
  51. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
  52. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/io_logic.py +0 -0
  53. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/main_window.py +0 -0
  54. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/main_window_init.py +0 -0
  55. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/mirror_dialog.py +0 -0
  56. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/molecular_scene_handler.py +0 -0
  57. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/molecule_scene.py +0 -0
  58. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/move_group_dialog.py +0 -0
  59. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py +0 -0
  60. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
  61. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/planarize_dialog.py +0 -0
  62. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/plugin_menu_manager.py +0 -0
  63. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/preview_molecule.py +0 -0
  64. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/settings_dialog.py +0 -0
  65. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/settings_tabs/__init__.py +0 -0
  66. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +0 -0
  67. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
  68. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +0 -0
  69. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
  70. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/string_importers.py +0 -0
  71. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
  72. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/template_preview_view.py +0 -0
  73. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/translation_dialog.py +0 -0
  74. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/ui_manager.py +0 -0
  75. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/user_template_dialog.py +0 -0
  76. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/view_3d_logic.py +0 -0
  77. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/ui/zoomable_view.py +0 -0
  78. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/utils/__init__.py +0 -0
  79. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/utils/constants.py +0 -0
  80. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/utils/default_settings.py +0 -0
  81. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/utils/hit_radius.py +0 -0
  82. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
  83. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/utils/suppress_log.py +0 -0
  84. {moleditpy_linux-4.8.1 → moleditpy_linux-4.8.2}/src/moleditpy_linux/utils/system_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy-linux
3
- Version: 4.8.1
3
+ Version: 4.8.2
4
4
  Summary: A cross-platform, simple, and intuitive molecular structure editor built in Python. It allows 2D molecular drawing and 3D structure visualization. It supports exporting structure files for input to DFT calculation software.
5
5
  Author-email: HiroYokoyama <titech.yoko.hiro@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -709,9 +709,9 @@ Dynamic: license-file
709
709
  [![Python Versions](https://img.shields.io/pypi/pyversions/MoleditPy.svg)](https://pypi.org/project/MoleditPy/)
710
710
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
711
711
  [![Build Status](https://github.com/HiroYokoyama/python_molecular_editor/actions/workflows/tests.yml/badge.svg)](https://github.com/HiroYokoyama/python_molecular_editor/actions)
712
- ![Overall Coverage](https://img.shields.io/badge/coverage->80%25-green)
713
- ![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)
714
- ![Pylint Score](https://img.shields.io/badge/pylint->9%2F10-brightgreen)
712
+ [![Overall Coverage](https://img.shields.io/badge/coverage->80%25-green)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/coverage_report.md)
713
+ [![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/MANUAL_CHECKLIST.md)
714
+ [![Pylint Score](https://img.shields.io/badge/pylint->9%2F10-brightgreen)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/pylint-score.txt)
715
715
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/moleditpy)
716
716
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=monthly&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=monthly+downloads)](https://pepy.tech/projects/moleditpy)
717
717
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/HiroYokoyama/python_molecular_editor)
@@ -6,9 +6,9 @@
6
6
  [![Python Versions](https://img.shields.io/pypi/pyversions/MoleditPy.svg)](https://pypi.org/project/MoleditPy/)
7
7
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
8
8
  [![Build Status](https://github.com/HiroYokoyama/python_molecular_editor/actions/workflows/tests.yml/badge.svg)](https://github.com/HiroYokoyama/python_molecular_editor/actions)
9
- ![Overall Coverage](https://img.shields.io/badge/coverage->80%25-green)
10
- ![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)
11
- ![Pylint Score](https://img.shields.io/badge/pylint->9%2F10-brightgreen)
9
+ [![Overall Coverage](https://img.shields.io/badge/coverage->80%25-green)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/coverage_report.md)
10
+ [![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/MANUAL_CHECKLIST.md)
11
+ [![Pylint Score](https://img.shields.io/badge/pylint->9%2F10-brightgreen)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/pylint-score.txt)
12
12
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/moleditpy)
13
13
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=monthly&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=monthly+downloads)](https://pepy.tech/projects/moleditpy)
14
14
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/HiroYokoyama/python_molecular_editor)
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "MoleditPy-linux"
7
7
 
8
- version = "4.8.1"
8
+ version = "4.8.2"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy-linux
3
- Version: 4.8.1
3
+ Version: 4.8.2
4
4
  Summary: A cross-platform, simple, and intuitive molecular structure editor built in Python. It allows 2D molecular drawing and 3D structure visualization. It supports exporting structure files for input to DFT calculation software.
5
5
  Author-email: HiroYokoyama <titech.yoko.hiro@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -709,9 +709,9 @@ Dynamic: license-file
709
709
  [![Python Versions](https://img.shields.io/pypi/pyversions/MoleditPy.svg)](https://pypi.org/project/MoleditPy/)
710
710
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
711
711
  [![Build Status](https://github.com/HiroYokoyama/python_molecular_editor/actions/workflows/tests.yml/badge.svg)](https://github.com/HiroYokoyama/python_molecular_editor/actions)
712
- ![Overall Coverage](https://img.shields.io/badge/coverage->80%25-green)
713
- ![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)
714
- ![Pylint Score](https://img.shields.io/badge/pylint->9%2F10-brightgreen)
712
+ [![Overall Coverage](https://img.shields.io/badge/coverage->80%25-green)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/coverage_report.md)
713
+ [![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/MANUAL_CHECKLIST.md)
714
+ [![Pylint Score](https://img.shields.io/badge/pylint->9%2F10-brightgreen)](https://github.com/HiroYokoyama/python_molecular_editor/blob/main/tests/pylint-score.txt)
715
715
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/moleditpy)
716
716
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=monthly&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=monthly+downloads)](https://pepy.tech/projects/moleditpy)
717
717
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/HiroYokoyama/python_molecular_editor)
@@ -26,6 +26,8 @@ from rdkit import Chem
26
26
  from rdkit.Chem import AllChem, rdGeometry
27
27
  from rdkit.DistanceGeometry import DoTriangleSmoothing
28
28
 
29
+ from ..utils.constants import DEFAULT_BOND_LENGTH_ANGSTROM
30
+
29
31
  from .. import OBABEL_AVAILABLE
30
32
 
31
33
  # Only import pybel on demand
@@ -367,6 +369,50 @@ def _apply_explicit_stereo(
367
369
  bond.SetStereo(stereo_type)
368
370
 
369
371
 
372
+ # Typical X-H distances used when placing hydrogens the 2D sketch never had.
373
+ _DIRECT_XH_LENGTHS = {6: 1.09, 7: 1.01, 8: 0.97, 15: 1.42, 16: 1.34}
374
+ _DIRECT_XH_DEFAULT = 1.10
375
+ # Out-of-plane tilt applied to added H and to wedge/hash bonds.
376
+ _DIRECT_H_TILT = math.radians(20.0)
377
+ _DIRECT_WEDGE_TILT = math.radians(35.0)
378
+
379
+
380
+ def _rescale_2d_layout(
381
+ coords: list, mol: Chem.Mol, _safe_status: Callable[[str], None]
382
+ ) -> list:
383
+ """Scale parsed 2D coordinates so the median bond is a chemically sane length."""
384
+ lengths = []
385
+ for bond in mol.GetBonds():
386
+ i, j = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()
387
+ if i < len(coords) and j < len(coords):
388
+ d = math.hypot(coords[i][0] - coords[j][0], coords[i][1] - coords[j][1])
389
+ if d > 1e-6:
390
+ lengths.append(d)
391
+ if not lengths:
392
+ return coords
393
+
394
+ median = float(np.median(lengths))
395
+ if median < 1e-6 or abs(median - DEFAULT_BOND_LENGTH_ANGSTROM) < 0.05:
396
+ return coords
397
+
398
+ scale = DEFAULT_BOND_LENGTH_ANGSTROM / median
399
+ _safe_status(f"Rescaling 2D layout (x{scale:.3f})...")
400
+ return [(x * scale, y * scale, z * scale) for x, y, z in coords]
401
+
402
+
403
+ def _center_conformer(conf: Chem.Conformer) -> None:
404
+ """Move the conformer's centroid to the origin."""
405
+ n = conf.GetNumAtoms()
406
+ if n == 0:
407
+ return
408
+ pos = np.array(
409
+ [[p.x, p.y, p.z] for p in (conf.GetAtomPosition(i) for i in range(n))],
410
+ dtype=float,
411
+ )
412
+ for i, (x, y, z) in enumerate(pos - pos.mean(axis=0)):
413
+ conf.SetAtomPosition(i, rdGeometry.Point3D(float(x), float(y), float(z)))
414
+
415
+
370
416
  def _perform_direct_conversion(
371
417
  mol_block: str,
372
418
  mol: Chem.Mol,
@@ -446,6 +492,8 @@ def _perform_direct_conversion(
446
492
  if not parsed_coords:
447
493
  raise ValueError("Failed to parse coordinates for direct conversion.")
448
494
 
495
+ parsed_coords = _rescale_2d_layout(parsed_coords, mol, _safe_status)
496
+
449
497
  num_existing = len(parsed_coords)
450
498
  conf = Chem.Conformer(mol.GetNumAtoms())
451
499
  for i in range(mol.GetNumAtoms()):
@@ -480,28 +528,45 @@ def _perform_direct_conversion(
480
528
  vecs[0][0],
481
529
  math.hypot(-vecs[0][1], vecs[0][0]),
482
530
  )
483
- angle = sum(
531
+ n_placed = sum(
484
532
  1
485
533
  for n in mol.GetAtomWithIdx(parent_idx).GetNeighbors()
486
534
  if n.GetIdx() < i and n.GetAtomicNum() == 1
487
- ) * (math.pi / 6.0)
535
+ )
536
+ angle = n_placed * (math.pi / 6.0)
488
537
  rx, ry = (
489
538
  (fx / fn) * math.cos(angle) - (fy / fn) * math.sin(angle),
490
539
  (fx / fn) * math.sin(angle) + (fy / fn) * math.cos(angle),
491
540
  )
541
+ bond_len = _DIRECT_XH_LENGTHS.get(
542
+ mol.GetAtomWithIdx(parent_idx).GetAtomicNum(),
543
+ _DIRECT_XH_DEFAULT,
544
+ )
545
+ in_plane = bond_len * math.cos(_DIRECT_H_TILT)
546
+ # Alternate the pucker so a CH2's two hydrogens straddle
547
+ # the plane instead of stacking on the same side.
548
+ dz = bond_len * math.sin(_DIRECT_H_TILT)
549
+ if n_placed % 2:
550
+ dz = -dz
492
551
  conf.SetAtomPosition(
493
552
  i,
494
553
  rdGeometry.Point3D(
495
- float(parent_pos.x) + rx, float(parent_pos.y) + ry, 0.3
554
+ float(parent_pos.x) + rx * in_plane,
555
+ float(parent_pos.y) + ry * in_plane,
556
+ float(parent_pos.z) + dz,
496
557
  ),
497
558
  )
498
559
  else:
560
+ bond_len = _DIRECT_XH_LENGTHS.get(
561
+ mol.GetAtomWithIdx(parent_idx).GetAtomicNum(),
562
+ _DIRECT_XH_DEFAULT,
563
+ )
499
564
  conf.SetAtomPosition(
500
565
  i,
501
566
  rdGeometry.Point3D(
502
- float(parent_pos.x) + 0.5,
503
- float(parent_pos.y) + 0.5,
504
- 0.3,
567
+ float(parent_pos.x) + bond_len,
568
+ float(parent_pos.y),
569
+ float(parent_pos.z),
505
570
  ),
506
571
  )
507
572
  else:
@@ -511,16 +576,28 @@ def _perform_direct_conversion(
511
576
  with suppress_log(AttributeError, RuntimeError, TypeError, IndexError):
512
577
  for b, e, flag in stereo_dirs:
513
578
  if b < num_existing and e < num_existing:
514
- pos = conf.GetAtomPosition(e)
579
+ start, pos = conf.GetAtomPosition(b), conf.GetAtomPosition(e)
580
+ vx, vy = float(pos.x) - float(start.x), float(pos.y) - float(start.y)
581
+ length = math.hypot(vx, vy)
582
+ if length < 1e-6:
583
+ continue
584
+ # Rotate the bond out of plane so it keeps its length; adding a
585
+ # flat +/-1.5 A of z stretched every wedge bond instead.
586
+ sign = 1.0 if flag == 1 else -1.0
587
+ scale = math.cos(_DIRECT_WEDGE_TILT)
515
588
  conf.SetAtomPosition(
516
589
  e,
517
590
  rdGeometry.Point3D(
518
- float(pos.x),
519
- float(pos.y),
520
- float(pos.z) + (1.5 if flag == 1 else -1.5),
591
+ float(start.x) + vx * scale,
592
+ float(start.y) + vy * scale,
593
+ float(start.z) + sign * length * math.sin(_DIRECT_WEDGE_TILT),
521
594
  ),
522
595
  )
523
596
 
597
+ # The sketch sits wherever it was drawn on the canvas, which can be hundreds
598
+ # of angstroms from the origin; centre it before handing it to the 3D view.
599
+ _center_conformer(conf)
600
+
524
601
  mol.RemoveAllConformers()
525
602
  mol.AddConformer(conf, assignId=True)
526
603
 
File without changes