MoleditPy 1.18.0__tar.gz → 2.0.0__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-1.18.0 → moleditpy-2.0.0}/PKG-INFO +3 -2
- {moleditpy-1.18.0 → moleditpy-2.0.0}/README.md +2 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/pyproject.toml +1 -2
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/MoleditPy.egg-info/PKG-INFO +3 -2
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/MoleditPy.egg-info/SOURCES.txt +1 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/align_plane_dialog.py +1 -1
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/alignment_dialog.py +1 -1
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/angle_dialog.py +2 -2
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/bond_length_dialog.py +2 -2
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/constants.py +1 -1
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/constrained_optimization_dialog.py +2 -2
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/dihedral_dialog.py +1 -1
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window.py +4 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_app_state.py +1 -1
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_edit_3d.py +7 -7
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_export.py +106 -49
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_main_init.py +68 -4
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_molecular_parsers.py +4 -3
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_project_io.py +2 -2
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_view_3d.py +13 -12
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_view_loaders.py +1 -1
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/molecule_scene.py +41 -13
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/planarize_dialog.py +1 -1
- moleditpy-2.0.0/src/moleditpy/modules/plugin_manager.py +85 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/settings_dialog.py +5 -23
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/user_template_dialog.py +9 -8
- {moleditpy-1.18.0 → moleditpy-2.0.0}/LICENSE +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/setup.cfg +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/MoleditPy.egg-info/entry_points.txt +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/MoleditPy.egg-info/requires.txt +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/MoleditPy.egg-info/top_level.txt +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/__init__.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/__main__.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/main.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/__init__.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/about_dialog.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/analysis_window.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/assets/icon.icns +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/assets/icon.ico +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/assets/icon.png +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/atom_item.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/bond_item.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/calculation_worker.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/color_settings_dialog.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/custom_interactor_style.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/custom_qt_interactor.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/dialog3_d_picking_mixin.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_compute.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_dialog_manager.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_edit_actions.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_string_importers.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/main_window_ui_manager.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/mirror_dialog.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/molecular_data.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/move_group_dialog.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/periodic_table_dialog.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/template_preview_item.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/template_preview_view.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/translation_dialog.py +0 -0
- {moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/zoomable_view.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
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
|
|
@@ -684,7 +684,6 @@ Classifier: Programming Language :: Python :: 3
|
|
|
684
684
|
Classifier: Operating System :: OS Independent
|
|
685
685
|
Classifier: Intended Audience :: Science/Research
|
|
686
686
|
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
687
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
688
687
|
Classifier: Programming Language :: Python :: 3.9
|
|
689
688
|
Classifier: Programming Language :: Python :: 3.10
|
|
690
689
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -760,6 +759,7 @@ This application combines a modern GUI built with **PyQt6**, powerful cheminform
|
|
|
760
759
|
* Import structures from **MOL/SDF** files or **SMILES** strings.
|
|
761
760
|
* Export 3D structures to **MOL** or **XYZ** formats, which are compatible with most DFT calculation software.
|
|
762
761
|
* Export 2D and 3D views as high-resolution PNG images.
|
|
762
|
+
* **Plugin System:** Extend functionality with Python scripts. Place custom scripts in `~/.moleditpy/plugins` to add new features to the "Plugin" menu.
|
|
763
763
|
|
|
764
764
|
## Installation and Execution
|
|
765
765
|
|
|
@@ -874,6 +874,7 @@ This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**.
|
|
|
874
874
|
* **MOL/SDF**ファイルや**SMILES**文字列から構造をインポートできます。
|
|
875
875
|
* 3D構造を**MOL**または**XYZ**形式でエクスポートでき、これらは多くのDFT計算ソフトウェアと互換性があります。
|
|
876
876
|
* 2Dおよび3Dビューを高解像度のPNG画像としてエクスポートできます。
|
|
877
|
+
* **プラグインシステム:** Pythonスクリプトで機能を拡張できます。`~/.moleditpy/plugins` にスクリプトを配置することで、「Plugin」メニューに独自の機能を追加できます。
|
|
877
878
|
|
|
878
879
|
## インストールと実行
|
|
879
880
|
|
|
@@ -57,6 +57,7 @@ This application combines a modern GUI built with **PyQt6**, powerful cheminform
|
|
|
57
57
|
* Import structures from **MOL/SDF** files or **SMILES** strings.
|
|
58
58
|
* Export 3D structures to **MOL** or **XYZ** formats, which are compatible with most DFT calculation software.
|
|
59
59
|
* Export 2D and 3D views as high-resolution PNG images.
|
|
60
|
+
* **Plugin System:** Extend functionality with Python scripts. Place custom scripts in `~/.moleditpy/plugins` to add new features to the "Plugin" menu.
|
|
60
61
|
|
|
61
62
|
## Installation and Execution
|
|
62
63
|
|
|
@@ -171,6 +172,7 @@ This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**.
|
|
|
171
172
|
* **MOL/SDF**ファイルや**SMILES**文字列から構造をインポートできます。
|
|
172
173
|
* 3D構造を**MOL**または**XYZ**形式でエクスポートでき、これらは多くのDFT計算ソフトウェアと互換性があります。
|
|
173
174
|
* 2Dおよび3Dビューを高解像度のPNG画像としてエクスポートできます。
|
|
175
|
+
* **プラグインシステム:** Pythonスクリプトで機能を拡張できます。`~/.moleditpy/plugins` にスクリプトを配置することで、「Plugin」メニューに独自の機能を追加できます。
|
|
174
176
|
|
|
175
177
|
## インストールと実行
|
|
176
178
|
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "MoleditPy"
|
|
7
7
|
|
|
8
|
-
version = "
|
|
8
|
+
version = "2.0.0"
|
|
9
9
|
|
|
10
10
|
license = {file = "LICENSE"}
|
|
11
11
|
|
|
@@ -24,7 +24,6 @@ classifiers = [
|
|
|
24
24
|
"Operating System :: OS Independent",
|
|
25
25
|
"Intended Audience :: Science/Research",
|
|
26
26
|
"Topic :: Scientific/Engineering :: Chemistry",
|
|
27
|
-
"Programming Language :: Python :: 3.8",
|
|
28
27
|
"Programming Language :: Python :: 3.9",
|
|
29
28
|
"Programming Language :: Python :: 3.10",
|
|
30
29
|
"Programming Language :: Python :: 3.11",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
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
|
|
@@ -684,7 +684,6 @@ Classifier: Programming Language :: Python :: 3
|
|
|
684
684
|
Classifier: Operating System :: OS Independent
|
|
685
685
|
Classifier: Intended Audience :: Science/Research
|
|
686
686
|
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
687
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
688
687
|
Classifier: Programming Language :: Python :: 3.9
|
|
689
688
|
Classifier: Programming Language :: Python :: 3.10
|
|
690
689
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -760,6 +759,7 @@ This application combines a modern GUI built with **PyQt6**, powerful cheminform
|
|
|
760
759
|
* Import structures from **MOL/SDF** files or **SMILES** strings.
|
|
761
760
|
* Export 3D structures to **MOL** or **XYZ** formats, which are compatible with most DFT calculation software.
|
|
762
761
|
* Export 2D and 3D views as high-resolution PNG images.
|
|
762
|
+
* **Plugin System:** Extend functionality with Python scripts. Place custom scripts in `~/.moleditpy/plugins` to add new features to the "Plugin" menu.
|
|
763
763
|
|
|
764
764
|
## Installation and Execution
|
|
765
765
|
|
|
@@ -874,6 +874,7 @@ This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**.
|
|
|
874
874
|
* **MOL/SDF**ファイルや**SMILES**文字列から構造をインポートできます。
|
|
875
875
|
* 3D構造を**MOL**または**XYZ**形式でエクスポートでき、これらは多くのDFT計算ソフトウェアと互換性があります。
|
|
876
876
|
* 2Dおよび3Dビューを高解像度のPNG画像としてエクスポートできます。
|
|
877
|
+
* **プラグインシステム:** Pythonスクリプトで機能を拡張できます。`~/.moleditpy/plugins` にスクリプトを配置することで、「Plugin」メニューに独自の機能を追加できます。
|
|
877
878
|
|
|
878
879
|
## インストールと実行
|
|
879
880
|
|
|
@@ -53,6 +53,7 @@ src/moleditpy/modules/molecule_scene.py
|
|
|
53
53
|
src/moleditpy/modules/move_group_dialog.py
|
|
54
54
|
src/moleditpy/modules/periodic_table_dialog.py
|
|
55
55
|
src/moleditpy/modules/planarize_dialog.py
|
|
56
|
+
src/moleditpy/modules/plugin_manager.py
|
|
56
57
|
src/moleditpy/modules/settings_dialog.py
|
|
57
58
|
src/moleditpy/modules/template_preview_item.py
|
|
58
59
|
src/moleditpy/modules/template_preview_view.py
|
|
@@ -202,7 +202,7 @@ class AngleDialog(Dialog3DPickingMixin, QDialog):
|
|
|
202
202
|
for label_actor in self.selection_labels:
|
|
203
203
|
try:
|
|
204
204
|
self.main_window.plotter.remove_actor(label_actor)
|
|
205
|
-
except:
|
|
205
|
+
except Exception:
|
|
206
206
|
pass
|
|
207
207
|
self.selection_labels = []
|
|
208
208
|
|
|
@@ -212,7 +212,7 @@ class AngleDialog(Dialog3DPickingMixin, QDialog):
|
|
|
212
212
|
for label_actor in self.selection_labels:
|
|
213
213
|
try:
|
|
214
214
|
self.main_window.plotter.remove_actor(label_actor)
|
|
215
|
-
except:
|
|
215
|
+
except Exception:
|
|
216
216
|
pass
|
|
217
217
|
self.selection_labels = []
|
|
218
218
|
|
|
@@ -190,7 +190,7 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog):
|
|
|
190
190
|
for label_actor in self.selection_labels:
|
|
191
191
|
try:
|
|
192
192
|
self.main_window.plotter.remove_actor(label_actor)
|
|
193
|
-
except:
|
|
193
|
+
except Exception:
|
|
194
194
|
pass
|
|
195
195
|
self.selection_labels = []
|
|
196
196
|
|
|
@@ -200,7 +200,7 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog):
|
|
|
200
200
|
for label_actor in self.selection_labels:
|
|
201
201
|
try:
|
|
202
202
|
self.main_window.plotter.remove_actor(label_actor)
|
|
203
|
-
except:
|
|
203
|
+
except Exception:
|
|
204
204
|
pass
|
|
205
205
|
self.selection_labels = []
|
|
206
206
|
|
{moleditpy-1.18.0 → moleditpy-2.0.0}/src/moleditpy/modules/constrained_optimization_dialog.py
RENAMED
|
@@ -394,7 +394,7 @@ class ConstrainedOptimizationDialog(Dialog3DPickingMixin, QDialog):
|
|
|
394
394
|
for label_actor in self.constraint_labels:
|
|
395
395
|
try:
|
|
396
396
|
self.main_window.plotter.remove_actor(label_actor)
|
|
397
|
-
except:
|
|
397
|
+
except Exception:
|
|
398
398
|
pass
|
|
399
399
|
self.constraint_labels = []
|
|
400
400
|
|
|
@@ -595,7 +595,7 @@ class ConstrainedOptimizationDialog(Dialog3DPickingMixin, QDialog):
|
|
|
595
595
|
for label_actor in self.selection_labels:
|
|
596
596
|
try:
|
|
597
597
|
self.main_window.plotter.remove_actor(label_actor)
|
|
598
|
-
except:
|
|
598
|
+
except Exception:
|
|
599
599
|
pass
|
|
600
600
|
self.selection_labels = []
|
|
601
601
|
|
|
@@ -211,6 +211,10 @@ class MainWindow(QMainWindow):
|
|
|
211
211
|
# --- MOVED TO main_window_main_init.py ---
|
|
212
212
|
return self.main_window_main_init.init_menu_bar()
|
|
213
213
|
|
|
214
|
+
def update_plugin_menu(self, plugin_menu):
|
|
215
|
+
# --- MOVED TO main_window_main_init.py ---
|
|
216
|
+
return self.main_window_main_init.update_plugin_menu(plugin_menu)
|
|
217
|
+
|
|
214
218
|
def init_worker_thread(self):
|
|
215
219
|
# --- MOVED TO main_window_main_init.py ---
|
|
216
220
|
return self.main_window_main_init.init_worker_thread()
|
|
@@ -574,7 +574,7 @@ class MainWindowAppState(object):
|
|
|
574
574
|
inchi_key = Chem.MolToInchiKey(self.current_mol)
|
|
575
575
|
json_data["identifiers"]["inchi"] = inchi
|
|
576
576
|
json_data["identifiers"]["inchi_key"] = inchi_key
|
|
577
|
-
except:
|
|
577
|
+
except Exception:
|
|
578
578
|
pass # InChI生成に失敗した場合は無視
|
|
579
579
|
|
|
580
580
|
except Exception as e:
|
|
@@ -119,7 +119,7 @@ class MainWindowEdit3d(object):
|
|
|
119
119
|
for dialog in dialogs_to_close:
|
|
120
120
|
try:
|
|
121
121
|
dialog.close()
|
|
122
|
-
except:
|
|
122
|
+
except Exception:
|
|
123
123
|
pass
|
|
124
124
|
self.active_3d_dialogs.clear()
|
|
125
125
|
|
|
@@ -169,7 +169,7 @@ class MainWindowEdit3d(object):
|
|
|
169
169
|
try:
|
|
170
170
|
# 既存の測定ラベルを削除
|
|
171
171
|
self.plotter.remove_actor('measurement_labels')
|
|
172
|
-
except:
|
|
172
|
+
except Exception:
|
|
173
173
|
pass
|
|
174
174
|
|
|
175
175
|
if not self.measurement_labels or not self.current_mol:
|
|
@@ -208,7 +208,7 @@ class MainWindowEdit3d(object):
|
|
|
208
208
|
self.measurement_labels.clear()
|
|
209
209
|
try:
|
|
210
210
|
self.plotter.remove_actor('measurement_labels')
|
|
211
|
-
except:
|
|
211
|
+
except Exception:
|
|
212
212
|
pass
|
|
213
213
|
|
|
214
214
|
# 2Dビューの測定ラベルも削除
|
|
@@ -219,7 +219,7 @@ class MainWindowEdit3d(object):
|
|
|
219
219
|
try:
|
|
220
220
|
self.plotter.remove_actor(self.measurement_text_actor)
|
|
221
221
|
self.measurement_text_actor = None
|
|
222
|
-
except:
|
|
222
|
+
except Exception:
|
|
223
223
|
pass
|
|
224
224
|
|
|
225
225
|
self.plotter.render()
|
|
@@ -434,7 +434,7 @@ class MainWindowEdit3d(object):
|
|
|
434
434
|
if self.measurement_text_actor:
|
|
435
435
|
try:
|
|
436
436
|
self.plotter.remove_actor(self.measurement_text_actor)
|
|
437
|
-
except:
|
|
437
|
+
except Exception:
|
|
438
438
|
pass
|
|
439
439
|
|
|
440
440
|
if not measurement_lines:
|
|
@@ -453,7 +453,7 @@ class MainWindowEdit3d(object):
|
|
|
453
453
|
text_color = 'black' if luminance > 128 else 'white'
|
|
454
454
|
else:
|
|
455
455
|
text_color = 'white'
|
|
456
|
-
except:
|
|
456
|
+
except Exception:
|
|
457
457
|
text_color = 'white'
|
|
458
458
|
|
|
459
459
|
# 左上に表示(小さな等幅フォント)
|
|
@@ -496,7 +496,7 @@ class MainWindowEdit3d(object):
|
|
|
496
496
|
try:
|
|
497
497
|
# 既存の選択ハイライトを削除
|
|
498
498
|
self.plotter.remove_actor('selection_highlight')
|
|
499
|
-
except:
|
|
499
|
+
except Exception:
|
|
500
500
|
pass
|
|
501
501
|
|
|
502
502
|
if not self.selected_atoms_3d or not self.current_mol:
|
|
@@ -178,11 +178,6 @@ class MainWindowExport(object):
|
|
|
178
178
|
except Exception as e:
|
|
179
179
|
self.statusBar().showMessage(f"Error exporting OBJ/MTL: {e}")
|
|
180
180
|
|
|
181
|
-
return meshes_with_colors
|
|
182
|
-
|
|
183
|
-
except Exception:
|
|
184
|
-
return []
|
|
185
|
-
|
|
186
181
|
|
|
187
182
|
|
|
188
183
|
def create_multi_material_obj(self, meshes_with_colors, obj_path, mtl_path):
|
|
@@ -199,17 +194,17 @@ class MainWindowExport(object):
|
|
|
199
194
|
material_name = f"material_{i}_{mesh_data['name'].replace(' ', '_')}"
|
|
200
195
|
|
|
201
196
|
mtl_file.write(f"newmtl {material_name}\n")
|
|
202
|
-
mtl_file.write("Ka 0.2 0.2 0.2\n") # Ambient
|
|
197
|
+
mtl_file.write(f"Ka 0.2 0.2 0.2\n") # Ambient
|
|
203
198
|
mtl_file.write(f"Kd {color[0]/255.0:.3f} {color[1]/255.0:.3f} {color[2]/255.0:.3f}\n") # Diffuse
|
|
204
|
-
mtl_file.write("Ks 0.5 0.5 0.5\n") # Specular
|
|
205
|
-
mtl_file.write("Ns 32.0\n") # Specular exponent
|
|
206
|
-
mtl_file.write("illum 2\n") # Illumination model
|
|
207
|
-
mtl_file.write("\n")
|
|
199
|
+
mtl_file.write(f"Ks 0.5 0.5 0.5\n") # Specular
|
|
200
|
+
mtl_file.write(f"Ns 32.0\n") # Specular exponent
|
|
201
|
+
mtl_file.write(f"illum 2\n") # Illumination model
|
|
202
|
+
mtl_file.write(f"\n")
|
|
208
203
|
|
|
209
204
|
# OBJファイルを作成
|
|
210
205
|
with open(obj_path, 'w') as obj_file:
|
|
211
|
-
obj_file.write("# OBJ file with multiple materials\n")
|
|
212
|
-
obj_file.write("# Generated with individual object colors\n")
|
|
206
|
+
obj_file.write(f"# OBJ file with multiple materials\n")
|
|
207
|
+
obj_file.write(f"# Generated with individual object colors\n")
|
|
213
208
|
obj_file.write(f"mtllib {os.path.basename(mtl_path)}\n\n")
|
|
214
209
|
|
|
215
210
|
vertex_offset = 1 # OBJファイルの頂点インデックスは1から始まる
|
|
@@ -229,6 +224,7 @@ class MainWindowExport(object):
|
|
|
229
224
|
obj_file.write(f"v {point[0]:.6f} {point[1]:.6f} {point[2]:.6f}\n")
|
|
230
225
|
|
|
231
226
|
# 面を書き込み
|
|
227
|
+
faces_written = 0
|
|
232
228
|
for j in range(mesh.n_cells):
|
|
233
229
|
cell = mesh.get_cell(j)
|
|
234
230
|
if cell.type == 5: # VTK_TRIANGLE
|
|
@@ -237,6 +233,25 @@ class MainWindowExport(object):
|
|
|
237
233
|
v2 = points_in_cell[1] + vertex_offset
|
|
238
234
|
v3 = points_in_cell[2] + vertex_offset
|
|
239
235
|
obj_file.write(f"f {v1} {v2} {v3}\n")
|
|
236
|
+
faces_written += 1
|
|
237
|
+
elif cell.type == 6: # VTK_TRIANGLE_STRIP
|
|
238
|
+
# Triangle strips share vertices between adjacent triangles
|
|
239
|
+
# For n points, we get (n-2) triangles
|
|
240
|
+
points_in_cell = cell.point_ids
|
|
241
|
+
n_points = len(points_in_cell)
|
|
242
|
+
for k in range(n_points - 2):
|
|
243
|
+
if k % 2 == 0:
|
|
244
|
+
# Even triangles: use points k, k+1, k+2
|
|
245
|
+
v1 = points_in_cell[k] + vertex_offset
|
|
246
|
+
v2 = points_in_cell[k+1] + vertex_offset
|
|
247
|
+
v3 = points_in_cell[k+2] + vertex_offset
|
|
248
|
+
else:
|
|
249
|
+
# Odd triangles: reverse winding to maintain consistent orientation
|
|
250
|
+
v1 = points_in_cell[k+1] + vertex_offset
|
|
251
|
+
v2 = points_in_cell[k] + vertex_offset
|
|
252
|
+
v3 = points_in_cell[k+2] + vertex_offset
|
|
253
|
+
obj_file.write(f"f {v1} {v2} {v3}\n")
|
|
254
|
+
faces_written += 1
|
|
240
255
|
elif cell.type == 9: # VTK_QUAD
|
|
241
256
|
points_in_cell = cell.point_ids
|
|
242
257
|
v1 = points_in_cell[0] + vertex_offset
|
|
@@ -244,10 +259,12 @@ class MainWindowExport(object):
|
|
|
244
259
|
v3 = points_in_cell[2] + vertex_offset
|
|
245
260
|
v4 = points_in_cell[3] + vertex_offset
|
|
246
261
|
obj_file.write(f"f {v1} {v2} {v3} {v4}\n")
|
|
262
|
+
faces_written += 1
|
|
247
263
|
|
|
248
|
-
vertex_offset += mesh.n_points
|
|
249
|
-
obj_file.write("\n")
|
|
250
264
|
|
|
265
|
+
vertex_offset += mesh.n_points
|
|
266
|
+
obj_file.write(f"\n")
|
|
267
|
+
|
|
251
268
|
except Exception as e:
|
|
252
269
|
raise Exception(f"Failed to create multi-material OBJ: {e}")
|
|
253
270
|
|
|
@@ -310,24 +327,25 @@ class MainWindowExport(object):
|
|
|
310
327
|
# VTKアクターからポリデータを取得する複数の方法を試行
|
|
311
328
|
mesh = None
|
|
312
329
|
|
|
313
|
-
# 方法1: mapperのinputから取得
|
|
330
|
+
# 方法1: mapperのinputから取得 (Improved)
|
|
331
|
+
mapper = None
|
|
314
332
|
if hasattr(actor, 'mapper') and actor.mapper is not None:
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
333
|
+
mapper = actor.mapper
|
|
334
|
+
elif hasattr(actor, 'GetMapper'):
|
|
335
|
+
mapper = actor.GetMapper()
|
|
336
|
+
|
|
337
|
+
if mapper is not None:
|
|
338
|
+
if hasattr(mapper, 'input') and mapper.input is not None:
|
|
339
|
+
mesh = mapper.input
|
|
340
|
+
elif hasattr(mapper, 'GetInput') and mapper.GetInput() is not None:
|
|
341
|
+
mesh = mapper.GetInput()
|
|
342
|
+
elif hasattr(mapper, 'GetInputAsDataSet'):
|
|
343
|
+
mesh = mapper.GetInputAsDataSet()
|
|
319
344
|
|
|
320
345
|
# 方法2: PyVistaプロッターの内部データから取得
|
|
321
346
|
if mesh is None and actor_name in self.plotter.mesh:
|
|
322
347
|
mesh = self.plotter.mesh[actor_name]
|
|
323
348
|
|
|
324
|
-
# 方法3: PyVistaのメッシュデータベースから検索
|
|
325
|
-
if mesh is None:
|
|
326
|
-
for mesh_name, mesh_data in self.plotter.mesh.items():
|
|
327
|
-
if mesh_data is not None and mesh_data.n_points > 0:
|
|
328
|
-
mesh = mesh_data
|
|
329
|
-
break
|
|
330
|
-
|
|
331
349
|
if mesh is not None and hasattr(mesh, 'n_points') and mesh.n_points > 0:
|
|
332
350
|
# PyVistaメッシュに変換(必要な場合)
|
|
333
351
|
if not isinstance(mesh, pv.PolyData):
|
|
@@ -391,23 +409,26 @@ class MainWindowExport(object):
|
|
|
391
409
|
# VTKアクターからポリデータを取得する複数の方法を試行
|
|
392
410
|
mesh = None
|
|
393
411
|
|
|
394
|
-
# 方法1: mapperのinputから取得
|
|
412
|
+
# 方法1: mapperのinputから取得 (Improved)
|
|
413
|
+
mapper = None
|
|
395
414
|
if hasattr(actor, 'mapper') and actor.mapper is not None:
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
415
|
+
mapper = actor.mapper
|
|
416
|
+
elif hasattr(actor, 'GetMapper'):
|
|
417
|
+
mapper = actor.GetMapper()
|
|
418
|
+
|
|
419
|
+
if mapper is not None:
|
|
420
|
+
if hasattr(mapper, 'input') and mapper.input is not None:
|
|
421
|
+
mesh = mapper.input
|
|
422
|
+
elif hasattr(mapper, 'GetInput') and mapper.GetInput() is not None:
|
|
423
|
+
mesh = mapper.GetInput()
|
|
424
|
+
elif hasattr(mapper, 'GetInputAsDataSet'):
|
|
425
|
+
mesh = mapper.GetInputAsDataSet()
|
|
400
426
|
|
|
401
427
|
# 方法2: PyVistaプロッターの内部データから取得
|
|
402
428
|
if mesh is None and actor_name in self.plotter.mesh:
|
|
403
429
|
mesh = self.plotter.mesh[actor_name]
|
|
404
430
|
|
|
405
|
-
# 方法3:
|
|
406
|
-
if mesh is None:
|
|
407
|
-
for mesh_name, mesh_data in self.plotter.mesh.items():
|
|
408
|
-
if mesh_data is not None and mesh_data.n_points > 0:
|
|
409
|
-
mesh = mesh_data
|
|
410
|
-
break
|
|
431
|
+
# 方法3: Removed unsafe fallback
|
|
411
432
|
|
|
412
433
|
if mesh is not None and hasattr(mesh, 'n_points') and mesh.n_points > 0:
|
|
413
434
|
# PyVistaメッシュに変換(必要な場合)
|
|
@@ -447,17 +468,26 @@ class MainWindowExport(object):
|
|
|
447
468
|
actors = renderer.actors
|
|
448
469
|
|
|
449
470
|
actor_count = 0
|
|
471
|
+
|
|
450
472
|
for actor_name, actor in actors.items():
|
|
451
473
|
try:
|
|
452
474
|
# VTKアクターからポリデータを取得
|
|
453
475
|
mesh = None
|
|
454
476
|
|
|
455
|
-
# 方法1: mapperのinputから取得
|
|
477
|
+
# 方法1: mapperのinputから取得 (Improved)
|
|
478
|
+
mapper = None
|
|
456
479
|
if hasattr(actor, 'mapper') and actor.mapper is not None:
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
480
|
+
mapper = actor.mapper
|
|
481
|
+
elif hasattr(actor, 'GetMapper'):
|
|
482
|
+
mapper = actor.GetMapper()
|
|
483
|
+
|
|
484
|
+
if mapper is not None:
|
|
485
|
+
if hasattr(mapper, 'input') and mapper.input is not None:
|
|
486
|
+
mesh = mapper.input
|
|
487
|
+
elif hasattr(mapper, 'GetInput') and mapper.GetInput() is not None:
|
|
488
|
+
mesh = mapper.GetInput()
|
|
489
|
+
elif hasattr(mapper, 'GetInputAsDataSet'):
|
|
490
|
+
mesh = mapper.GetInputAsDataSet()
|
|
461
491
|
|
|
462
492
|
# 方法2: PyVistaプロッターの内部データから取得
|
|
463
493
|
if mesh is None and actor_name in self.plotter.mesh:
|
|
@@ -484,7 +514,7 @@ class MainWindowExport(object):
|
|
|
484
514
|
if prop is not None:
|
|
485
515
|
vtk_color = prop.GetColor()
|
|
486
516
|
color = [int(c * 255) for c in vtk_color]
|
|
487
|
-
except:
|
|
517
|
+
except Exception:
|
|
488
518
|
# 色取得に失敗した場合はデフォルト色をそのまま使用
|
|
489
519
|
pass
|
|
490
520
|
|
|
@@ -513,6 +543,16 @@ class MainWindowExport(object):
|
|
|
513
543
|
# 単一の colors 配列があればそれを使う
|
|
514
544
|
elif 'colors' in pd:
|
|
515
545
|
colors = np.asarray(pd['colors'])
|
|
546
|
+
|
|
547
|
+
# cell_dataのcolorsも確認(Tubeフィルタなどはcell_dataに色を持つ場合がある)
|
|
548
|
+
if colors is None and 'colors' in mesh_copy.cell_data:
|
|
549
|
+
try:
|
|
550
|
+
# cell_dataをpoint_dataに変換
|
|
551
|
+
temp_mesh = mesh_copy.cell_data_to_point_data()
|
|
552
|
+
if 'colors' in temp_mesh.point_data:
|
|
553
|
+
colors = np.asarray(temp_mesh.point_data['colors'])
|
|
554
|
+
except Exception:
|
|
555
|
+
pass
|
|
516
556
|
|
|
517
557
|
if colors is not None and colors.size > 0:
|
|
518
558
|
# 整数に変換。colors が 0-1 の float の場合は 255 倍して正規化する。
|
|
@@ -539,18 +579,26 @@ class MainWindowExport(object):
|
|
|
539
579
|
|
|
540
580
|
# 一意な色ごとにサブメッシュを抽出して追加
|
|
541
581
|
unique_colors, inverse = np.unique(colors_int, axis=0, return_inverse=True)
|
|
582
|
+
|
|
583
|
+
split_success = False
|
|
542
584
|
if unique_colors.shape[0] > 1:
|
|
543
585
|
for uc_idx, uc in enumerate(unique_colors):
|
|
544
586
|
point_inds = np.where(inverse == uc_idx)[0]
|
|
545
587
|
if point_inds.size == 0:
|
|
546
588
|
continue
|
|
547
589
|
try:
|
|
548
|
-
|
|
590
|
+
# Use temp_mesh if available (has point data), else mesh_copy
|
|
591
|
+
target_mesh = temp_mesh if 'temp_mesh' in locals() else mesh_copy
|
|
592
|
+
|
|
593
|
+
# extract_points with adjacent_cells=False to avoid pulling in neighbors
|
|
594
|
+
submesh = target_mesh.extract_points(point_inds, adjacent_cells=False)
|
|
595
|
+
|
|
549
596
|
except Exception:
|
|
550
597
|
# extract_points が利用できない場合はスキップ
|
|
551
598
|
continue
|
|
552
599
|
if submesh is None or getattr(submesh, 'n_points', 0) == 0:
|
|
553
600
|
continue
|
|
601
|
+
|
|
554
602
|
color_rgb = [int(uc[0]), int(uc[1]), int(uc[2])]
|
|
555
603
|
meshes_with_colors.append({
|
|
556
604
|
'mesh': submesh,
|
|
@@ -559,13 +607,22 @@ class MainWindowExport(object):
|
|
|
559
607
|
'type': 'display_actor',
|
|
560
608
|
'actor_name': actor_name
|
|
561
609
|
})
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
610
|
+
split_success = True
|
|
611
|
+
|
|
612
|
+
if split_success:
|
|
613
|
+
actor_count += 1
|
|
614
|
+
# 分割に成功したので以下の通常追加は行わない
|
|
615
|
+
continue
|
|
616
|
+
# If splitting failed (no submeshes added), fall through to default
|
|
617
|
+
else:
|
|
618
|
+
# 色が1色のみの場合は、その色を使用してメッシュ全体を出力
|
|
619
|
+
uc = unique_colors[0]
|
|
620
|
+
color = [int(uc[0]), int(uc[1]), int(uc[2])]
|
|
621
|
+
# ここでは continue せず、下のデフォルト追加処理に任せる(colorを更新したため)
|
|
565
622
|
except Exception:
|
|
566
623
|
# 分割処理に失敗した場合はフォールバックで単体メッシュを追加
|
|
567
624
|
pass
|
|
568
|
-
|
|
625
|
+
|
|
569
626
|
meshes_with_colors.append({
|
|
570
627
|
'mesh': mesh_copy,
|
|
571
628
|
'color': color,
|
|
@@ -577,9 +634,9 @@ class MainWindowExport(object):
|
|
|
577
634
|
actor_count += 1
|
|
578
635
|
|
|
579
636
|
except Exception as e:
|
|
580
|
-
print(f"Error processing actor {actor_name}: {e}")
|
|
581
637
|
continue
|
|
582
638
|
|
|
639
|
+
|
|
583
640
|
return meshes_with_colors
|
|
584
641
|
|
|
585
642
|
except Exception as e:
|