MoleditPy 3.0.0a2__tar.gz → 3.0.0a4__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.
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/PKG-INFO +3 -2
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/README.md +2 -1
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/pyproject.toml +1 -1
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/MoleditPy.egg-info/PKG-INFO +3 -2
- moleditpy-3.0.0a4/src/MoleditPy.egg-info/SOURCES.txt +84 -0
- moleditpy-3.0.0a4/src/moleditpy/__init__.py +20 -0
- moleditpy-3.0.0a4/src/moleditpy/core/__init__.py +0 -0
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/core}/mol_geometry.py +168 -16
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/core}/molecular_data.py +137 -66
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/moleditpy/main.py +23 -4
- moleditpy-3.0.0a4/src/moleditpy/modules/__init__.py +122 -0
- {moleditpy-3.0.0a2/src/moleditpy → moleditpy-3.0.0a4/src/moleditpy/plugins}/__init__.py +11 -15
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/plugins}/plugin_manager.py +574 -578
- moleditpy-3.0.0a4/src/moleditpy/ui/__init__.py +25 -0
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/about_dialog.py +4 -3
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/align_plane_dialog.py +306 -306
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/alignment_dialog.py +1 -1
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/analysis_window.py +226 -226
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/angle_dialog.py +5 -4
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_app_state.py → moleditpy-3.0.0a4/src/moleditpy/ui/app_state.py +22 -27
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/atom_item.py +38 -29
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/bond_item.py +543 -528
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/bond_length_dialog.py +3 -2
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/calculation_worker.py +970 -970
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/color_settings_dialog.py +404 -404
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_compute.py → moleditpy-3.0.0a4/src/moleditpy/ui/compute_engine.py +4 -11
- moleditpy-3.0.0a4/src/moleditpy/ui/compute_logic.py +529 -0
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/constrained_optimization_dialog.py +2 -1
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/custom_interactor_style.py +34 -24
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/custom_qt_interactor.py +97 -97
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/dialog_3d_picking_mixin.py +210 -210
- moleditpy-3.0.0a4/src/moleditpy/ui/dialog_logic.py +438 -0
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_dialog_manager.py → moleditpy-3.0.0a4/src/moleditpy/ui/dialog_manager.py +17 -21
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/dihedral_dialog.py +5 -4
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_edit_3d.py → moleditpy-3.0.0a4/src/moleditpy/ui/edit_3d_logic.py +51 -26
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_edit_actions.py → moleditpy-3.0.0a4/src/moleditpy/ui/edit_actions_logic.py +216 -322
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_export.py → moleditpy-3.0.0a4/src/moleditpy/ui/export_logic.py +37 -32
- moleditpy-3.0.0a4/src/moleditpy/ui/main_window.py +218 -0
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_main_init.py → moleditpy-3.0.0a4/src/moleditpy/ui/main_window_init.py +58 -72
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/mirror_dialog.py +140 -140
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_molecular_parsers.py → moleditpy-3.0.0a4/src/moleditpy/ui/molecular_parsers.py +592 -594
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/molecular_scene_handler.py +30 -15
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/molecule_scene.py +33 -24
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/move_group_dialog.py +4 -6
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/periodic_table_dialog.py +196 -196
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/planarize_dialog.py +239 -242
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_project_io.py → moleditpy-3.0.0a4/src/moleditpy/ui/project_io.py +9 -15
- moleditpy-3.0.0a4/src/moleditpy/ui/settings_dialog.py +229 -0
- moleditpy-3.0.0a4/src/moleditpy/ui/settings_tabs/__init__.py +11 -0
- moleditpy-3.0.0a4/src/moleditpy/ui/settings_tabs/settings_2d_tab.py +249 -0
- moleditpy-3.0.0a4/src/moleditpy/ui/settings_tabs/settings_3d_tabs.py +321 -0
- moleditpy-3.0.0a4/src/moleditpy/ui/settings_tabs/settings_other_tab.py +99 -0
- moleditpy-3.0.0a4/src/moleditpy/ui/settings_tabs/settings_tab_base.py +33 -0
- moleditpy-3.0.0a2/src/moleditpy/modules/__init__.py → moleditpy-3.0.0a4/src/moleditpy/ui/sip_isdeleted_safe.py +35 -41
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_string_importers.py → moleditpy-3.0.0a4/src/moleditpy/ui/string_importers.py +247 -248
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/template_preview_item.py +2 -2
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/template_preview_view.py +85 -85
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/translation_dialog.py +347 -347
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_ui_manager.py → moleditpy-3.0.0a4/src/moleditpy/ui/ui_manager.py +511 -518
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/user_template_dialog.py +712 -712
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_view_3d.py → moleditpy-3.0.0a4/src/moleditpy/ui/view_3d_logic.py +89 -69
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window_view_loaders.py → moleditpy-3.0.0a4/src/moleditpy/ui/view_loaders.py +274 -282
- moleditpy-3.0.0a4/src/moleditpy/utils/__init__.py +11 -0
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/utils}/constants.py +1 -1
- moleditpy-3.0.0a4/src/moleditpy/utils/sip_isdeleted_safe.py +41 -0
- moleditpy-3.0.0a2/src/MoleditPy.egg-info/SOURCES.txt +0 -72
- moleditpy-3.0.0a2/src/moleditpy/modules/main_window.py +0 -107
- moleditpy-3.0.0a2/src/moleditpy/modules/settings_dialog.py +0 -1705
- moleditpy-3.0.0a2/src/moleditpy/modules/sip_isdeleted_safe.py +0 -17
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/LICENSE +0 -0
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/setup.cfg +0 -0
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/MoleditPy.egg-info/entry_points.txt +0 -0
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/MoleditPy.egg-info/requires.txt +0 -0
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/MoleditPy.egg-info/top_level.txt +0 -0
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/moleditpy/__main__.py +0 -0
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/moleditpy/assets/file_icon.ico +0 -0
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/moleditpy/assets/icon.icns +0 -0
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/moleditpy/assets/icon.ico +0 -0
- {moleditpy-3.0.0a2 → moleditpy-3.0.0a4}/src/moleditpy/assets/icon.png +0 -0
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/plugins}/plugin_interface.py +0 -0
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/plugins}/plugin_manager_window.py +0 -0
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/ui}/zoomable_view.py +0 -0
- {moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/utils}/system_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0a4
|
|
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
|
|
@@ -711,7 +711,7 @@ Dynamic: license-file
|
|
|
711
711
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
712
712
|
[](https://github.com/HiroYokoyama/python_molecular_editor/actions)
|
|
713
713
|

|
|
714
|
-

|
|
715
715
|

|
|
716
716
|

|
|
717
717
|
[](https://pepy.tech/projects/moleditpy)
|
|
@@ -834,6 +834,7 @@ moleditpy
|
|
|
834
834
|
* **GUI and 2D Drawing (PyQt6):** The editor is built on a `QGraphicsScene`, where custom `AtomItem` and `BondItem` objects are interactively manipulated. The Undo/Redo feature is implemented by serializing the application state.
|
|
835
835
|
* **Chemical Calculations (RDKit / Open Babel):** RDKit is used to generate molecule objects from 2D data, perform 3D coordinate generation, and calculate properties. Open Babel serves as a fallback for 3D conversion. All heavy computations are run on a separate `QThread` to keep the GUI responsive.
|
|
836
836
|
* **3D Visualization (PyVista / pyvistaqt):** 3D rendering is achieved by generating PyVista meshes (spheres and cylinders) from RDKit conformer coordinates. A custom `vtkInteractorStyle` enables direct drag-and-drop editing of atoms in the 3D view.
|
|
837
|
+
* **Modular Architecture:** The codebase is organized into dedicated packages for `core` logic, `ui` components, and `utils`. The main application logic is decomposed into reusable mixins, ensuring long-term maintainability and easier verification.
|
|
837
838
|
|
|
838
839
|
## License
|
|
839
840
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
8
8
|
[](https://github.com/HiroYokoyama/python_molecular_editor/actions)
|
|
9
9
|

|
|
10
|
-

|
|
11
11
|

|
|
12
12
|

|
|
13
13
|
[](https://pepy.tech/projects/moleditpy)
|
|
@@ -130,6 +130,7 @@ moleditpy
|
|
|
130
130
|
* **GUI and 2D Drawing (PyQt6):** The editor is built on a `QGraphicsScene`, where custom `AtomItem` and `BondItem` objects are interactively manipulated. The Undo/Redo feature is implemented by serializing the application state.
|
|
131
131
|
* **Chemical Calculations (RDKit / Open Babel):** RDKit is used to generate molecule objects from 2D data, perform 3D coordinate generation, and calculate properties. Open Babel serves as a fallback for 3D conversion. All heavy computations are run on a separate `QThread` to keep the GUI responsive.
|
|
132
132
|
* **3D Visualization (PyVista / pyvistaqt):** 3D rendering is achieved by generating PyVista meshes (spheres and cylinders) from RDKit conformer coordinates. A custom `vtkInteractorStyle` enables direct drag-and-drop editing of atoms in the 3D view.
|
|
133
|
+
* **Modular Architecture:** The codebase is organized into dedicated packages for `core` logic, `ui` components, and `utils`. The main application logic is decomposed into reusable mixins, ensuring long-term maintainability and easier verification.
|
|
133
134
|
|
|
134
135
|
## License
|
|
135
136
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0a4
|
|
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
|
|
@@ -711,7 +711,7 @@ Dynamic: license-file
|
|
|
711
711
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
712
712
|
[](https://github.com/HiroYokoyama/python_molecular_editor/actions)
|
|
713
713
|

|
|
714
|
-

|
|
715
715
|

|
|
716
716
|

|
|
717
717
|
[](https://pepy.tech/projects/moleditpy)
|
|
@@ -834,6 +834,7 @@ moleditpy
|
|
|
834
834
|
* **GUI and 2D Drawing (PyQt6):** The editor is built on a `QGraphicsScene`, where custom `AtomItem` and `BondItem` objects are interactively manipulated. The Undo/Redo feature is implemented by serializing the application state.
|
|
835
835
|
* **Chemical Calculations (RDKit / Open Babel):** RDKit is used to generate molecule objects from 2D data, perform 3D coordinate generation, and calculate properties. Open Babel serves as a fallback for 3D conversion. All heavy computations are run on a separate `QThread` to keep the GUI responsive.
|
|
836
836
|
* **3D Visualization (PyVista / pyvistaqt):** 3D rendering is achieved by generating PyVista meshes (spheres and cylinders) from RDKit conformer coordinates. A custom `vtkInteractorStyle` enables direct drag-and-drop editing of atoms in the 3D view.
|
|
837
|
+
* **Modular Architecture:** The codebase is organized into dedicated packages for `core` logic, `ui` components, and `utils`. The main application logic is decomposed into reusable mixins, ensuring long-term maintainability and easier verification.
|
|
837
838
|
|
|
838
839
|
## License
|
|
839
840
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/MoleditPy.egg-info/PKG-INFO
|
|
5
|
+
src/MoleditPy.egg-info/SOURCES.txt
|
|
6
|
+
src/MoleditPy.egg-info/dependency_links.txt
|
|
7
|
+
src/MoleditPy.egg-info/entry_points.txt
|
|
8
|
+
src/MoleditPy.egg-info/requires.txt
|
|
9
|
+
src/MoleditPy.egg-info/top_level.txt
|
|
10
|
+
src/moleditpy/__init__.py
|
|
11
|
+
src/moleditpy/__main__.py
|
|
12
|
+
src/moleditpy/main.py
|
|
13
|
+
src/moleditpy.egg-info/PKG-INFO
|
|
14
|
+
src/moleditpy.egg-info/SOURCES.txt
|
|
15
|
+
src/moleditpy.egg-info/dependency_links.txt
|
|
16
|
+
src/moleditpy.egg-info/entry_points.txt
|
|
17
|
+
src/moleditpy.egg-info/requires.txt
|
|
18
|
+
src/moleditpy.egg-info/top_level.txt
|
|
19
|
+
src/moleditpy/assets/file_icon.ico
|
|
20
|
+
src/moleditpy/assets/icon.icns
|
|
21
|
+
src/moleditpy/assets/icon.ico
|
|
22
|
+
src/moleditpy/assets/icon.png
|
|
23
|
+
src/moleditpy/core/__init__.py
|
|
24
|
+
src/moleditpy/core/mol_geometry.py
|
|
25
|
+
src/moleditpy/core/molecular_data.py
|
|
26
|
+
src/moleditpy/modules/__init__.py
|
|
27
|
+
src/moleditpy/plugins/__init__.py
|
|
28
|
+
src/moleditpy/plugins/plugin_interface.py
|
|
29
|
+
src/moleditpy/plugins/plugin_manager.py
|
|
30
|
+
src/moleditpy/plugins/plugin_manager_window.py
|
|
31
|
+
src/moleditpy/ui/__init__.py
|
|
32
|
+
src/moleditpy/ui/about_dialog.py
|
|
33
|
+
src/moleditpy/ui/align_plane_dialog.py
|
|
34
|
+
src/moleditpy/ui/alignment_dialog.py
|
|
35
|
+
src/moleditpy/ui/analysis_window.py
|
|
36
|
+
src/moleditpy/ui/angle_dialog.py
|
|
37
|
+
src/moleditpy/ui/app_state.py
|
|
38
|
+
src/moleditpy/ui/atom_item.py
|
|
39
|
+
src/moleditpy/ui/bond_item.py
|
|
40
|
+
src/moleditpy/ui/bond_length_dialog.py
|
|
41
|
+
src/moleditpy/ui/calculation_worker.py
|
|
42
|
+
src/moleditpy/ui/color_settings_dialog.py
|
|
43
|
+
src/moleditpy/ui/compute_engine.py
|
|
44
|
+
src/moleditpy/ui/compute_logic.py
|
|
45
|
+
src/moleditpy/ui/constrained_optimization_dialog.py
|
|
46
|
+
src/moleditpy/ui/custom_interactor_style.py
|
|
47
|
+
src/moleditpy/ui/custom_qt_interactor.py
|
|
48
|
+
src/moleditpy/ui/dialog_3d_picking_mixin.py
|
|
49
|
+
src/moleditpy/ui/dialog_logic.py
|
|
50
|
+
src/moleditpy/ui/dialog_manager.py
|
|
51
|
+
src/moleditpy/ui/dihedral_dialog.py
|
|
52
|
+
src/moleditpy/ui/edit_3d_logic.py
|
|
53
|
+
src/moleditpy/ui/edit_actions_logic.py
|
|
54
|
+
src/moleditpy/ui/export_logic.py
|
|
55
|
+
src/moleditpy/ui/main_window.py
|
|
56
|
+
src/moleditpy/ui/main_window_init.py
|
|
57
|
+
src/moleditpy/ui/mirror_dialog.py
|
|
58
|
+
src/moleditpy/ui/molecular_parsers.py
|
|
59
|
+
src/moleditpy/ui/molecular_scene_handler.py
|
|
60
|
+
src/moleditpy/ui/molecule_scene.py
|
|
61
|
+
src/moleditpy/ui/move_group_dialog.py
|
|
62
|
+
src/moleditpy/ui/periodic_table_dialog.py
|
|
63
|
+
src/moleditpy/ui/planarize_dialog.py
|
|
64
|
+
src/moleditpy/ui/project_io.py
|
|
65
|
+
src/moleditpy/ui/settings_dialog.py
|
|
66
|
+
src/moleditpy/ui/sip_isdeleted_safe.py
|
|
67
|
+
src/moleditpy/ui/string_importers.py
|
|
68
|
+
src/moleditpy/ui/template_preview_item.py
|
|
69
|
+
src/moleditpy/ui/template_preview_view.py
|
|
70
|
+
src/moleditpy/ui/translation_dialog.py
|
|
71
|
+
src/moleditpy/ui/ui_manager.py
|
|
72
|
+
src/moleditpy/ui/user_template_dialog.py
|
|
73
|
+
src/moleditpy/ui/view_3d_logic.py
|
|
74
|
+
src/moleditpy/ui/view_loaders.py
|
|
75
|
+
src/moleditpy/ui/zoomable_view.py
|
|
76
|
+
src/moleditpy/ui/settings_tabs/__init__.py
|
|
77
|
+
src/moleditpy/ui/settings_tabs/settings_2d_tab.py
|
|
78
|
+
src/moleditpy/ui/settings_tabs/settings_3d_tabs.py
|
|
79
|
+
src/moleditpy/ui/settings_tabs/settings_other_tab.py
|
|
80
|
+
src/moleditpy/ui/settings_tabs/settings_tab_base.py
|
|
81
|
+
src/moleditpy/utils/__init__.py
|
|
82
|
+
src/moleditpy/utils/constants.py
|
|
83
|
+
src/moleditpy/utils/sip_isdeleted_safe.py
|
|
84
|
+
src/moleditpy/utils/system_utils.py
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
MoleditPy — A Python-based molecular editing software
|
|
6
|
+
|
|
7
|
+
Author: Hiromichi Yokoyama
|
|
8
|
+
License: GPL-3.0 license
|
|
9
|
+
Repo: https://github.com/HiroYokoyama/python_molecular_editor
|
|
10
|
+
DOI: 10.5281/zenodo.17268532
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
"""Top-level package for moleditpy."""
|
|
14
|
+
|
|
15
|
+
import importlib.util
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
OBABEL_AVAILABLE = importlib.util.find_spec("openbabel") is not None
|
|
19
|
+
except ImportError:
|
|
20
|
+
OBABEL_AVAILABLE = False
|
|
File without changes
|
{moleditpy-3.0.0a2/src/moleditpy/modules → moleditpy-3.0.0a4/src/moleditpy/core}/mol_geometry.py
RENAMED
|
@@ -10,12 +10,11 @@ Repo: https://github.com/HiroYokoyama/python_molecular_editor
|
|
|
10
10
|
DOI: 10.5281/zenodo.17268532
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
import math
|
|
15
|
+
import logging
|
|
16
|
+
from collections import deque
|
|
17
|
+
from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Union
|
|
19
18
|
|
|
20
19
|
import numpy as np
|
|
21
20
|
|
|
@@ -23,8 +22,7 @@ import numpy as np
|
|
|
23
22
|
# Primitive geometry helpers
|
|
24
23
|
# ------------------------------------------------------------------
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
def calc_distance(pos1, pos2) -> float:
|
|
25
|
+
def calc_distance(pos1: Union[np.ndarray, Tuple[float, float, float], List[float]], pos2: Union[np.ndarray, Tuple[float, float, float], List[float]]) -> float:
|
|
28
26
|
"""Return the Euclidean distance between two 3-D positions.
|
|
29
27
|
|
|
30
28
|
Parameters
|
|
@@ -42,7 +40,11 @@ def calc_distance(pos1, pos2) -> float:
|
|
|
42
40
|
)
|
|
43
41
|
|
|
44
42
|
|
|
45
|
-
def calc_angle_deg(
|
|
43
|
+
def calc_angle_deg(
|
|
44
|
+
pos1: Union[np.ndarray, Tuple[float, float, float], List[float]],
|
|
45
|
+
pos2_vertex: Union[np.ndarray, Tuple[float, float, float], List[float]],
|
|
46
|
+
pos3: Union[np.ndarray, Tuple[float, float, float], List[float]],
|
|
47
|
+
) -> float:
|
|
46
48
|
"""Return the angle pos1–pos2_vertex–pos3 in degrees.
|
|
47
49
|
|
|
48
50
|
The angle is measured at *pos2_vertex* and is always in [0, 180].
|
|
@@ -76,7 +78,7 @@ def calc_angle_deg(pos1, pos2_vertex, pos3) -> float:
|
|
|
76
78
|
# ------------------------------------------------------------------
|
|
77
79
|
|
|
78
80
|
|
|
79
|
-
def get_connected_group(mol, start_atom, exclude=None):
|
|
81
|
+
def get_connected_group(mol: Any, start_atom: int, exclude: Optional[int] = None) -> Set[int]:
|
|
80
82
|
"""Return the set of atom indices reachable from *start_atom*
|
|
81
83
|
without passing through *exclude*.
|
|
82
84
|
|
|
@@ -122,7 +124,7 @@ def get_connected_group(mol, start_atom, exclude=None):
|
|
|
122
124
|
# ------------------------------------------------------------------
|
|
123
125
|
|
|
124
126
|
|
|
125
|
-
def rodrigues_rotate(v, axis, angle):
|
|
127
|
+
def rodrigues_rotate(v: np.ndarray, axis: np.ndarray, angle: float) -> np.ndarray:
|
|
126
128
|
"""Rotate vector *v* around a unit *axis* by *angle* radians.
|
|
127
129
|
|
|
128
130
|
Implements Rodrigues' rotation formula:
|
|
@@ -149,8 +151,13 @@ def rodrigues_rotate(v, axis, angle):
|
|
|
149
151
|
|
|
150
152
|
|
|
151
153
|
def adjust_bond_angle(
|
|
152
|
-
positions
|
|
153
|
-
|
|
154
|
+
positions: np.ndarray,
|
|
155
|
+
idx_a: int,
|
|
156
|
+
idx_b: int,
|
|
157
|
+
idx_c: int,
|
|
158
|
+
target_angle_deg: float,
|
|
159
|
+
atom_indices_to_move: Iterable[int],
|
|
160
|
+
) -> float:
|
|
154
161
|
"""Adjust the A–B–C bond angle to *target_angle_deg* using a
|
|
155
162
|
difference-based rotation.
|
|
156
163
|
|
|
@@ -242,7 +249,7 @@ def adjust_bond_angle(
|
|
|
242
249
|
# ------------------------------------------------------------------
|
|
243
250
|
|
|
244
251
|
|
|
245
|
-
def calculate_dihedral(positions, i1, i2, i3, i4):
|
|
252
|
+
def calculate_dihedral(positions: Any, i1: int, i2: int, i3: int, i4: int) -> float:
|
|
246
253
|
"""Compute the dihedral angle defined by four atom indices.
|
|
247
254
|
|
|
248
255
|
Parameters
|
|
@@ -312,7 +319,7 @@ _VALENCE_LIMITS = {
|
|
|
312
319
|
}
|
|
313
320
|
|
|
314
321
|
|
|
315
|
-
def is_problematic_valence(symbol, bond_count, charge=0):
|
|
322
|
+
def is_problematic_valence(symbol: str, bond_count: Union[int, float], charge: int = 0) -> bool:
|
|
316
323
|
"""Return ``True`` if the atom's total bond order exceeds its
|
|
317
324
|
typical maximum valence.
|
|
318
325
|
|
|
@@ -404,7 +411,7 @@ def inject_ez_stereo_to_mol_block(mol_block, rdkit_mol, bonds_data):
|
|
|
404
411
|
return "\n".join(mol_lines)
|
|
405
412
|
|
|
406
413
|
|
|
407
|
-
def identify_valence_problems(atoms_data, bonds_data):
|
|
414
|
+
def identify_valence_problems(atoms_data: Dict[int, Any], bonds_data: Dict[Tuple[int, int], Any]) -> List[int]:
|
|
408
415
|
"""Identify atoms with problematic valence.
|
|
409
416
|
|
|
410
417
|
Parameters
|
|
@@ -437,3 +444,148 @@ def identify_valence_problems(atoms_data, bonds_data):
|
|
|
437
444
|
problem_atom_ids.append(atom_id)
|
|
438
445
|
|
|
439
446
|
return problem_atom_ids
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def optimize_2d_coords(mol: Any) -> Dict[int, Tuple[float, float]]:
|
|
450
|
+
"""Generate 2D coordinates using RDKit and return a map of (x, y) tuples."""
|
|
451
|
+
from rdkit.Chem import AllChem
|
|
452
|
+
|
|
453
|
+
AllChem.Compute2DCoords(mol)
|
|
454
|
+
conf = mol.GetConformer()
|
|
455
|
+
new_positions = {}
|
|
456
|
+
for rdkit_atom in mol.GetAtoms():
|
|
457
|
+
if rdkit_atom.HasProp("_original_atom_id"):
|
|
458
|
+
original_id = rdkit_atom.GetIntProp("_original_atom_id")
|
|
459
|
+
pos = conf.GetAtomPosition(rdkit_atom.GetIdx())
|
|
460
|
+
new_positions[original_id] = (pos.x, pos.y)
|
|
461
|
+
return new_positions
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def calculate_best_fit_plane_projection(centered_positions, normal, centroid):
|
|
465
|
+
"""Project centered points orthogonally onto the plane defined by normal and centroid."""
|
|
466
|
+
projections = centered_positions - np.outer(
|
|
467
|
+
np.dot(centered_positions, normal), normal
|
|
468
|
+
)
|
|
469
|
+
return projections + centroid
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def rotate_2d_points(points_map, center_x, center_y, angle_degrees):
|
|
473
|
+
"""Rotate 2D points (atom_id -> (x, y)) around a center."""
|
|
474
|
+
rad = math.radians(angle_degrees)
|
|
475
|
+
cos_a = math.cos(rad)
|
|
476
|
+
sin_a = math.sin(rad)
|
|
477
|
+
new_positions = {}
|
|
478
|
+
for atom_id, (x, y) in points_map.items():
|
|
479
|
+
dx = x - center_x
|
|
480
|
+
dy = y - center_y
|
|
481
|
+
new_dx = dx * cos_a - dy * sin_a
|
|
482
|
+
new_dy = dx * sin_a + dy * cos_a
|
|
483
|
+
new_positions[atom_id] = (center_x + new_dx, center_y + new_dy)
|
|
484
|
+
return new_positions
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def resolve_2d_overlaps(
|
|
488
|
+
atom_ids,
|
|
489
|
+
positions_map,
|
|
490
|
+
adjacency_list,
|
|
491
|
+
overlap_threshold=0.5,
|
|
492
|
+
move_distance=20,
|
|
493
|
+
has_bond_check_func=None,
|
|
494
|
+
):
|
|
495
|
+
"""Detect and resolve overlapping atom groups in 2D.
|
|
496
|
+
|
|
497
|
+
Returns list of (atom_ids_set, translation_vector_tuple).
|
|
498
|
+
"""
|
|
499
|
+
overlapping_pairs = []
|
|
500
|
+
ids_list = list(atom_ids)
|
|
501
|
+
for i in range(len(ids_list)):
|
|
502
|
+
for j in range(i + 1, len(ids_list)):
|
|
503
|
+
id1 = ids_list[i]
|
|
504
|
+
id2 = ids_list[j]
|
|
505
|
+
|
|
506
|
+
# Skip directly bonded pairs
|
|
507
|
+
if has_bond_check_func and has_bond_check_func(id1, id2):
|
|
508
|
+
continue
|
|
509
|
+
|
|
510
|
+
p1 = positions_map[id1]
|
|
511
|
+
p2 = positions_map[id2]
|
|
512
|
+
dist = math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
|
|
513
|
+
if dist < overlap_threshold:
|
|
514
|
+
overlapping_pairs.append((id1, id2))
|
|
515
|
+
|
|
516
|
+
if not overlapping_pairs:
|
|
517
|
+
return []
|
|
518
|
+
|
|
519
|
+
# Union-Find for overlap groups
|
|
520
|
+
parent = {aid: aid for aid in atom_ids}
|
|
521
|
+
|
|
522
|
+
def find_set(aid):
|
|
523
|
+
if parent[aid] == aid:
|
|
524
|
+
return aid
|
|
525
|
+
parent[aid] = find_set(parent[aid])
|
|
526
|
+
return parent[aid]
|
|
527
|
+
|
|
528
|
+
def unite_sets(aid1, aid2):
|
|
529
|
+
root1 = find_set(aid1)
|
|
530
|
+
root2 = find_set(aid2)
|
|
531
|
+
if root1 != root2:
|
|
532
|
+
parent[root2] = root1
|
|
533
|
+
|
|
534
|
+
for id1, id2 in overlapping_pairs:
|
|
535
|
+
unite_sets(id1, id2)
|
|
536
|
+
|
|
537
|
+
groups_by_root = {}
|
|
538
|
+
for aid in atom_ids:
|
|
539
|
+
root = find_set(aid)
|
|
540
|
+
groups_by_root.setdefault(root, []).append(aid)
|
|
541
|
+
|
|
542
|
+
move_operations = []
|
|
543
|
+
processed_roots = set()
|
|
544
|
+
|
|
545
|
+
for root_id, group_ids in groups_by_root.items():
|
|
546
|
+
if root_id in processed_roots or len(group_ids) < 2:
|
|
547
|
+
continue
|
|
548
|
+
processed_roots.add(root_id)
|
|
549
|
+
|
|
550
|
+
# Split into fragments via BFS
|
|
551
|
+
fragments = []
|
|
552
|
+
visited = set()
|
|
553
|
+
group_set = set(group_ids)
|
|
554
|
+
for aid in group_ids:
|
|
555
|
+
if aid not in visited:
|
|
556
|
+
frag = set()
|
|
557
|
+
q = deque([aid])
|
|
558
|
+
visited.add(aid)
|
|
559
|
+
frag.add(aid)
|
|
560
|
+
while q:
|
|
561
|
+
curr = q.popleft()
|
|
562
|
+
for neighbor in adjacency_list.get(curr, []):
|
|
563
|
+
if neighbor in group_set and neighbor not in visited:
|
|
564
|
+
visited.add(neighbor)
|
|
565
|
+
frag.add(neighbor)
|
|
566
|
+
q.append(neighbor)
|
|
567
|
+
fragments.append(frag)
|
|
568
|
+
|
|
569
|
+
if len(fragments) < 2:
|
|
570
|
+
continue
|
|
571
|
+
|
|
572
|
+
# Find representative pair
|
|
573
|
+
rep_id1, rep_id2 = None, None
|
|
574
|
+
for i1, i2 in overlapping_pairs:
|
|
575
|
+
if find_set(i1) == root_id:
|
|
576
|
+
rep_id1, rep_id2 = i1, i2
|
|
577
|
+
break
|
|
578
|
+
|
|
579
|
+
if rep_id1 is None:
|
|
580
|
+
continue
|
|
581
|
+
|
|
582
|
+
frag1 = next((f for f in fragments if rep_id1 in f), None)
|
|
583
|
+
frag2 = next((f for f in fragments if rep_id2 in f), None)
|
|
584
|
+
if frag1 is None or frag2 is None or frag1 == frag2:
|
|
585
|
+
continue
|
|
586
|
+
|
|
587
|
+
ids_to_move = frag1 if rep_id1 > rep_id2 else frag2
|
|
588
|
+
# Move vector (-move_distance, move_distance)
|
|
589
|
+
move_operations.append((ids_to_move, (-move_distance, move_distance)))
|
|
590
|
+
|
|
591
|
+
return move_operations
|