q3dviewer 1.1.6__py3-none-any.whl → 1.1.8__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.
Files changed (37) hide show
  1. q3dviewer/.vscode/c_cpp_properties.json +30 -0
  2. q3dviewer/.vscode/settings.json +10 -0
  3. q3dviewer/base_glwidget.py +56 -1
  4. q3dviewer/custom_items/__init__.py +1 -0
  5. q3dviewer/custom_items/cloud_io_item.py +16 -1
  6. q3dviewer/custom_items/cloud_item.py +37 -22
  7. q3dviewer/custom_items/text3d_item.py +120 -0
  8. q3dviewer/custom_items/text_item.py +19 -4
  9. q3dviewer/gau_io.py +0 -168
  10. q3dviewer/glwidget.py +23 -3
  11. q3dviewer/shaders/cloud_frag.glsl +1 -1
  12. q3dviewer/shaders/cloud_vert.glsl +13 -4
  13. q3dviewer/shaders/gau_frag.glsl +1 -1
  14. q3dviewer/shaders/gau_prep.glsl +1 -1
  15. q3dviewer/shaders/gau_vert.glsl +1 -1
  16. q3dviewer/shaders/sort_by_key.glsl +1 -1
  17. q3dviewer/test/test_interpolation.py +58 -0
  18. q3dviewer/test/test_rendering.py +73 -0
  19. q3dviewer/tools/cinematographer.py +367 -0
  20. q3dviewer/tools/cloud_viewer.py +79 -3
  21. q3dviewer/tools/example_viewer.py +8 -28
  22. q3dviewer/tools/film_maker.py +1 -1
  23. q3dviewer/tools/lidar_cam_calib.py +10 -11
  24. q3dviewer/utils/convert_ros_msg.py +49 -6
  25. q3dviewer/viewer.py +4 -1
  26. {q3dviewer-1.1.6.dist-info → q3dviewer-1.1.8.dist-info}/METADATA +4 -3
  27. q3dviewer-1.1.8.dist-info/RECORD +50 -0
  28. {q3dviewer-1.1.6.dist-info → q3dviewer-1.1.8.dist-info}/WHEEL +1 -1
  29. q3dviewer/basic_window.py +0 -228
  30. q3dviewer/cloud_viewer.py +0 -74
  31. q3dviewer/custom_items/camera_frame_item.py +0 -173
  32. q3dviewer/custom_items/trajectory_item.py +0 -79
  33. q3dviewer/utils.py +0 -71
  34. q3dviewer-1.1.6.dist-info/RECORD +0 -49
  35. {q3dviewer-1.1.6.dist-info → q3dviewer-1.1.8.dist-info}/LICENSE +0 -0
  36. {q3dviewer-1.1.6.dist-info → q3dviewer-1.1.8.dist-info}/entry_points.txt +0 -0
  37. {q3dviewer-1.1.6.dist-info → q3dviewer-1.1.8.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,9 @@ Distributed under MIT license. See LICENSE for more information.
8
8
  import numpy as np
9
9
  import q3dviewer as q3d
10
10
  from q3dviewer.Qt.QtWidgets import QVBoxLayout, QProgressBar, QDialog, QLabel
11
- from q3dviewer.Qt.QtCore import QThread, Signal
11
+ from q3dviewer.Qt.QtCore import QThread, Signal, Qt
12
+ from q3dviewer.Qt.QtGui import QKeyEvent
13
+ from q3dviewer import GLWidget
12
14
 
13
15
 
14
16
  class ProgressDialog(QDialog):
@@ -57,9 +59,57 @@ class FileLoaderThread(QThread):
57
59
  self.finished.emit()
58
60
 
59
61
 
62
+ class CustomGLWidget(GLWidget):
63
+ def __init__(self, viewer):
64
+ super().__init__()
65
+ self.viewer = viewer
66
+ self.selected_points = []
67
+
68
+ def mousePressEvent(self, event):
69
+ # ctrl + left click to add select point
70
+ # ctrl + right click to remove a point from selected points
71
+ if event.button() == Qt.LeftButton and event.modifiers() & Qt.ControlModifier:
72
+ x, y = event.x(), event.y()
73
+ p = self.get_point(x, y)
74
+ if p is not None:
75
+ self.selected_points.append(p)
76
+ self.update_marker()
77
+ elif event.button() == Qt.RightButton and event.modifiers() & Qt.ControlModifier:
78
+ if len(self.selected_points) > 0:
79
+ self.selected_points.pop()
80
+ self.update_marker()
81
+ super().mouseReleaseEvent(event)
82
+
83
+ def update_marker(self):
84
+ marks = []
85
+ for p in self.selected_points:
86
+ m = {'text': '',
87
+ 'position': p,
88
+ 'color': (0.0, 1.0, 0.0, 1.0),
89
+ 'font_size': 16,
90
+ 'point_size': 5.0,
91
+ 'line_width': 1.0}
92
+ marks.append(m)
93
+
94
+ # calculate distances between consecutive points
95
+ self.viewer['marker'].set_data(data=marks, append=False)
96
+ if len(self.selected_points) >= 2:
97
+ total_dists = 0.0
98
+ for i in range(1, len(self.selected_points)):
99
+ p1 = self.selected_points[i - 1]
100
+ p2 = self.selected_points[i]
101
+ dist = np.linalg.norm(np.array(p2) - np.array(p1))
102
+ total_dists += dist
103
+ self.viewer['text'].set_data(text=f'Total Distance: {total_dists:.2f} meter')
104
+ else:
105
+ self.viewer['text'].set_data(text='')
106
+
107
+
60
108
  class CloudViewer(q3d.Viewer):
61
109
  def __init__(self, **kwargs):
62
- super(CloudViewer, self).__init__(**kwargs)
110
+ gl_widget_class=lambda: CustomGLWidget(self)
111
+ super(CloudViewer, self).__init__(
112
+ **kwargs, gl_widget_class=gl_widget_class)
63
113
  self.setAcceptDrops(True)
64
114
 
65
115
  def dragEnterEvent(self, event):
@@ -96,8 +146,28 @@ class CloudViewer(q3d.Viewer):
96
146
  center = np.nanmean(cloud['xyz'].astype(np.float64), axis=0)
97
147
  self.glwidget.set_cam_position(center=center)
98
148
 
149
+ # print a quick help message
150
+ def print_help():
151
+ # ANSI color codes
152
+ GREEN = '\033[92m'
153
+ BLUE = '\033[94m'
154
+ BOLD = '\033[1m'
155
+ END = '\033[0m'
156
+
157
+ help_msg = f"""
158
+ {BOLD}Cloud Viewer Help:{END}
159
+ {GREEN}• Drag and drop cloud files into the viewer to load them.{END}
160
+ {GREEN}• Measure distance between points:{END}
161
+ {BLUE}- Hold Ctrl and left-click to select points on the cloud.{END}
162
+ {BLUE}- Hold Ctrl and right-click to remove the last selected point.{END}
163
+ {BLUE}- The total distance between selected points will be displayed.{END}
164
+ {GREEN}• Press 'M' to open the settings window.{END}
165
+ {BLUE}- Use the settings window to adjust item properties.{END}
166
+ """
167
+ print(help_msg)
99
168
 
100
169
  def main():
170
+ print_help()
101
171
  import argparse
102
172
  parser = argparse.ArgumentParser()
103
173
  parser.add_argument("--path", help="the cloud file path")
@@ -107,9 +177,15 @@ def main():
107
177
  cloud_item = q3d.CloudIOItem(size=1, alpha=0.1)
108
178
  axis_item = q3d.AxisItem(size=0.5, width=5)
109
179
  grid_item = q3d.GridItem(size=1000, spacing=20)
180
+ marker_item = q3d.Text3DItem() # Changed from CloudItem to Text3DItem
181
+ text_item = q3d.Text2DItem(pos=(20, 40), text="", color='lime', size=16)
110
182
 
111
183
  viewer.add_items(
112
- {'cloud': cloud_item, 'grid': grid_item, 'axis': axis_item})
184
+ {'marker': marker_item,
185
+ 'cloud': cloud_item,
186
+ 'grid': grid_item,
187
+ 'axis': axis_item,
188
+ 'text': text_item})
113
189
 
