q3dviewer 1.0.7__py3-none-any.whl → 1.0.9__py3-none-any.whl

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.
q3dviewer/utils/maths.py CHANGED
@@ -3,9 +3,145 @@ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
3
3
  Distributed under MIT license. See LICENSE for more information.
4
4
  """
5
5
 
6
+ """
7
+ Math proofs and implementations:
8
+ https://github.com/scomup/MathematicalRobotics.git
9
+ """
10
+
11
+
6
12
  import numpy as np
7
13
 
8
14
 
15
+ _epsilon_ = 1e-5
16
+
17
+
18
+ def skew(vector):
19
+ return np.array([[0, -vector[2], vector[1]],
20
+ [vector[2], 0, -vector[0]],
21
+ [-vector[1], vector[0], 0]])
22
+
23
+
24
+ def expSO3(omega):
25
+ """
26
+ Exponential map of SO3
27
+ The proof is shown in 3d_rotation_group.md (10)
28
+ """
29
+ theta2 = omega.dot(omega)
30
+ theta = np.sqrt(theta2)
31
+ nearZero = theta2 <= _epsilon_
32
+ W = skew(omega)
33
+ if (nearZero):
34
+ return np.eye(3) + W
35
+ else:
36
+ K = W/theta
37
+ KK = K.dot(K)
38
+ sin_theta = np.sin(theta)
39
+ one_minus_cos = 1 - np.cos(theta)
40
+ R = np.eye(3) + sin_theta * K + one_minus_cos * KK # rotation.md (10)
41
+ return R
42
+
43
+
44
+ def logSO3(R):
45
+ """
46
+ Logarithm map of SO3
47
+ The proof is shown in rotation.md (14)
48
+ """
49
+ R11, R12, R13 = R[0, :]
50
+ R21, R22, R23 = R[1, :]
51
+ R31, R32, R33 = R[2, :]
52
+ tr = np.trace(R)
53
+ omega = np.zeros(3)
54
+ v = np.array([R32 - R23, R13 - R31, R21 - R12])
55
+ # when trace == -1, i.e., the theta is approx to +-pi, +-3pi, +-5pi, etc.
56
+ # we do something special
57
+ if (tr + 1.0 < 1e-3):
58
+ if (R33 > R22 and R33 > R11):
59
+ # R33 is largest
60
+ # sin(theta) approx to sgn_w*pi-theta, cos(theta) approx to -1
61
+ W = R21 - R12 # 2*r3*sin(theta) = 2*r3*(sgn_w*pi-theta)
62
+ Q1 = R31 + R13 # 4 * r1*r3
63
+ Q2 = R23 + R32 # 4 * r2*r3
64
+ Q3 = 2.0 + 2.0 * R33 # 4 * r3*r3
65
+ r = np.sqrt(Q3) # 2 * r3
66
+ one_over_r = 1 / r # 1 / (2*r3)
67
+ norm = np.sqrt(Q1*Q1 + Q2*Q2 + Q3*Q3 + W*W) # 4*r3
68
+ sgn_w = np.sign(W) # get the sgn of theta
69
+ mag = np.pi - (2 * sgn_w * W) / norm # theta*sgn_w
70
+ scale = 0.5 * mag * one_over_r # theta * sgn_w / (4*r3)
71
+ # omega = theta * [4*r1*r3, 4*r2*r3, 4*r3*r3]/ (4*r3)
72
+ omega = sgn_w * scale * np.array([Q1, Q2, Q3])
73
+ elif (R22 > R11):
74
+ # R22 is the largest
75
+ W = R13 - R31 # 2*r2*sin(theta) = 2*r2*(sgn_w*pi-theta)
76
+ Q1 = R12 + R21 # 4 * r2*r1
77
+ Q2 = 2.0 + 2.0 * R22 # 4 * r2*r2
78
+ Q3 = R23 + R32 # 4 * r2*r3
79
+ r = np.sqrt(Q2)
80
+ one_over_r = 1 / r
81
+ norm = np.sqrt(Q1*Q1 + Q2*Q2 + Q3*Q3 + W*W)
82
+ sgn_w = np.sign(W)
83
+ mag = np.pi - (2 * sgn_w * W) / norm
84
+ scale = 0.5 * one_over_r * mag
85
+ omega = sgn_w * scale * np.array([Q1, Q2, Q3])
86
+ else:
87
+ # R11 is the largest
88
+ W = R32 - R23 # 2*r1*sin(theta) = 2*r1*(sgn_w*pi-theta)
89
+ Q1 = 2.0 + 2.0 * R11 # 4 * r1*r1
90
+ Q2 = R12 + R21 # 4 * r1*r2
91
+ Q3 = R31 + R13 # 4 * r1*r3
92
+ r = np.sqrt(Q1)
93
+ one_over_r = 1 / r
94
+ norm = np.sqrt(Q1*Q1 + Q2*Q2 + Q3*Q3 + W*W)
95
+ sgn_w = np.sign(W)
96
+ mag = np.pi - (2 * sgn_w * W) / norm
97
+ scale = 0.5 * one_over_r * mag
98
+ omega = sgn_w * scale * np.array([Q1, Q2, Q3])
99
+ else:
100
+ magnitude = 0
101
+ tr_3 = tr - 3.0
102
+ if (tr_3 < -1e-6):
103
+ # this is the normal case -1 < trace < 3
104
+ theta = np.arccos((tr - 1.0) / 2.0)
105
+ magnitude = theta / (2.0 * np.sin(theta))
106
+ else:
107
+ # when theta near 0, +-2pi, +-4pi, etc. (trace near 3.0)
108
+ # use Taylor expansion: theta \approx 1/2-(t-3)/12 + O((t-3)^2)
109
+ # see https://github.com/borglab/gtsam/issues/746 for details
110
+ magnitude = 0.5 - tr_3 / 12.0 + tr_3*tr_3/60.0
111
+ omega = magnitude * np.array([R32 - R23, R13 - R31, R21 - R12])
112
+ return omega
113
+
114
+
115
+ def interpolate_pose(T1, T2, v_max, omega_max, dt=0.02):
116
+ R1, t1 = makeRt(T1)
117
+ R2, t2 = makeRt(T2)
118
+
119
+ # Get transfrom time based on linear velocity
120
+ d = np.linalg.norm(t2 - t1)
121
+ t_lin = d / v_max
122
+
123
+ # Get transform time based on angular velocity
124
+ omega = logSO3(R2 @ R1.T)
125
+ theta = np.linalg.norm(omega)
126
+ t_ang = theta / omega_max
127
+
128
+ # Get total time based on the linear and angular time.
129
+ t_total = max(t_lin, t_ang)
130
+ num_steps = int(np.ceil(t_total / dt))
131
+
132
+ # Generate interpolated transforms
133
+ interpolated_Ts = []
134
+ for i in range(num_steps):
135
+ s = i / num_steps
136
+ t_interp = (1 - s) * t1 + s * t2
137
+ # Interpolate rotation using SO3.
138
+ R_interp = expSO3(s * omega) @ R1
139
+ T_interp = makeT(R_interp, t_interp)
140
+ interpolated_Ts.append(T_interp)
141
+
142
+ return interpolated_Ts
143
+
144
+
9
145
  def frustum(left, right, bottom, top, near, far):
10
146
  # see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml
11
147
  if near <= 0 or far <= 0 or near >= far or left == right or bottom == top:
@@ -157,11 +293,25 @@ def makeRt(T):
157
293
  return R, t
158
294
 
159
295
  def hex_to_rgba(hex_color):
160
- color_flat = int(hex_color[1:], 16)
161
- red = (color_flat >> 16) & 0xFF
162
- green = (color_flat >> 8) & 0xFF
163
- blue = color_flat & 0xFF
164
- return (red / 255.0, green / 255.0, blue / 255.0, 1.0)
296
+ if not hex_color.startswith("#"):
297
+ print("Invalid hex color string.")
298
+ return (1.0, 1.0, 1.0, 1.0)
299
+ if len(hex_color) == 7:
300
+ color_flat = int(hex_color[1:], 16)
301
+ red = (color_flat >> 16) & 0xFF
302
+ green = (color_flat >> 8) & 0xFF
303
+ blue = color_flat & 0xFF
304
+ return (red / 255.0, green / 255.0, blue / 255.0, 1.0)
305
+ elif len(hex_color) == 9:
306
+ color_flat = int(hex_color[1:], 16)
307
+ red = (color_flat >> 24) & 0xFF
308
+ green = (color_flat >> 16) & 0xFF
309
+ blue = (color_flat >> 8) & 0xFF
310
+ alpha = color_flat & 0xFF
311
+ return (red / 255.0, green / 255.0, blue / 255.0, alpha / 255.0)
312
+ else:
313
+ print("Invalid hex color string.")
314
+ return (1.0, 1.0, 1.0, 1.0)
165
315
 
166
316
  # euler = np.array([1, 0.1, 0.1])
167
317
  # euler_angles = matrix_to_euler(euler_to_matrix(euler))
q3dviewer/viewer.py CHANGED
@@ -6,7 +6,7 @@ Distributed under MIT license. See LICENSE for more information.
6
6
 
7
7
  from q3dviewer.glwidget import *
8
8
  import signal
9
- from PySide6.QtWidgets import QMainWindow, QApplication
9
+ from PySide6.QtWidgets import QMainWindow, QApplication, QHBoxLayout
10
10
 
11
11
 
12
12
  def handler(signal, frame):
@@ -15,22 +15,45 @@ def handler(signal, frame):
15
15
 
16
16
 
17
17
  class Viewer(QMainWindow):
18
- def __init__(self, name='Viewer', win_size=[1920, 1080]):
18
+ def __init__(self, name='Viewer', win_size=[1920, 1080],
19
+ gl_widget_class=GLWidget, update_interval=20):
19
20
  signal.signal(signal.SIGINT, handler)
20
21
  super(Viewer, self).__init__()
21
22
  self.setGeometry(0, 0, win_size[0], win_size[1])
23
+ self.gl_widget_class = gl_widget_class
22
24
  self.init_ui()
25
+ self.update_interval = update_interval
26
+ self.add_update_timer()
23
27
  self.setWindowTitle(name)
28
+ self.installEventFilter(self)
24
29
 
25
30
  def init_ui(self):
26
31
  center_widget = QWidget()
27
32
  self.setCentralWidget(center_widget)
28
- self.layout = QHBoxLayout()
29
- center_widget.setLayout(self.layout)
30
- self.glwidget = GLWidget()
31
- self.layout.addWidget(self.glwidget, 1)
33
+ main_layout = QHBoxLayout()
34
+ self.add_control_panel(main_layout)
35
+ center_widget.setLayout(main_layout)
36
+ self.glwidget = self.gl_widget_class()
37
+ main_layout.addWidget(self.glwidget, 1)
38
+ self.default_gl_setting(self.glwidget)
39
+
40
+ def add_control_panel(self, main_layout):
41
+ """
42
+ Override this function to add your own control panel to
43
+ the left side of the main window.
44
+ Don't forget add your own layout to the main_layout.
45
+ """
46
+ pass
47
+
48
+ def default_gl_setting(self, glwidget):
49
+ """
50
+ Override this function to set the default opengl setting of the viewer.
51
+ """
52
+ pass
53
+
54
+ def add_update_timer(self):
32
55
  timer = QtCore.QTimer(self)
33
- timer.setInterval(20) # period, in milliseconds
56
+ timer.setInterval(self.update_interval) # period, in milliseconds
34
57
  timer.timeout.connect(self.update)
35
58
  timer.start()
36
59
 
@@ -1,21 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: q3dviewer
3
- Version: 1.0.7
4
- Summary: UNKNOWN
5
- Home-page: UNKNOWN
6
- Author: UNKNOWN
7
- Author-email: UNKNOWN
8
- License: UNKNOWN
9
- Platform: UNKNOWN
10
- Requires-Dist: PyOpenGL
11
- Requires-Dist: laspy
12
- Requires-Dist: meshio
3
+ Version: 1.0.9
4
+ License-File: LICENSE
13
5
  Requires-Dist: numpy
6
+ Requires-Dist: pyside6
7
+ Requires-Dist: PyOpenGL
14
8
  Requires-Dist: pillow
15
- Requires-Dist: pye57
9
+ Requires-Dist: meshio
16
10
  Requires-Dist: pypcd4
17
- Requires-Dist: pyside6
18
-
19
- UNKNOWN
20
-
11
+ Requires-Dist: pye57
12
+ Requires-Dist: laspy
13
+ Requires-Dist: imageio
21
14
 
@@ -0,0 +1,45 @@
1
+ q3dviewer/__init__.py,sha256=rP5XX_x8g7hxIMqNHlU89BN4dt5MSvoYYwip68fCmhc,173
2
+ q3dviewer/base_glwidget.py,sha256=7CJKea2JSP78jsESZJP38yfLhhRINF-VQ9R8sMXwAo0,10697
3
+ q3dviewer/base_item.py,sha256=lzb04oRaS4rRJrAP6C1Bu4ugK237FgupMTB97zjNVFw,1768
4
+ q3dviewer/gau_io.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ q3dviewer/glwidget.py,sha256=im8hjVYEL0Zl7fOIHTQMJdWu7WNOHlvTdIDYjebz9WA,4940
6
+ q3dviewer/viewer.py,sha256=LH1INLFhi6pRjzazzQJ0AWT4hgyXI6GnmqoJFUwUZVE,2517
7
+ q3dviewer/custom_items/__init__.py,sha256=gOiAxdjDaAnFL8YbqSEWWWOwUrJfvzP9JLR34sCB9-4,434
8
+ q3dviewer/custom_items/axis_item.py,sha256=PTBSf5DmQI8ieSinYjY_aC7P8q1nzE-2Vc2GNd1O3Os,2568
9
+ q3dviewer/custom_items/cloud_io_item.py,sha256=gjK3n9WKB7JwxC93ijkweEHA5EezpgNJ8KO-PBaDKCs,2835
10
+ q3dviewer/custom_items/cloud_item.py,sha256=7r6s_j1qEUfLe70t-M68-oIrGRrPOUcZp18ML6jST0s,12542
11
+ q3dviewer/custom_items/frame_item.py,sha256=6BOM3MXi-Akv6KUXDC3QYEAXKxwk0Eo1KQn-F7jkqrQ,7559
12
+ q3dviewer/custom_items/gaussian_item.py,sha256=CZoXMmj2JPFfMqu7v4q4dLUI2cg_WfE1DHWGXjEYn6M,9869
13
+ q3dviewer/custom_items/grid_item.py,sha256=20n4TGm5YEaudhnEOCOk-HtsKwxVxaPr8YV36kO04yU,4802
14
+ q3dviewer/custom_items/image_item.py,sha256=ctNR81fVgxkdl2n3U_TPFL6yn086UhNI_A9fFHgkc-4,5491
15
+ q3dviewer/custom_items/line_item.py,sha256=u0oFN2iHzsRHtnbvyvC_iglEkCwEU2NnTuE536sKUAE,4348
16
+ q3dviewer/custom_items/text_item.py,sha256=nuHMVMQrwy50lNk9hxB94criFxbJJK-SYiK2fSXWUMQ,2158
17
+ q3dviewer/shaders/cloud_frag.glsl,sha256=tbCsDUp9YlPe0hRWlFS724SH6TtMeLO-GVYROzEElZg,609
18
+ q3dviewer/shaders/cloud_vert.glsl,sha256=Vxgw-Zrr0knAK0z4qMXKML6IC4EbffKMwYN2TMXROoI,2117
19
+ q3dviewer/shaders/gau_frag.glsl,sha256=5_UY84tWDts59bxP8x4I-wgnzY8aGeGuo28wX--LW7E,975
20
+ q3dviewer/shaders/gau_prep.glsl,sha256=eCT9nm65uz32w8NaDjeGKhyAZh42Aea-QTwr3yQVr9U,7218
21
+ q3dviewer/shaders/gau_vert.glsl,sha256=NNbVhv_JyqZDK9iXAyBAcIHAtim7G9yWbC9IaUfTL1w,1666
22
+ q3dviewer/shaders/sort_by_key.glsl,sha256=CA2zOcbyDGYAJSJEUvgjUqNshg9NAehf8ipL3Jsv4qE,1097
23
+ q3dviewer/test/test_interpolation.py,sha256=rR_CXsYFLpn0zO0mHf_jL-naluDBMSky--FviOQga0Q,1657
24
+ q3dviewer/test/test_rendering.py,sha256=gbTcu7-cg20DgC5Zoi17C1s5lBGLfAE1rW9biqPjRsA,2164
25
+ q3dviewer/tools/__init__.py,sha256=01wG7BGM6VX0QyFBKsqPmyf2e-vrmV_N3-mo-VQ1VBg,20
26
+ q3dviewer/tools/cinematographer.py,sha256=o_24SSQ4mF062QQ7Gv3i90v7fA79PcHLB03UHXufuEA,13950
27
+ q3dviewer/tools/cloud_viewer.py,sha256=lNvOD0XWDAfgdqc2aJGigcCJsaWUS4qxadjFwf59-OY,3861
28
+ q3dviewer/tools/example_viewer.py,sha256=yeVXT0k4-h1vTLKnGzWADZD3our6XUaYUTy0p5daTkE,959
29
+ q3dviewer/tools/film_maker.py,sha256=MtmmL1-iogSmHXCZc0MLk35_bP-RMBN8twaOtTkv77w,15180
30
+ q3dviewer/tools/gaussian_viewer.py,sha256=vIwWmiFhjNmknrEkBLzt2yiegeH7LP3OeNjnGM6GzaI,1633
31
+ q3dviewer/tools/lidar_calib.py,sha256=M01bGg2mT8LwVcYybolr4UW_UUaR-f-BFciEHtjeK-w,10488
32
+ q3dviewer/tools/lidar_cam_calib.py,sha256=SYNLDvi15MX7Q3aGn771fvu1cES9xeXgP0_WmDq33w4,11200
33
+ q3dviewer/tools/ros_viewer.py,sha256=ARB3I5wohY3maP8dCu0O0hxObd6JFKuK2y7AApVgMWA,2551
34
+ q3dviewer/utils/__init__.py,sha256=irm8Z_bT8l9kzhoMlds2Dal8g4iw4vjmqNPZSs4W6e0,157
35
+ q3dviewer/utils/cloud_io.py,sha256=ttD8FJExdDhXB1Z0Ej9S939i8gcq4JfyqwLXVh8CEFw,11094
36
+ q3dviewer/utils/convert_ros_msg.py,sha256=sAoQfy3qLQKsIArBAVm8H--wlQXOcmkKK3-Ox9UCcrc,1686
37
+ q3dviewer/utils/gl_helper.py,sha256=dRY_kUqyPMr7NTcupUr6_VTvgnj53iE2C0Lk0-oFYsI,1435
38
+ q3dviewer/utils/maths.py,sha256=5TmjWUX1K3UjygXxrUsydjbo7tPzu0gD-yy7qtQUGBU,10588
39
+ q3dviewer/utils/range_slider.py,sha256=jZJQL-uQgnpgLvtYSWpKTrJlLkt3aqNpaRQAePEpNd0,3174
40
+ q3dviewer-1.0.9.dist-info/LICENSE,sha256=81cMOyNfw8KLb1JnPYngGHJ5W83gSbZEBU9MEP3tl-E,1124
41
+ q3dviewer-1.0.9.dist-info/METADATA,sha256=zD6g_ySQ3hityOMXY5cKo1wpbaYk0whJEVzQIRReqL4,275
42
+ q3dviewer-1.0.9.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
43
+ q3dviewer-1.0.9.dist-info/entry_points.txt,sha256=aeUdGH7UIgMZEMFUc-0xPZWspY95GoPdZcZuLceq85g,361
44
+ q3dviewer-1.0.9.dist-info/top_level.txt,sha256=HFFDCbGu28txcGe2HPc46A7EPaguBa_b5oH7bufmxHM,10
45
+ q3dviewer-1.0.9.dist-info/RECORD,,
@@ -1,8 +1,8 @@
1
1
  [console_scripts]
2
2
  cloud_viewer = q3dviewer.tools.cloud_viewer:main
3
+ film_maker = q3dviewer.tools.film_maker:main
3
4
  gaussian_viewer = q3dviewer.tools.gaussian_viewer:main
4
5
  lidar_calib = q3dviewer.tools.lidar_calib:main
5
6
  lidar_cam_calib = q3dviewer.tools.lidar_cam_calib:main
6
7
  mesh_viewer = q3dviewer.tools.mesh_viewer:main
7
8
  ros_viewer = q3dviewer.tools.ros_viewer:main
8
-
q3dviewer/basic_window.py DELETED
@@ -1,228 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- import numpy as np
4
- import pyqtgraph.opengl as gl
5
- from pyqtgraph.Qt import QtCore
6
- from PyQt5.QtWidgets import QWidget, QComboBox, QVBoxLayout, QHBoxLayout, QSizePolicy,\
7
- QSpacerItem, QMainWindow
8
- from OpenGL.GL import *
9
- from PyQt5.QtGui import QKeyEvent, QVector3D
10
- from PyQt5.QtWidgets import QApplication, QWidget
11
- import numpy as np
12
- import signal
13
- import sys
14
- from PyQt5.QtWidgets import QLabel, QLineEdit, QDoubleSpinBox, QSpinBox
15
-
16
-
17
- class SettingWindow(QWidget):
18
- def __init__(self):
19
- super().__init__()
20
- self.combo_items = QComboBox()
21
- self.combo_items.currentIndexChanged.connect(self.onComboboxSelection)
22
- main_layout = QVBoxLayout()
23
- self.stretch = QSpacerItem(
24
- 10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding)
25
- main_layout.addWidget(self.combo_items)
26
- self.layout = QVBoxLayout()
27
- self.layout.addItem(self.stretch)
28
- main_layout.addLayout(self.layout)
29
- self.setLayout(main_layout)
30
- self.setWindowTitle("Setting Window")
31
- self.setGeometry(200, 200, 300, 200)
32
- self.items = {}
33
-
34
- def addSetting(self, name, item):
35
- self.items.update({name: item})
36
- self.combo_items.addItem("%s(%s)" % (name, item.__class__.__name__))
37
-
38
- def clearSetting(self):
39
- while self.layout.count():
40
- child = self.layout.takeAt(0)
41
- if child.widget():
42
- child.widget().deleteLater()
43
-
44
- def onComboboxSelection(self, index):
45
- self.layout.removeItem(self.stretch)
46
- # remove all setting of previous widget
47
- self.clearSetting()
48
-
49
- key = list(self.items.keys())
50
- try:
51
- item = self.items[key[index]]
52
- item.addSetting(self.layout)
53
- self.layout.addItem(self.stretch)
54
- except AttributeError:
55
- print("%s: No setting." % (item.__class__.__name__))
56
-
57
-
58
- class ViewWidget(gl.GLViewWidget):
59
- def __init__(self):
60
- self.followed_name = 'none'
61
- self.named_items = {}
62
- self.color = '#000000'
63
- self.followable_item_name = ['none']
64
- self.setting_window = SettingWindow()
65
- super(ViewWidget, self).__init__()
66
-
67
- def onFollowableSelection(self, index):
68
- self.followed_name = self.followable_item_name[index]
69
-
70
- def update(self):
71
- if self.followed_name != 'none':
72
- pos = self.named_items[self.followed_name].T[:3, 3]
73
- self.opts['center'] = QVector3D(pos[0], pos[1], pos[2])
74
- super().update()
75
-
76
- def addSetting(self, layout):
77
- label1 = QLabel("Set background color:")
78
- label1.setToolTip("using '#xxxxxx', i.e. #FF4500")
79
- box1 = QLineEdit()
80
- box1.setToolTip("'using '#xxxxxx', i.e. #FF4500")
81
- box1.setText(str(self.color))
82
- box1.textChanged.connect(self.setBKColor)
83
- layout.addWidget(label1)
84
- layout.addWidget(box1)
85
- label2 = QLabel("Set Focus:")
86
- combo2 = QComboBox()
87
- for name in self.followable_item_name:
88
- combo2.addItem(name)
89
- combo2.currentIndexChanged.connect(self.onFollowableSelection)
90
- layout.addWidget(label2)
91
- layout.addWidget(combo2)
92
-
93
- def setBKColor(self, color):
94
- if (type(color) != str):
95
- return
96
- if color.startswith("#"):
97
- try:
98
- self.setBackgroundColor(color)
99
- self.color = color
100
- except ValueError:
101
- return
102
-
103
- def addItem(self, name, item):
104
- self.named_items.update({name: item})
105
- if (item.__class__.__name__ == 'GLAxisItem'):
106
- self.followable_item_name.append(name)
107
- self.setting_window.addSetting(name, item)
108
- super().addItem(item)
109
-
110
- def mouseReleaseEvent(self, ev):
111
- if hasattr(self, 'mousePos'):
112
- delattr(self, 'mousePos')
113
-
114
- def mouseMoveEvent(self, ev):
115
- lpos = ev.localPos()
116
- if not hasattr(self, 'mousePos'):
117
- self.mousePos = lpos
118
- diff = lpos - self.mousePos
119
- self.mousePos = lpos
120
- if ev.buttons() == QtCore.Qt.MouseButton.RightButton:
121
- self.orbit(-diff.x(), diff.y())
122
- elif ev.buttons() == QtCore.Qt.MouseButton.LeftButton:
123
- pitch_abs = np.abs(self.opts['elevation'])
124
- camera_mode = 'view-upright'
125
- if(pitch_abs <= 45.0 or pitch_abs == 90):
126
- camera_mode = 'view'
127
- self.pan(diff.x(), diff.y(), 0, relative=camera_mode)
128
-
129
- def keyPressEvent(self, ev: QKeyEvent):
130
- step = 10
131
- zoom_delta = 20
132
- speed = 2
133
- self.projectionMatrix().data()
134
-
135
- pitch_abs = np.abs(self.opts['elevation'])
136
- camera_mode = 'view-upright'
137
- if(pitch_abs <= 45.0 or pitch_abs == 90):
138
- camera_mode = 'view'
139
-
140
- if ev.key() == QtCore.Qt.Key_M: # setting meun
141
- print("Open setting windows")
142
- self.openSettingWindow()
143
- elif ev.key() == QtCore.Qt.Key_R:
144
- print("Clear viewer")
145
- for item in self.named_items.values():
146
- try:
147
- item.clear()
148
- except:
149
- pass
150
- elif ev.key() == QtCore.Qt.Key_Up:
151
- if ev.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier:
152
- self.pan(0, +step, 0, relative=camera_mode)
153
- else:
154
- self.orbit(azim=0, elev=-speed)
155
- elif ev.key() == QtCore.Qt.Key_Down:
156
- if ev.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier:
157
- self.pan(0, -step, 0, relative=camera_mode)
158
- else:
159
- self.orbit(azim=0, elev=speed)
160
- elif ev.key() == QtCore.Qt.Key_Left:
161
- if ev.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier:
162
- self.pan(+step, 0, 0, relative=camera_mode)
163
- else:
164
- self.orbit(azim=speed, elev=0)
165
-
166
- elif ev.key() == QtCore.Qt.Key_Right:
167
- if ev.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier:
168
- self.pan(-step, 0, 0, relative=camera_mode)
169
- else:
170
- self.orbit(azim=-speed, elev=0)
171
-
172
- elif ev.key() == QtCore.Qt.Key_Z:
173
- self.opts['distance'] *= 0.999**(+zoom_delta)
174
- elif ev.key() == QtCore.Qt.Key_X:
175
- self.opts['distance'] *= 0.999**(-zoom_delta)
176
- else:
177
- super().keyPressEvent(ev)
178
-
179
- def openSettingWindow(self):
180
- if self.setting_window.isVisible():
181
- self.setting_window.raise_()
182
-
183
- else:
184
- self.setting_window.show()
185
-
186
-
187
- class Viewer(QMainWindow):
188
- def __init__(self, name='Viewer', win_size=[1920, 1080], vw=ViewWidget):
189
- signal.signal(signal.SIGINT, signal.SIG_DFL)
190
- super(Viewer, self).__init__()
191
- self.vw = vw
192
- self.setGeometry(0, 0, win_size[0], win_size[1])
193
- self.initUI()
194
- self.setWindowTitle(name)
195
-
196
- def initUI(self):
197
- centerWidget = QWidget()
198
- self.setCentralWidget(centerWidget)
199
- layout = QVBoxLayout()
200
- centerWidget.setLayout(layout)
201
- self.viewerWidget = self.vw()
202
- layout.addWidget(self.viewerWidget, 1)
203
- timer = QtCore.QTimer(self)
204
- timer.setInterval(20) # period, in milliseconds
205
- timer.timeout.connect(self.update)
206
- self.viewerWidget.setCameraPosition(distance=40)
207
- timer.start()
208
-
209
- def addItems(self, named_items: dict):
210
- for name, item in named_items.items():
211
- self.viewerWidget.addItem(name, item)
212
-
213
- def __getitem__(self, name: str):
214
- if name in self.viewerWidget.named_items:
215
- return self.viewerWidget.named_items[name]
216
- else:
217
- return None
218
-
219
- def update(self):
220
- # force update by timer
221
- self.viewerWidget.update()
222
-
223
- def closeEvent(self, _):
224
- sys.exit(0)
225
-
226
- def show(self):
227
- self.viewerWidget.setting_window.addSetting("main win", self.viewerWidget)
228
- super().show()
q3dviewer/cloud_viewer.py DELETED
@@ -1,74 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- import numpy as np
4
- from q3dviewer.custom_items import *
5
- from q3dviewer.basic_window import *
6
- from pypcd4 import PointCloud
7
-
8
- class CloudViewer(Viewer):
9
- def __init__(self):
10
- super(CloudViewer, self).__init__(name="Cloud Viewer")
11
- self.setAcceptDrops(True)
12
-
13
- def dragEnterEvent(self, event):
14
- if event.mimeData().hasUrls():
15
- event.accept()
16
- else:
17
- event.ignore()
18
-
19
- def dropEvent(self, event):
20
- for url in event.mimeData().urls():
21
- file_path = url.toLocalFile()
22
- self.openCloudFile(file_path)
23
-
24
- def openCloudFile(self, file):
25
- cloud_item = self['cloud']
26
- if cloud_item is None:
27
- print("Can't find clouditem.")
28
- return
29
- if file.endswith('.pcd'):
30
- pc = PointCloud.from_path(file).pc_data
31
- if 'rgb' in pc.dtype.names:
32
- color = pc["rgb"].astype(np.uint32)
33
- cloud_item.setColorMode('RGB')
34
- elif 'intensity' in pc.dtype.names:
35
- color = pc["intensity"].astype(np.uint32)
36
- cloud_item.setColorMode('I')
37
- else:
38
- color = pc['z'].astype(np.uint32)
39
- cloud_item.setColorMode('#FFFFFF')
40
- cloud = np.rec.fromarrays(
41
- [np.stack([pc["x"], pc["y"], pc["z"]], axis=1), color],
42
- dtype=cloud_item.data_type)
43
- elif file.endswith('.npy'):
44
- pc = np.load(file)
45
- cloud = np.rec.fromarrays(
46
- [np.stack([pc[:, 0], pc[:, 1], pc[:, 2]], axis=1), pc[:, 3].astype(np.uint32)],
47
- dtype=cloud_item.data_type)
48
- cloud_item.setData(data=cloud)
49
-
50
-
51
- def main():
52
- import argparse
53
- parser = argparse.ArgumentParser()
54
- parser.add_argument("--pcd", help="the pcd path")
55
- args = parser.parse_args()
56
- app = QApplication([])
57
- viewer = CloudViewer()
58
- cloud_item = CloudIOItem(size=1, alpha=0.1)
59
- axis_item = GLAxisItem(size=0.5, width=5)
60
- gird_item = GridItem(size=1000, spacing=20)
61
- # viewer.viewerWidget.setBackgroundColor(255, 255, 255, 255)
62
-
63
- viewer.addItems({'cloud': cloud_item, 'grid': gird_item, 'axis': axis_item})
64
-
65
- if args.pcd:
66
- pcd_fn = args.pcd
67
- viewer.openCloudFile(pcd_fn)
68
-
69
- viewer.show()
70
- app.exec_()
71
-
72
-
73
- if __name__ == '__main__':
74
- main()