automod 0.0.1__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.
- automod-0.0.1/LICENSE.txt +19 -0
- automod-0.0.1/PKG-INFO +21 -0
- automod-0.0.1/README.md +2 -0
- automod-0.0.1/pyproject.toml +32 -0
- automod-0.0.1/src/automod/__init__.py +0 -0
- automod-0.0.1/src/automod/automod.py +25 -0
- automod-0.0.1/src/automod/csview.py +120 -0
- automod-0.0.1/src/automod/curvescene.py +37 -0
- automod-0.0.1/src/automod/curveview.py +326 -0
- automod-0.0.1/src/automod/gridline.py +103 -0
- automod-0.0.1/src/automod/gridpoint.py +100 -0
- automod-0.0.1/src/automod/helixpoint.py +240 -0
- automod-0.0.1/src/automod/icons/app_icon.png +0 -0
- automod-0.0.1/src/automod/icons/arrow_black.png +0 -0
- automod-0.0.1/src/automod/icons/arrow_blue.png +0 -0
- automod-0.0.1/src/automod/icons/arrow_green.png +0 -0
- automod-0.0.1/src/automod/icons/arrow_red.png +0 -0
- automod-0.0.1/src/automod/icons/connect.png +0 -0
- automod-0.0.1/src/automod/icons/disconnect_next.png +0 -0
- automod-0.0.1/src/automod/icons/disconnect_prev.png +0 -0
- automod-0.0.1/src/automod/icons/honeycomb.png +0 -0
- automod-0.0.1/src/automod/icons/import.png +0 -0
- automod-0.0.1/src/automod/icons/insert_plus.png +0 -0
- automod-0.0.1/src/automod/icons/interpolate.png +0 -0
- automod-0.0.1/src/automod/icons/json.png +0 -0
- automod-0.0.1/src/automod/icons/print.png +0 -0
- automod-0.0.1/src/automod/icons/renum.png +0 -0
- automod-0.0.1/src/automod/icons/restore.png +0 -0
- automod-0.0.1/src/automod/icons/rotate_black.png +0 -0
- automod-0.0.1/src/automod/icons/rotate_blue.png +0 -0
- automod-0.0.1/src/automod/icons/rotate_green.png +0 -0
- automod-0.0.1/src/automod/icons/rotate_red.png +0 -0
- automod-0.0.1/src/automod/icons/save.png +0 -0
- automod-0.0.1/src/automod/icons/square.png +0 -0
- automod-0.0.1/src/automod/icons/trash.png +0 -0
- automod-0.0.1/src/automod/icons/zoom.png +0 -0
- automod-0.0.1/src/automod/icons.py +5368 -0
- automod-0.0.1/src/automod/icons.qrc +27 -0
- automod-0.0.1/src/automod/mainwindow.py +1628 -0
- automod-0.0.1/src/automod/nodepoint.py +209 -0
- automod-0.0.1/src/automod/pathcurve.py +698 -0
- automod-0.0.1/src/automod/pathline.py +71 -0
- automod-0.0.1/src/automod/storage3d.py +1007 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2025 Elina Heikkilä
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
automod-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: automod
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Automated modification and crossover placement for the design of curved DNA origami structures.
|
|
5
|
+
Project-URL: Homepage, https://github.com/AutoMod-DNA/automod
|
|
6
|
+
Project-URL: Issues, https://github.com/AutoMod-DNA/automod/issues
|
|
7
|
+
Author-email: Elina Heikkilä <elina.s.heikkila@aalto.fi>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE.txt
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Requires-Dist: numpy
|
|
16
|
+
Requires-Dist: pyside6
|
|
17
|
+
Requires-Dist: scipy
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# AutoMod
|
|
21
|
+
*Auto*mated *Mod*ification and crossover placement for the design of curved DNA origami structures.
|
automod-0.0.1/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling >= 1.26"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
[project]
|
|
5
|
+
name = "automod"
|
|
6
|
+
version = "0.0.1"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name="Elina Heikkilä", email="elina.s.heikkila@aalto.fi" },
|
|
9
|
+
]
|
|
10
|
+
description = "Automated modification and crossover placement for the design of curved DNA origami structures."
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
]
|
|
19
|
+
license = "MIT"
|
|
20
|
+
license-files = ["LICEN[CS]E*"]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"PySide6",
|
|
23
|
+
"numpy",
|
|
24
|
+
"scipy",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/AutoMod-DNA/automod"
|
|
29
|
+
Issues = "https://github.com/AutoMod-DNA/automod/issues"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
automod = "automod:main"
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from PySide6.QtCore import Qt
|
|
4
|
+
from PySide6.QtGui import QIcon
|
|
5
|
+
from PySide6.QtWidgets import QApplication
|
|
6
|
+
|
|
7
|
+
from mainwindow import MainWindow
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
app = QApplication()
|
|
12
|
+
app.setApplicationDisplayName("AutoMod")
|
|
13
|
+
app.setDesktopFileName("AutoMod")
|
|
14
|
+
app.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
|
|
15
|
+
app.setQuitOnLastWindowClosed(True)
|
|
16
|
+
app.setWindowIcon(QIcon("icons/app_icon.png"))
|
|
17
|
+
|
|
18
|
+
main_window = MainWindow()
|
|
19
|
+
main_window.show()
|
|
20
|
+
|
|
21
|
+
sys.exit(app.exec())
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == '__main__':
|
|
25
|
+
main()
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from PySide6.QtWidgets import QGraphicsView
|
|
6
|
+
from PySide6.QtCore import Qt, QPointF
|
|
7
|
+
|
|
8
|
+
from helixpoint import HelixPoint
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CSView(QGraphicsView):
|
|
12
|
+
|
|
13
|
+
TIME: float = 0.2 * 10 ** 9
|
|
14
|
+
ZOOM_SENSITIVITY: int = 200
|
|
15
|
+
|
|
16
|
+
def __init__(self, parent=None):
|
|
17
|
+
super().__init__(parent)
|
|
18
|
+
self.zoom = False
|
|
19
|
+
self.translate_ref = False
|
|
20
|
+
self.rotate_lat = False
|
|
21
|
+
self.previous_mouse_pos = (0, 0, time.time_ns())
|
|
22
|
+
self.ref_point = None
|
|
23
|
+
self.rotation_amount = 0
|
|
24
|
+
|
|
25
|
+
def set_ref_point(self, point):
|
|
26
|
+
self.ref_point = point
|
|
27
|
+
|
|
28
|
+
def get_ref_point(self):
|
|
29
|
+
return self.ref_point
|
|
30
|
+
|
|
31
|
+
def get_rotation_amount(self):
|
|
32
|
+
return self.rotation_amount
|
|
33
|
+
|
|
34
|
+
def set_rotation_amount(self, theta):
|
|
35
|
+
self.rotation_amount = theta
|
|
36
|
+
|
|
37
|
+
def enable_helix_editing(self):
|
|
38
|
+
for item in self.scene().items():
|
|
39
|
+
if isinstance(item, HelixPoint):
|
|
40
|
+
item.enable_editing()
|
|
41
|
+
|
|
42
|
+
def disable_helix_editing(self):
|
|
43
|
+
for item in self.scene().items():
|
|
44
|
+
if isinstance(item, HelixPoint):
|
|
45
|
+
item.disable_editing()
|
|
46
|
+
|
|
47
|
+
def enable_helix_renumbering(self):
|
|
48
|
+
for item in self.scene().items():
|
|
49
|
+
if isinstance(item, HelixPoint):
|
|
50
|
+
item.enable_renumbering()
|
|
51
|
+
|
|
52
|
+
def disable_helix_renumbering(self):
|
|
53
|
+
for item in self.scene().items():
|
|
54
|
+
if isinstance(item, HelixPoint):
|
|
55
|
+
item.disable_renumbering()
|
|
56
|
+
|
|
57
|
+
def activate_zoom(self):
|
|
58
|
+
self.zoom = True
|
|
59
|
+
self.setMouseTracking(False)
|
|
60
|
+
|
|
61
|
+
def deactivate_zoom(self):
|
|
62
|
+
self.zoom = False
|
|
63
|
+
self.setMouseTracking(True)
|
|
64
|
+
|
|
65
|
+
def activate_translate_ref(self):
|
|
66
|
+
self.translate_ref = True
|
|
67
|
+
self.setMouseTracking(False)
|
|
68
|
+
|
|
69
|
+
def deactivate_translate_ref(self):
|
|
70
|
+
self.translate_ref = False
|
|
71
|
+
self.setMouseTracking(True)
|
|
72
|
+
|
|
73
|
+
def activate_rotate(self):
|
|
74
|
+
self.rotate_lat = True
|
|
75
|
+
self.setMouseTracking(False)
|
|
76
|
+
|
|
77
|
+
def deactivate_rotate(self):
|
|
78
|
+
self.rotate_lat = False
|
|
79
|
+
self.setMouseTracking(True)
|
|
80
|
+
|
|
81
|
+
def mouseMoveEvent(self, event):
|
|
82
|
+
event.accept()
|
|
83
|
+
if time.time_ns() - self.previous_mouse_pos[2] <= self.TIME:
|
|
84
|
+
dx = event.x() - self.previous_mouse_pos[0]
|
|
85
|
+
dy = event.y() - self.previous_mouse_pos[1]
|
|
86
|
+
d = np.sqrt(dx**2 + dy**2)
|
|
87
|
+
if self.zoom and event.buttons() != Qt.MouseButton.NoButton:
|
|
88
|
+
event.accept()
|
|
89
|
+
if dy <= 0:
|
|
90
|
+
self.scale(1 + d / self.ZOOM_SENSITIVITY, 1 + d / self.ZOOM_SENSITIVITY)
|
|
91
|
+
else:
|
|
92
|
+
self.scale(1 / (1 + d / self.ZOOM_SENSITIVITY), 1 / (1 + d / self.ZOOM_SENSITIVITY))
|
|
93
|
+
self.scene().update()
|
|
94
|
+
self.update()
|
|
95
|
+
elif self.translate_ref and event.buttons() != Qt.MouseButton.NoButton:
|
|
96
|
+
event.accept()
|
|
97
|
+
inverse, _ = self.transform().inverted()
|
|
98
|
+
mapped_move = inverse.map(QPointF(dx, dy))
|
|
99
|
+
self.ref_point.setPos(self.ref_point.x() + mapped_move.x(), self.ref_point.y() + mapped_move.y())
|
|
100
|
+
self.parent().parent().update_ref_point_value(self.ref_point.x(), self.ref_point.y())
|
|
101
|
+
self.scene().update()
|
|
102
|
+
self.update()
|
|
103
|
+
elif self.rotate_lat and event.buttons() != Qt.MouseButton.NoButton:
|
|
104
|
+
event.accept()
|
|
105
|
+
if dy <= 0:
|
|
106
|
+
self.rotate(d)
|
|
107
|
+
self.parent().parent().update_cs_angle(d)
|
|
108
|
+
self.rotation_amount += d
|
|
109
|
+
else:
|
|
110
|
+
self.rotate(-d)
|
|
111
|
+
self.parent().parent().update_cs_angle(-d)
|
|
112
|
+
self.rotation_amount -= d
|
|
113
|
+
for item in self.scene().items():
|
|
114
|
+
if isinstance(item, HelixPoint):
|
|
115
|
+
item.update_text_rotation()
|
|
116
|
+
self.scene().update()
|
|
117
|
+
self.update()
|
|
118
|
+
else:
|
|
119
|
+
super().mouseMoveEvent(event)
|
|
120
|
+
self.previous_mouse_pos = (event.x(), event.y(), time.time_ns())
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from PySide6.QtWidgets import QGraphicsScene
|
|
2
|
+
|
|
3
|
+
from storage3d import Storage3D
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CurveScene(QGraphicsScene):
|
|
7
|
+
|
|
8
|
+
def __init__(self, size, w, parent=None):
|
|
9
|
+
super().__init__(parent=parent)
|
|
10
|
+
self.storage = Storage3D(self)
|
|
11
|
+
self.corners = [None for _ in range(8)]
|
|
12
|
+
self.size = size
|
|
13
|
+
self.w = w
|
|
14
|
+
self.storage.create_grid()
|
|
15
|
+
self.setSceneRect(-w/2, -w/2, w, w)
|
|
16
|
+
|
|
17
|
+
def remove_all_corners(self):
|
|
18
|
+
self.corners = [None for _ in range(8)]
|
|
19
|
+
|
|
20
|
+
def replace_corner(self, grid_corner, index):
|
|
21
|
+
self.corners[index] = grid_corner
|
|
22
|
+
|
|
23
|
+
def update_grid_scale(self):
|
|
24
|
+
self.size = 8 * self.parent().get_parameters()['gs']
|
|
25
|
+
self.w = self.parent().get_parameters()['gs']
|
|
26
|
+
|
|
27
|
+
def get_storage(self):
|
|
28
|
+
return self.storage
|
|
29
|
+
|
|
30
|
+
def get_corners(self):
|
|
31
|
+
return self.corners
|
|
32
|
+
|
|
33
|
+
def get_w(self):
|
|
34
|
+
return self.w
|
|
35
|
+
|
|
36
|
+
def get_size(self):
|
|
37
|
+
return self.size
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from PySide6.QtCore import Qt, QPointF
|
|
5
|
+
from PySide6.QtGui import QTransform
|
|
6
|
+
from PySide6.QtWidgets import QGraphicsView
|
|
7
|
+
|
|
8
|
+
from nodepoint import NodePoint
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CurveView(QGraphicsView):
|
|
12
|
+
|
|
13
|
+
TIME: float = 0.2 * 10 ** 9
|
|
14
|
+
ZOOM_SENSITIVITY: int = 200
|
|
15
|
+
TRANSLATE_SENSITIVITY: int = 20
|
|
16
|
+
ROTATE_SENSITIVITY: int = 200
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def rotation_matrix_y(theta):
|
|
20
|
+
return np.array([[np.cos(theta), 0, np.sin(theta)], [0, 1, 0], [(-1)*np.sin(theta), 0, np.cos(theta)]])
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def rotation_matrix_x(theta):
|
|
24
|
+
return np.array([[1, 0, 0], [0, np.cos(theta), (-1)*np.sin(theta)], [0, np.sin(theta), np.cos(theta)]])
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def rotation_matrix_z(theta):
|
|
28
|
+
return np.array([[np.cos(theta), (-1)*np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1]])
|
|
29
|
+
|
|
30
|
+
def __init__(self, curve_scene, parent=None):
|
|
31
|
+
super().__init__(curve_scene, parent)
|
|
32
|
+
self.zoom = False
|
|
33
|
+
self.zoom_amount = 1.1
|
|
34
|
+
self.x_translation = False
|
|
35
|
+
self.y_translation = False
|
|
36
|
+
self.z_translation = False
|
|
37
|
+
self.x_rotation = False
|
|
38
|
+
self.y_rotation = False
|
|
39
|
+
self.z_rotation = False
|
|
40
|
+
self.add_points = False
|
|
41
|
+
self.connect_points = False
|
|
42
|
+
self.point_x_translation = False
|
|
43
|
+
self.point_y_translation = False
|
|
44
|
+
self.point_z_translation = False
|
|
45
|
+
self.def_transform = None
|
|
46
|
+
self.scene = curve_scene
|
|
47
|
+
self.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.FullViewportUpdate)
|
|
48
|
+
self.setSceneRect(-self.scene.size/2, -self.scene.size/2, self.scene.size, self.scene.size)
|
|
49
|
+
self.previous_mouse_pos = (0, 0, time.time_ns())
|
|
50
|
+
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
51
|
+
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
52
|
+
self.setMinimumWidth(500)
|
|
53
|
+
self.setMinimumHeight(500)
|
|
54
|
+
self.init_fit()
|
|
55
|
+
self.init_rotation()
|
|
56
|
+
|
|
57
|
+
def init_rotation(self):
|
|
58
|
+
R = self.rotation_matrix_x(0.05)
|
|
59
|
+
self.scene.get_storage().rotate_all_points(R)
|
|
60
|
+
R = self.rotation_matrix_z(0.05)
|
|
61
|
+
self.scene.get_storage().rotate_all_points(R)
|
|
62
|
+
|
|
63
|
+
def init_fit(self):
|
|
64
|
+
dx = 100 / self.parent().get_parameters()['gs']
|
|
65
|
+
dy = 100 / self.parent().get_parameters()['gs']
|
|
66
|
+
self.def_transform = QTransform.fromScale(dx, dy)
|
|
67
|
+
self.setTransform(self.def_transform)
|
|
68
|
+
self.scale(self.zoom_amount, self.zoom_amount)
|
|
69
|
+
|
|
70
|
+
def restore_view(self):
|
|
71
|
+
self.scene.get_storage().rotate_to_default()
|
|
72
|
+
self.scene.get_storage().translate_to_default()
|
|
73
|
+
self.zoom_amount = 1.1
|
|
74
|
+
self.init_fit()
|
|
75
|
+
self.init_rotation()
|
|
76
|
+
self.parent().deactivate_point_tools()
|
|
77
|
+
self.scene.update()
|
|
78
|
+
self.update()
|
|
79
|
+
|
|
80
|
+
def pre_translate(self, dx, dy):
|
|
81
|
+
delta = (np.dot(np.array([dx, dy]), np.array([0, -1])) /
|
|
82
|
+
(self.TRANSLATE_SENSITIVITY * self.parent().get_parameters()["ts"]))
|
|
83
|
+
w = self.scene.get_w()
|
|
84
|
+
if np.abs(delta) > w:
|
|
85
|
+
delta = (delta/np.abs(delta)) * w
|
|
86
|
+
return delta
|
|
87
|
+
|
|
88
|
+
def activate_add_points(self):
|
|
89
|
+
self.add_points = True
|
|
90
|
+
self.setMouseTracking(False)
|
|
91
|
+
|
|
92
|
+
def deactivate_add_points(self):
|
|
93
|
+
self.add_points = False
|
|
94
|
+
self.setMouseTracking(True)
|
|
95
|
+
|
|
96
|
+
def activate_zoom(self):
|
|
97
|
+
self.zoom = True
|
|
98
|
+
self.setMouseTracking(False)
|
|
99
|
+
|
|
100
|
+
def activate_x_translation(self):
|
|
101
|
+
self.x_translation = True
|
|
102
|
+
self.setMouseTracking(False)
|
|
103
|
+
|
|
104
|
+
def activate_y_translation(self):
|
|
105
|
+
self.y_translation = True
|
|
106
|
+
self.setMouseTracking(False)
|
|
107
|
+
|
|
108
|
+
def activate_z_translation(self):
|
|
109
|
+
self.z_translation = True
|
|
110
|
+
self.setMouseTracking(False)
|
|
111
|
+
|
|
112
|
+
def activate_x_rotation(self):
|
|
113
|
+
self.x_rotation = True
|
|
114
|
+
self.setMouseTracking(False)
|
|
115
|
+
|
|
116
|
+
def activate_y_rotation(self):
|
|
117
|
+
self.y_rotation = True
|
|
118
|
+
self.setMouseTracking(False)
|
|
119
|
+
|
|
120
|
+
def activate_z_rotation(self):
|
|
121
|
+
self.z_rotation = True
|
|
122
|
+
self.setMouseTracking(False)
|
|
123
|
+
|
|
124
|
+
def activate_point_x_translation(self):
|
|
125
|
+
self.point_x_translation = True
|
|
126
|
+
self.setMouseTracking(False)
|
|
127
|
+
|
|
128
|
+
def activate_point_y_translation(self):
|
|
129
|
+
self.point_y_translation = True
|
|
130
|
+
self.setMouseTracking(False)
|
|
131
|
+
|
|
132
|
+
def activate_point_z_translation(self):
|
|
133
|
+
self.point_z_translation = True
|
|
134
|
+
self.setMouseTracking(False)
|
|
135
|
+
|
|
136
|
+
def activate_connect_points(self):
|
|
137
|
+
self.connect_points = True
|
|
138
|
+
self.setMouseTracking(True)
|
|
139
|
+
self.scene.clearSelection()
|
|
140
|
+
|
|
141
|
+
def deactivate_connect_points(self):
|
|
142
|
+
self.connect_points = False
|
|
143
|
+
self.setMouseTracking(False)
|
|
144
|
+
self.scene.clearSelection()
|
|
145
|
+
|
|
146
|
+
def deactivate_zoom(self):
|
|
147
|
+
self.zoom = False
|
|
148
|
+
self.setMouseTracking(True)
|
|
149
|
+
|
|
150
|
+
def deactivate_x_translation(self):
|
|
151
|
+
self.x_translation = False
|
|
152
|
+
self.setMouseTracking(True)
|
|
153
|
+
|
|
154
|
+
def deactivate_y_translation(self):
|
|
155
|
+
self.y_translation = False
|
|
156
|
+
self.setMouseTracking(True)
|
|
157
|
+
|
|
158
|
+
def deactivate_z_translation(self):
|
|
159
|
+
self.z_translation = False
|
|
160
|
+
self.setMouseTracking(True)
|
|
161
|
+
|
|
162
|
+
def deactivate_x_rotation(self):
|
|
163
|
+
self.x_rotation = False
|
|
164
|
+
self.setMouseTracking(True)
|
|
165
|
+
|
|
166
|
+
def deactivate_y_rotation(self):
|
|
167
|
+
self.y_rotation = False
|
|
168
|
+
self.setMouseTracking(True)
|
|
169
|
+
|
|
170
|
+
def deactivate_z_rotation(self):
|
|
171
|
+
self.z_rotation = False
|
|
172
|
+
self.setMouseTracking(True)
|
|
173
|
+
|
|
174
|
+
def deactivate_point_x_translation(self):
|
|
175
|
+
self.point_x_translation = False
|
|
176
|
+
self.setMouseTracking(True)
|
|
177
|
+
|
|
178
|
+
def deactivate_point_y_translation(self):
|
|
179
|
+
self.point_y_translation = False
|
|
180
|
+
self.setMouseTracking(True)
|
|
181
|
+
|
|
182
|
+
def deactivate_point_z_translation(self):
|
|
183
|
+
self.point_z_translation = False
|
|
184
|
+
self.setMouseTracking(True)
|
|
185
|
+
|
|
186
|
+
def mouseMoveEvent(self, event):
|
|
187
|
+
if time.time_ns() - self.previous_mouse_pos[2] <= self.TIME:
|
|
188
|
+
dx = event.position().x() - self.previous_mouse_pos[0]
|
|
189
|
+
dy = event.position().y() - self.previous_mouse_pos[1]
|
|
190
|
+
d = np.sqrt(dx ** 2 + dy ** 2)
|
|
191
|
+
if self.zoom and event.buttons() != Qt.MouseButton.NoButton:
|
|
192
|
+
event.accept()
|
|
193
|
+
if dy <= 0:
|
|
194
|
+
delta = 1 + (d / (self.ZOOM_SENSITIVITY * self.parent().get_parameters()["zs"]))
|
|
195
|
+
self.scale(delta, delta)
|
|
196
|
+
self.zoom_amount = self.zoom_amount * delta
|
|
197
|
+
else:
|
|
198
|
+
delta = 1 / (1 + (d / (self.ZOOM_SENSITIVITY * self.parent().get_parameters()["zs"])))
|
|
199
|
+
self.scale(delta, delta)
|
|
200
|
+
self.zoom_amount = self.zoom_amount * delta
|
|
201
|
+
self.scene.update()
|
|
202
|
+
self.update()
|
|
203
|
+
elif (self.x_translation or self.y_translation or self.z_translation) and event.buttons() != Qt.MouseButton.NoButton:
|
|
204
|
+
event.accept()
|
|
205
|
+
delta = self.pre_translate(dx, dy)
|
|
206
|
+
if self.x_translation:
|
|
207
|
+
self.scene.get_storage().translate_all_points(delta, 0, 0)
|
|
208
|
+
elif self.y_translation:
|
|
209
|
+
self.scene.get_storage().translate_all_points(0, delta, 0)
|
|
210
|
+
else:
|
|
211
|
+
self.scene.get_storage().translate_all_points(0, 0, delta)
|
|
212
|
+
self.scene.get_storage().update_grid()
|
|
213
|
+
self.scene.update()
|
|
214
|
+
self.update()
|
|
215
|
+
elif (self.x_rotation or self.y_rotation or self.z_rotation) and event.buttons() != Qt.MouseButton.NoButton:
|
|
216
|
+
event.accept()
|
|
217
|
+
theta = d / (self.ROTATE_SENSITIVITY * self.parent().get_parameters()["rs"])
|
|
218
|
+
if self.y_rotation:
|
|
219
|
+
if dy > 0:
|
|
220
|
+
R = self.rotation_matrix_y(theta)
|
|
221
|
+
else:
|
|
222
|
+
R = self.rotation_matrix_y(-theta)
|
|
223
|
+
elif self.x_rotation:
|
|
224
|
+
if dy > 0:
|
|
225
|
+
R = self.rotation_matrix_x(theta)
|
|
226
|
+
else:
|
|
227
|
+
R = self.rotation_matrix_x(-theta)
|
|
228
|
+
else:
|
|
229
|
+
if dy > 0:
|
|
230
|
+
R = self.rotation_matrix_z(theta)
|
|
231
|
+
else:
|
|
232
|
+
R = self.rotation_matrix_z(-theta)
|
|
233
|
+
self.scene.get_storage().rotate_all_points(R)
|
|
234
|
+
self.scene.update()
|
|
235
|
+
self.update()
|
|
236
|
+
elif (self.point_x_translation or self.point_y_translation or self.point_z_translation) and event.buttons() != Qt.MouseButton.NoButton:
|
|
237
|
+
event.accept()
|
|
238
|
+
delta = self.pre_translate(dx, dy)
|
|
239
|
+
if self.point_x_translation:
|
|
240
|
+
self.scene.get_storage().translate_node(delta, 0, 0)
|
|
241
|
+
elif self.point_y_translation:
|
|
242
|
+
self.scene.get_storage().translate_node(0, delta, 0)
|
|
243
|
+
else:
|
|
244
|
+
self.scene.get_storage().translate_node(0, 0, delta)
|
|
245
|
+
self.scene.update()
|
|
246
|
+
self.update()
|
|
247
|
+
|
|
248
|
+
else:
|
|
249
|
+
super().mouseMoveEvent(event)
|
|
250
|
+
self.previous_mouse_pos = (event.position().x(), event.position().y(), time.time_ns())
|
|
251
|
+
|
|
252
|
+
def mousePressEvent(self, event):
|
|
253
|
+
if event.buttons() == Qt.MouseButton.LeftButton:
|
|
254
|
+
event_pos = QPointF(((event.x() - self.width()/2) / self.transform().m11()) / 2,
|
|
255
|
+
((event.y() - self.height()/2) / self.transform().m22()) / 2)
|
|
256
|
+
if self.add_points:
|
|
257
|
+
self.scene.get_storage().add_node_point(event_pos)
|
|
258
|
+
elif self.connect_points and self.scene.get_storage().has_node_at(event_pos):
|
|
259
|
+
for item in self.scene.items():
|
|
260
|
+
if isinstance(item, NodePoint) and item.has_selection():
|
|
261
|
+
subseq_node = self.scene.get_storage().get_node_at(event_pos)
|
|
262
|
+
if ((item.get_next_node() is None) and (subseq_node.get_prev_node() is None) and
|
|
263
|
+
(item is not subseq_node)):
|
|
264
|
+
item.set_next_node(subseq_node)
|
|
265
|
+
subseq_node.set_prev_node(item)
|
|
266
|
+
self.scene.get_storage().add_path_line(item, subseq_node)
|
|
267
|
+
self.scene.get_storage().get_nodes()[item.get_node_index()][3] = subseq_node.get_node_index()
|
|
268
|
+
self.scene.get_storage().get_nodes()[subseq_node.get_node_index()][2] = item.get_node_index()
|
|
269
|
+
else:
|
|
270
|
+
self.scene.get_storage().get_node_at(event_pos).mousePressEvent(event)
|
|
271
|
+
elif self.point_x_translation or self.point_y_translation or self.point_z_translation:
|
|
272
|
+
pass
|
|
273
|
+
else:
|
|
274
|
+
if self.scene.get_storage().has_node_at(event_pos):
|
|
275
|
+
self.scene.get_storage().get_node_at(event_pos).mousePressEvent(event)
|
|
276
|
+
|
|
277
|
+
def resizeEvent(self, event):
|
|
278
|
+
w1, w2, w3, w4 = self.parent().get_tool_widths()
|
|
279
|
+
d = event.oldSize().width() - event.size().width()
|
|
280
|
+
if d in [w1 + i for i in range(-2, 2)] or d in [w2 + i for i in range(-2, 2)]\
|
|
281
|
+
or d in [-w1 + i for i in range(-2, 2)] or d in [-w2 + i for i in range(-2, 2)]:
|
|
282
|
+
dx_scene = (d / self.transform().m11()) / 4
|
|
283
|
+
dy_scene = ((event.oldSize().height() - event.size().height())/ self.transform().m22()) / 4
|
|
284
|
+
d_vec = np.array([-dx_scene, -dy_scene, 0])
|
|
285
|
+
x_hat = self.scene.get_storage().get_unit_x()
|
|
286
|
+
y_hat = self.scene.get_storage().get_unit_y()
|
|
287
|
+
z_hat = self.scene.get_storage().get_unit_z()
|
|
288
|
+
self.scene.get_storage().translate_all_points(np.dot(d_vec, x_hat), np.dot(d_vec, y_hat), np.dot(d_vec, z_hat))
|
|
289
|
+
elif d in [w3 + i for i in range(-8, 8)] or d in [w4 + i for i in range(-2, 2)]\
|
|
290
|
+
or d in [-w3 + i for i in range(-6, 6)] or d in [-w4 + i for i in range(-2, 2)]:
|
|
291
|
+
dx_scene = (d / self.transform().m11()) / 4
|
|
292
|
+
dy_scene = ((event.oldSize().height() - event.size().height()) / self.transform().m22()) / 4
|
|
293
|
+
d_vec = np.array([dx_scene, -dy_scene, 0])
|
|
294
|
+
x_hat = self.scene.get_storage().get_unit_x()
|
|
295
|
+
y_hat = self.scene.get_storage().get_unit_y()
|
|
296
|
+
z_hat = self.scene.get_storage().get_unit_z()
|
|
297
|
+
self.scene.get_storage().translate_all_points(np.dot(d_vec, x_hat), np.dot(d_vec, y_hat), np.dot(d_vec, z_hat))
|
|
298
|
+
elif d in [w2 + w3 + w4 + i for i in range(-6, 6)] or d in [-(w2 + w3 + w4) + i for i in range(-6, 6)]:
|
|
299
|
+
dx_scene_1 = (w2 / self.transform().m11()) / 4
|
|
300
|
+
dx_scene_2 = ((-d - w2) / self.transform().m11()) / 4
|
|
301
|
+
dy_scene = ((event.oldSize().height() - event.size().height()) / self.transform().m22()) / 4
|
|
302
|
+
d_vec_1 = np.array([dx_scene_1, -dy_scene, 0])
|
|
303
|
+
d_vec_2 = np.array([-dx_scene_2, -dy_scene, 0])
|
|
304
|
+
x_hat = self.scene.get_storage().get_unit_x()
|
|
305
|
+
y_hat = self.scene.get_storage().get_unit_y()
|
|
306
|
+
z_hat = self.scene.get_storage().get_unit_z()
|
|
307
|
+
self.scene.get_storage().translate_all_points(np.dot(d_vec_1, x_hat), np.dot(d_vec_1, y_hat), np.dot(d_vec_1, z_hat))
|
|
308
|
+
self.scene.get_storage().translate_all_points(np.dot(d_vec_2, x_hat), np.dot(d_vec_2, y_hat), np.dot(d_vec_2, z_hat))
|
|
309
|
+
elif d in [w3 + w4 + i for i in range(-6, 6)] or d in [-(w3 + w4) + i for i in range(-6, 6)]:
|
|
310
|
+
dx_scene = (d / self.transform().m11()) / 4
|
|
311
|
+
dy_scene = ((event.oldSize().height() - event.size().height()) / self.transform().m22()) / 4
|
|
312
|
+
d_vec = np.array([dx_scene, -dy_scene, 0])
|
|
313
|
+
x_hat = self.scene.get_storage().get_unit_x()
|
|
314
|
+
y_hat = self.scene.get_storage().get_unit_y()
|
|
315
|
+
z_hat = self.scene.get_storage().get_unit_z()
|
|
316
|
+
self.scene.get_storage().translate_all_points(np.dot(d_vec, x_hat), np.dot(d_vec, y_hat),
|
|
317
|
+
np.dot(d_vec, z_hat))
|
|
318
|
+
self.init_fit()
|
|
319
|
+
self.scene.update()
|
|
320
|
+
self.update()
|
|
321
|
+
|
|
322
|
+
def wheelEvent(self, event):
|
|
323
|
+
event.accept()
|
|
324
|
+
|
|
325
|
+
def keyPressEvent(self, event):
|
|
326
|
+
event.accept()
|