114
190
  if args.path:
115
191
  pcd_fn = args.path
@@ -1,47 +1,27 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- import numpy as np
4
- import q3dviewer as q3d
5
- from q3dviewer.Qt.QtCore import QTimer
6
-
7
- i = 0.0
8
-
9
- def update(viewer):
10
- global i
11
- i += 0.05
12
- new = np.array([np.sin(i) * i, np.cos(i) * i, i])
13
- viewer['traj'].set_data(new, append=True)
14
- viewer.update()
15
- return i
16
-
3
+ import q3dviewer as q3d # Import q3dviewer
17
4
 
18
5
  def main():
19
- # add a qt application
6
+ # Create a Qt application
20
7
  app = q3d.QApplication([])
21
8
 
22
- # create opengl items
9
+ # Create various 3D items
23
10
  axis_item = q3d.AxisItem(size=0.5, width=5)
24
11
  grid_item = q3d.GridItem(size=10, spacing=1)
25
- traj_item = q3d.LineItem(width=2)
26
12
 
27
- # create viewer
13
+ # Create a viewer
28
14
  viewer = q3d.Viewer(name='example')
29
-
30
- # add all items to viewer
15
+
16
+ # Add items to the viewer
31
17
  viewer.add_items({
32
18
  'grid': grid_item,
33
19
  'axis': axis_item,
34
- 'traj': traj_item})
35
-
36
- # dynamic update by timer
37
- timer = QTimer()
38
- timer.timeout.connect(lambda: update(viewer))
39
- timer.start(100)
20
+ })
40
21
 
41
- # show viewer and start application
22
+ # Show the viewer & run the Qt application
42
23
  viewer.show()
43
24
  app.exec()
44
25
 
45
-
46
26
  if __name__ == '__main__':
47
27
  main()
@@ -417,7 +417,7 @@ def main():
417
417
  args = parser.parse_args()
418
418
  app = q3d.QApplication(['Film Maker'])
419
419
  viewer = CMMViewer(name='Film Maker', update_interval=30)
420
- cloud_item = q3d.CloudIOItem(size=0.1, point_type='SPHERE', alpha=0.5, depth_test=True)
420
+ cloud_item = q3d.CloudIOItem(size=1, point_type='SPHERE', alpha=0.5, depth_test=True)
421
421
  grid_item = q3d.GridItem(size=1000, spacing=20)
422
422
 
423
423
  viewer.add_items(
@@ -17,8 +17,7 @@ import cv2
17
17
  import argparse
18
18
  from q3dviewer.utils.convert_ros_msg import convert_pointcloud2_msg, convert_image_msg
19
19
  from q3dviewer.utils.maths import euler_to_matrix, matrix_to_quaternion, matrix_to_euler
20
-
21
-
20
+ from q3dviewer.utils.helpers import rainbow
22
21
  clouds = []
23
22
  remap_info = None
24
23
  K = None
@@ -188,11 +187,11 @@ class LidarCamViewer(q3d.Viewer):
188
187
  def camera_info_cb(data):
189
188
  global remap_info, K
190
189
  if remap_info is None: # Initialize only once
191
- K = np.array(data.K).reshape(3, 3)
192
- D = np.array(data.D)
190
+ K = np.array([2.4480877131144061e+02, 0., 4.7808179466584482e+02, 0., 2.4540886113128877e+02, 2.8080868163348435e+02, 0., 0., 1.]).reshape(3, 3)
191
+ D = np.array([0,0,0,0,0])
193
192
  rospy.loginfo("Camera intrinsic parameters set")
194
- height = data.height
195
- width = data.width
193
+ height = 540
194
+ width = 960
196
195
  mapx, mapy = cv2.initUndistortRectifyMap(
197
196
  K, D, None, K, (width, height), cv2.CV_32FC1)
198
197
  remap_info = [mapx, mapy]
@@ -254,7 +253,7 @@ def image_cb(data):
254
253
  intensity = cloud_local['irgb'][u_mask][valid_points] >> 24
255
254
  vmin = viewer['scan'].vmin
256
255
  vmax = viewer['scan'].vmax
257
- intensity_color = q3d.rainbow(intensity, scalar_min=vmin, scalar_max=vmax).astype(np.uint8)
256
+ intensity_color = rainbow(intensity, scalar_min=vmin, scalar_max=vmax).astype(np.uint8)
258
257
  draw_image = image_un.copy()
259
258
  draw_image = draw_larger_points(draw_image, u, intensity_color, radius)
260
259
  rgb = image_un[u[:, 1], u[:, 0]]
@@ -288,14 +287,14 @@ def main():
288
287
  scan_item = q3d.CloudItem(size=2, alpha=1, color_mode='I')
289
288
  img_item = q3d.ImageItem(pos=np.array([0, 0]), size=np.array([800, 600]))
290
289
  viewer.add_items({'scan': scan_item, 'grid': grid_item, 'img': img_item})
290
+ camera_info_cb(None)
291
291
 
292
292
  rospy.init_node('lidar_cam_calib', anonymous=True)
293
293
 
294
294
  # Use topic names from arguments
295
- rospy.Subscriber(args.lidar, PointCloud2, scan_cb, queue_size=1)
296
- rospy.Subscriber(args.camera, Image, image_cb, queue_size=1)
297
- rospy.Subscriber(args.camera_info, CameraInfo,
298
- camera_info_cb, queue_size=1)
295
+ rospy.Subscriber("cloud", PointCloud2, scan_cb, queue_size=1)
296
+ rospy.Subscriber("/usb_cam/image_raw", Image, image_cb, queue_size=1)
297
+ # rospy.Subscriber(args.camera_info, CameraInfo, camera_info_cb, queue_size=1)
299
298
 
300
299
  viewer.show()
301
300
  app.exec()
@@ -4,18 +4,59 @@ Distributed under MIT license. See LICENSE for more information.
4
4
  """
5
5
 
6
6
  import numpy as np
7
- from pypcd4 import PointCloud
8
7
  from q3dviewer.utils.maths import make_transform
9
8
 
9
+ def get_dtype(msg):
10
+ dtype_map = {
11
+ 1: 'i1', # INT8
12
+ 2: 'u1', # UINT8
13
+ 3: 'i2', # INT16
14
+ 4: 'u2', # UINT16
15
+ 5: 'i4', # INT32
16
+ 6: 'u4', # UINT32
17
+ 7: 'f4', # FLOAT32
18
+ 8: 'f8' # FLOAT64
19
+ }
20
+
21
+ point_step = msg.point_step
22
+ dtype_list = []
23
+ for field in msg.fields:
24
+ dtype_str = dtype_map.get(field.datatype, 'f4')
25
+ dtype_list.append((field.name, dtype_str, field.offset))
26
+
27
+ dtype_list.sort(key=lambda x: x[2])
28
+
29
+ structured_dtype = []
30
+ last_offset = 0
31
+
32
+ for field_name, field_dtype, offset in dtype_list:
33
+ if offset > last_offset:
34
+ padding_size = offset - last_offset
35
+ if padding_size > 0:
36
+ structured_dtype.append((f'pad{last_offset}', f'u1', padding_size))
37
+
38
+ structured_dtype.append((field_name, field_dtype))
39
+ last_offset = offset + np.dtype(field_dtype).itemsize
40
+
41
+ if point_step > last_offset:
42
+ final_padding = point_step - last_offset
43
+ structured_dtype.append((f'pad{last_offset}', f'u1', final_padding))
44
+ return structured_dtype
45
+
10
46
 
11
47
  def convert_pointcloud2_msg(msg):
12
- pc = PointCloud.from_msg(msg).pc_data
48
+ # Build dtype with proper offsets for PointCloud2 message
49
+ structured_dtype = get_dtype(msg)
50
+ pc = np.frombuffer(msg.data, dtype=structured_dtype)
51
+ # pc = PointCloud.from_msg(msg).pc_data
13
52
  data_type = [('xyz', '<f4', (3,)), ('irgb', '<u4')]
14
53
  rgb = np.zeros([pc.shape[0]], dtype=np.uint32)
15
54
  intensity = np.zeros([pc.shape[0]], dtype=np.uint32)
16
55
  fields = ['xyz']
17
56
  if 'intensity' in pc.dtype.names:
18
- intensity = pc['intensity'].astype(np.uint32)
57
+ intensity = pc['intensity']
58
+ intensity = np.clip(intensity, 0, 255)
59
+ intensity = intensity.astype(np.uint32)
19
60
  fields.append('intensity')
20
61
  if 'rgb' in pc.dtype.names:
21
62
  rgb = pc['rgb'].view(np.uint32)
@@ -42,10 +83,12 @@ def convert_odometry_msg(msg):
42
83
  return transform, stamp
43
84
 
44
85
 
45
- def convert_image_msg(msg):
86
+ def convert_image_msg(msg, bgr=False):
46
87
  image = np.frombuffer(msg.data, dtype=np.uint8).reshape(
47
88
  msg.height, msg.width, -1)
48
- if (msg.encoding == 'bgr8'):
49
- image = image[:, :, ::-1] # convert bgr to rgb
89
+ # if bgr is True return BGR image, else return RGB image
90
+ if (bgr and msg.encoding == 'rgb8') or (not bgr and msg.encoding == 'bgr8'):
91
+ image = image[:, :, ::-1]
92
+
50
93
  stamp = msg.header.stamp.to_sec()
51
94
  return image, stamp
q3dviewer/viewer.py CHANGED
@@ -17,7 +17,7 @@ def handler(signal, frame):
17
17
  class Viewer(QMainWindow):
18
18
  def __init__(self, name='Viewer', win_size=[1920, 1080],
19
19
  gl_widget_class=GLWidget, update_interval=20):
20
- signal.signal(signal.SIGINT, handler)
20
+ self.set_quit_handler(handler)
21
21
  super(Viewer, self).__init__()
22
22
  self.setGeometry(0, 0, win_size[0], win_size[1])
23
23
  self.gl_widget_class = gl_widget_class
@@ -27,6 +27,9 @@ class Viewer(QMainWindow):
27
27
  self.setWindowTitle(name)
28
28
  self.installEventFilter(self)
29
29
 
30
+ def set_quit_handler(self, handler):
31
+ signal.signal(signal.SIGINT, handler)
32
+
30
33
  def init_ui(self):
31
34
  center_widget = QWidget()
32
35
  self.setCentralWidget(center_widget)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: q3dviewer
3
- Version: 1.1.6
3
+ Version: 1.1.8
4
4
  Summary: A library designed for quickly deploying a 3D viewer.
5
5
  Home-page: https://github.com/scomup/q3dviewer
6
6
  Author: Liu Yang
@@ -26,7 +26,7 @@ Requires-Dist: matplotlib
26
26
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
27
27
  [![PyPI version](https://badge.fury.io/py/q3dviewer.svg)](https://badge.fury.io/py/q3dviewer)
28
28
 
29
- `q3dviewer` is a library designed for quickly deploying a 3D viewer. It is based on Qt (PySide6) and provides efficient OpenGL items for displaying 3D objects (e.g., point clouds, cameras, and 3D Gaussians). You can use it to visualize your 3D data or set up an efficient viewer application. It is inspired by PyQtGraph but focuses more on efficient 3D rendering.
29
+ `q3dviewer` is a library designed for quickly deploying a 3D viewer. It is based on Qt and provides efficient OpenGL items for displaying 3D objects (e.g., point clouds, cameras, and 3D Gaussians). You can use it to visualize your 3D data or set up an efficient viewer application. It is inspired by PyQtGraph but focuses more on efficient 3D rendering.
30
30
 
31
31
 
32
32
  To show how to use `q3dviewer` as a library, we also provide some [very useful tools](#tools).
@@ -195,6 +195,7 @@ if __name__ == '__main__':
195
195
  - **GridItem**: Displays grids.
196
196
  - **ImageItem**: Displays 2D images.
197
197
  - **Text2DItem**: Displays 2D text.
198
+ - **Text3DItem**: Displays 3D test and mark.
198
199
  - **LineItem**: Displays lines or trajectories.
199
200
 
200
201
  ### Developing Custom Items
@@ -205,7 +206,7 @@ In addition to the standard 3D items provided, you can visualize custom 3D items
205
206
  from OpenGL.GL import *
206
207
  import numpy as np
207
208
  import q3dviewer as q3d
208
- from PySide6.QtWidgets import QLabel, QSpinBox
209
+ from q3dviewer.Qt.QtWidgets import QLabel, QSpinBox
209
210
 
210
211
  class YourItem(q3d.BaseItem):
211
212
  def __init__(self):
@@ -0,0 +1,50 @@
1
+ q3dviewer/__init__.py,sha256=cjyfUE5zK6xohDGDQIWfb0DKkWChVznBd7CrVLg7whQ,376
2
+ q3dviewer/base_glwidget.py,sha256=0AWYwOntyCSterxOebTg_VwStPYV6k1H_BDRGHl41bo,14382
3
+ q3dviewer/base_item.py,sha256=63MarHyoWszPL40ox-vPoOAQ1N4ypekOjoRARdPik-E,1755
4
+ q3dviewer/gau_io.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ q3dviewer/glwidget.py,sha256=EmrxPtVQ8RdPK5INKMlpKpVfX0KCfjSKRdGf4cSB1f0,5405
6
+ q3dviewer/viewer.py,sha256=Vq3ucDlBcBBoiVVGmqG1sRjhLePl50heblx6wJpsc1A,2603
7
+ q3dviewer/.vscode/c_cpp_properties.json,sha256=rcEXwMKO_Fve86bpEeEhoWRKzdLSVuG5dbNCtE5U3Fw,1037
8
+ q3dviewer/.vscode/settings.json,sha256=3G4gE-fW3vfObgaZrlJzAG7vym2pIfr0fNnL8Jo3RJo,322
9
+ q3dviewer/Qt/__init__.py,sha256=CcwS6oSXBXTMr58JNbRNYcPMVubDD2jiPtJ55DoLm8o,2199
10
+ q3dviewer/custom_items/__init__.py,sha256=cwcNn40MzYXVO9eSzpRknJdzZNKPf2kaEMZy5DCNYcE,567
11
+ q3dviewer/custom_items/axis_item.py,sha256=-WM2urosqV847zpTpOtxdLjb7y9NJqFCH13qqodcCTg,2572
12
+ q3dviewer/custom_items/cloud_io_item.py,sha256=Haz-SOUUCPDSHgmKyyyFfP7LXBSEiN4r8xmchQwCm-k,4721
13
+ q3dviewer/custom_items/cloud_item.py,sha256=NkZ5rFM_hVxBPMZYml3oX2CI5d0ApJY1wet8I1eIssE,14024
14
+ q3dviewer/custom_items/frame_item.py,sha256=bUzww3tSDah0JZeqtU6_cYHhhTVWzXhJVMcAa5pCXHI,7458
15
+ q3dviewer/custom_items/gaussian_item.py,sha256=JMubpahkTPh0E8ShL3FLTahv0e35ODzjgK5K1i0YXSU,9884
16
+ q3dviewer/custom_items/grid_item.py,sha256=LDB_MYACoxld-xvz01_MfAf12vLcRkH7R_WtGHHdSgk,4945
17
+ q3dviewer/custom_items/image_item.py,sha256=k7HNTqdL2ckTbxMx7A7eKaP4aksZ85-pBjNdbpm6PXM,5355
18
+ q3dviewer/custom_items/line_item.py,sha256=rel-lx8AgjDY7qyIecHxHQZzaswRn2ZTiOIjB_0Mrqo,4444
19
+ q3dviewer/custom_items/text3d_item.py,sha256=mQX-8ldUsPcM3ef77fevsXqVJY0_lnNsxCGrtpF4b-A,4167
20
+ q3dviewer/custom_items/text_item.py,sha256=toeGjBu7RtT8CMUuaDWnmXPnA1UKHhnCzUNeonGczSo,2703
21
+ q3dviewer/shaders/cloud_frag.glsl,sha256=psKVt9qI6BW0bCqOk4lcKqUd6XgYGtdFigyN9OdYSNI,609
22
+ q3dviewer/shaders/cloud_vert.glsl,sha256=gKI6EJrzX5ga2W2yjU6x7Wjz7Cu2Y-wrPl4g10RfTLM,2376
23
+ q3dviewer/shaders/gau_frag.glsl,sha256=vWt5I3Ojrc2PCxRlBJGyJhujbveSicMA54T01Fk293A,975
24
+ q3dviewer/shaders/gau_prep.glsl,sha256=0BiWhYCQGeX2iN-e7m3dy1xWXqWrErErRAzHlcmWHF0,7218
25
+ q3dviewer/shaders/gau_vert.glsl,sha256=_rkm51zaWgPDJ-otJL-WX12fDvnPBOTooVfqo21Rexs,1666
26
+ q3dviewer/shaders/sort_by_key.glsl,sha256=M5RK6uRDp40vVH6XtBIrdJTcYatqXyZwd6kCzEa2DZg,1097
27
+ q3dviewer/test/test_interpolation.py,sha256=rR_CXsYFLpn0zO0mHf_jL-naluDBMSky--FviOQga0Q,1657
28
+ q3dviewer/test/test_rendering.py,sha256=CrJkJjxkcizZxC4MVyDuJjY_41-eeiD5u0vD_8VFHgU,2206
29
+ q3dviewer/tools/__init__.py,sha256=01wG7BGM6VX0QyFBKsqPmyf2e-vrmV_N3-mo-VQ1VBg,20
30
+ q3dviewer/tools/cinematographer.py,sha256=o_24SSQ4mF062QQ7Gv3i90v7fA79PcHLB03UHXufuEA,13950
31
+ q3dviewer/tools/cloud_viewer.py,sha256=UsKALGiFR2Ck___LSGNPIZ3PPUlECO20jVkcP5g6yyc,6807
32
+ q3dviewer/tools/example_viewer.py,sha256=C867mLnCBjawS6LGgRsJ_c6-6wztfL9vOBQt85KbbdU,572
33
+ q3dviewer/tools/film_maker.py,sha256=xLFgRhFWoMQ37qlvcu1lXWaTWXMNRYlRcZFfHW5JtmQ,16676
34
+ q3dviewer/tools/gaussian_viewer.py,sha256=vIwWmiFhjNmknrEkBLzt2yiegeH7LP3OeNjnGM6GzaI,1633
35
+ q3dviewer/tools/lidar_calib.py,sha256=hHnsSaQh_Pkdh8tPntt0MgEW26nQyAdC_HQHq4I3sw8,10562
36
+ q3dviewer/tools/lidar_cam_calib.py,sha256=4CDcZZiFZDeKo2Y2_lXF9tfbiF9dPsz0OjppQdxQsU4,11430
37
+ q3dviewer/tools/ros_viewer.py,sha256=ARB3I5wohY3maP8dCu0O0hxObd6JFKuK2y7AApVgMWA,2551
38
+ q3dviewer/utils/__init__.py,sha256=dwTNAAebTiKY4ygv2G1O-w6-TbJnmnNVO2UfJXvJhaQ,107
39
+ q3dviewer/utils/cloud_io.py,sha256=xarfYakY0zgKwvZkgKSPO6b4DEo42hsq3mcvCbK64yg,12134
40
+ q3dviewer/utils/convert_ros_msg.py,sha256=lNbLIawJfwp3VzygdW3dUXkfSG8atg_CoZbQFmt8H70,3142
41
+ q3dviewer/utils/gl_helper.py,sha256=dRY_kUqyPMr7NTcupUr6_VTvgnj53iE2C0Lk0-oFYsI,1435
42
+ q3dviewer/utils/helpers.py,sha256=SqR4YTQZi13FKbkVUYgodXce1JJ_YmrHEIRkUmnIUas,3085
43
+ q3dviewer/utils/maths.py,sha256=zHaPtvVZIuo8xepIXCMeSL9tpx8FahUrq0l4K1oXrBk,8834
44
+ q3dviewer/utils/range_slider.py,sha256=9djTxuzmzH54DgSwAljRpLGjsrIJ0hTxhaxFjPxsk8g,4007
45
+ q3dviewer-1.1.8.dist-info/LICENSE,sha256=81cMOyNfw8KLb1JnPYngGHJ5W83gSbZEBU9MEP3tl-E,1124
46
+ q3dviewer-1.1.8.dist-info/METADATA,sha256=oAsXecjioM4FjtdSDR-8nOjte6sAMxyA0nAklwItxsQ,8014
47
+ q3dviewer-1.1.8.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
48
+ q3dviewer-1.1.8.dist-info/entry_points.txt,sha256=aeUdGH7UIgMZEMFUc-0xPZWspY95GoPdZcZuLceq85g,361
49
+ q3dviewer-1.1.8.dist-info/top_level.txt,sha256=HFFDCbGu28txcGe2HPc46A7EPaguBa_b5oH7bufmxHM,10
50
+ q3dviewer-1.1.8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.3.2)
2
+ Generator: bdist_wheel (0.34.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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()