q3dviewer 1.0.7__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.
Files changed (38) hide show
  1. q3dviewer-1.0.7/PKG-INFO +10 -0
  2. q3dviewer-1.0.7/README.md +164 -0
  3. q3dviewer-1.0.7/q3dviewer/__init__.py +5 -0
  4. q3dviewer-1.0.7/q3dviewer/base_glwidget.py +261 -0
  5. q3dviewer-1.0.7/q3dviewer/base_item.py +57 -0
  6. q3dviewer-1.0.7/q3dviewer/custom_items/__init__.py +9 -0
  7. q3dviewer-1.0.7/q3dviewer/custom_items/axis_item.py +148 -0
  8. q3dviewer-1.0.7/q3dviewer/custom_items/cloud_io_item.py +79 -0
  9. q3dviewer-1.0.7/q3dviewer/custom_items/cloud_item.py +313 -0
  10. q3dviewer-1.0.7/q3dviewer/custom_items/frame_item.py +194 -0
  11. q3dviewer-1.0.7/q3dviewer/custom_items/gaussian_item.py +254 -0
  12. q3dviewer-1.0.7/q3dviewer/custom_items/grid_item.py +88 -0
  13. q3dviewer-1.0.7/q3dviewer/custom_items/image_item.py +172 -0
  14. q3dviewer-1.0.7/q3dviewer/custom_items/line_item.py +120 -0
  15. q3dviewer-1.0.7/q3dviewer/custom_items/text_item.py +63 -0
  16. q3dviewer-1.0.7/q3dviewer/glwidget.py +131 -0
  17. q3dviewer-1.0.7/q3dviewer/tools/__init__.py +1 -0
  18. q3dviewer-1.0.7/q3dviewer/tools/cloud_viewer.py +123 -0
  19. q3dviewer-1.0.7/q3dviewer/tools/example_viewer.py +47 -0
  20. q3dviewer-1.0.7/q3dviewer/tools/gaussian_viewer.py +60 -0
  21. q3dviewer-1.0.7/q3dviewer/tools/lidar_calib.py +294 -0
  22. q3dviewer-1.0.7/q3dviewer/tools/lidar_cam_calib.py +314 -0
  23. q3dviewer-1.0.7/q3dviewer/tools/ros_viewer.py +85 -0
  24. q3dviewer-1.0.7/q3dviewer/utils/__init__.py +4 -0
  25. q3dviewer-1.0.7/q3dviewer/utils/cloud_io.py +323 -0
  26. q3dviewer-1.0.7/q3dviewer/utils/convert_ros_msg.py +51 -0
  27. q3dviewer-1.0.7/q3dviewer/utils/gl_helper.py +40 -0
  28. q3dviewer-1.0.7/q3dviewer/utils/maths.py +168 -0
  29. q3dviewer-1.0.7/q3dviewer/utils/range_slider.py +86 -0
  30. q3dviewer-1.0.7/q3dviewer/viewer.py +58 -0
  31. q3dviewer-1.0.7/q3dviewer.egg-info/PKG-INFO +10 -0
  32. q3dviewer-1.0.7/q3dviewer.egg-info/SOURCES.txt +36 -0
  33. q3dviewer-1.0.7/q3dviewer.egg-info/dependency_links.txt +1 -0
  34. q3dviewer-1.0.7/q3dviewer.egg-info/entry_points.txt +8 -0
  35. q3dviewer-1.0.7/q3dviewer.egg-info/requires.txt +8 -0
  36. q3dviewer-1.0.7/q3dviewer.egg-info/top_level.txt +1 -0
  37. q3dviewer-1.0.7/setup.cfg +4 -0
  38. q3dviewer-1.0.7/setup.py +31 -0
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 1.0
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
+ Description: UNKNOWN
10
+ Platform: UNKNOWN
@@ -0,0 +1,164 @@
1
+ ## q3dviewer
2
+
3
+ `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 it focuses more on efficient 3D rendering.
4
+
5
+ ## Installation
6
+
7
+ To install `q3dviewer`, execute the following command in your terminal on either Linux or Windows:
8
+
9
+ ```bash
10
+ pip install q3dviewer
11
+ ```
12
+
13
+ ### Note for Windows Users
14
+
15
+ - Ensure that you have a Python 3 environment set up:
16
+ - Download and install Python 3 from the [official Python website](https://www.python.org/downloads/).
17
+ - During installation, make sure to check the "Add Python to PATH" option.
18
+
19
+ ## Tools
20
+
21
+ Once installed, you can directly use the following tools:
22
+
23
+ ### 1. Cloud Viewer
24
+
25
+ A tool for visualizing point cloud files. Launch it by executing the following command in your terminal:
26
+
27
+ ```sh
28
+ cloud_viewer # The viewer will be displayed
29
+ # Use the command below if the path is not set, though it's not recommended
30
+ python3 -m q3dviewer.tools.cloud_viewer
31
+ ```
32
+
33
+ After the viewer launches, you can drag and drop files onto the window to display the point clouds. Multiple files can be dropped simultaneously to view them together. Supported formats include LAS, PCD, PLY, and E57.
34
+
35
+ For example, you can download and check point clouds of Tokyo in LAS format from the following link:
36
+
37
+ [Tokyo Point Clouds](https://www.geospatial.jp/ckan/dataset/tokyopc-23ku-2024/resource/7807d6d1-29f3-4b36-b0c8-f7aa0ea2cff3)
38
+
39
+ ![Screenshot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149168/03c981c6-1aec-e5b9-4536-e07e1e56ff29.png)
40
+
41
+ Press `M` on your keyboard to display a menu on the screen, where you can modify visualization settings for each item. for example, You can adjust various settings such as shape, size, color, and transparency of the point clouds for CloudItem,.
42
+
43
+ ![Screenshot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149168/deeb996a-e419-58f4-6bc2-535099b1b73a.png)
44
+
45
+ ### 2. ROS Viewer
46
+
47
+ A high-performance SLAM viewer with strong compatibility with ROS, serving as an alternative to RVIZ.
48
+
49
+ ```sh
50
+ roscore &
51
+ ros_viewer
52
+ ```
53
+
54
+ ### 3. Gaussian Viewer
55
+
56
+ A simple viewer for 3D Gaussian.
57
+ see: https://github.com/scomup/EasyGaussianSplatting
58
+
59
+
60
+ ```sh
61
+ gaussian_viewer # Drag and drop your Gaussian file onto the window
62
+ ```
63
+
64
+ ![Viewer GIF](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149168/441e6f5a-214d-f7c1-11bf-5fa79e63b38e.gif)
65
+
66
+ ### 4. LiDAR-LiDAR Calibration Tools
67
+
68
+ A tool to compute the relative pose between two LiDARs. It allows for both manual adjustment in the settings screen and automatic calibration.
69
+
70
+ ```sh
71
+ lidar_calib --lidar0=/YOUR_LIDAR0_TOPIC --lidar1=/YOUR_LIDAR1_TOPIC
72
+ ```
73
+
74
+ ![LiDAR Calibration](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149168/5a8a9903-a42a-8322-1d23-0cbecd3fa99a.png)
75
+
76
+ ### 5. LiDAR-Camera Calibration Tools
77
+
78
+ A tool for calculating the relative pose between a LiDAR and a camera. It allows for manual adjustment in the settings screen and real-time verification of LiDAR point projection onto images.
79
+
80
+ ```sh
81
+ lidar_cam_calib --lidar=/YOUR_LIDAR_TOPIC --camera=/YOUR_CAMERA_TOPIC --camera_info=/YOUR_CAMERA_INFO_TOPIC
82
+ ```
83
+
84
+ ![LiDAR-Camera Calibration](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149168/f8359820-2ae7-aa37-6577-0fa035f4dd95.png)
85
+
86
+ ## Using as a Library
87
+
88
+ Using the examples above, you can easily customize and develop your own 3D viewer with `q3dviewer`. Below is a coding example.
89
+
90
+ ### Custom 3D Viewer
91
+
92
+ ```python
93
+ #!/usr/bin/env python3
94
+
95
+ import q3dviewer as q3d # Import q3dviewer
96
+
97
+ def main():
98
+ # Create a Qt application
99
+ app = q3d.QApplication([])
100
+
101
+ # Create various 3D items
102
+ axis_item = q3d.AxisItem(size=0.5, width=5)
103
+ grid_item = q3d.GridItem(size=10, spacing=1)
104
+
105
+ # Create a viewer
106
+ viewer = q3d.Viewer(name='example')
107
+ # Add items to the viewer
108
+ viewer.add_items({
109
+ 'grid': grid_item,
110
+ 'axis': axis_item,
111
+ })
112
+
113
+ # Show the viewer & run the Qt application
114
+ viewer.show()
115
+ app.exec()
116
+
117
+ if __name__ == '__main__':
118
+ main()
119
+ ```
120
+
121
+ `q3dviewer` provides the following 3D items:
122
+
123
+ - **AxisItem**: Displaying coordinate axes or self-position
124
+ - **CloudItem**: Displaying point clouds
125
+ - **CloudIOItem**: Displaying point clouds with I/O capabilities
126
+ - **GaussianItem**: Displaying 3D Gaussians
127
+ - **GridItem**: Displaying grids
128
+ - **ImageItem**: Displaying 2D images
129
+ - **Text2DItem**: Displaying 2D text
130
+ - **LineItem**: Displaying lines or trajectories
131
+
132
+ ### Developing Custom Items
133
+
134
+ In addition to the standard 3D items provided, you can visualize custom 3D items with simple coding. Below is a sample:
135
+
136
+ ```python
137
+ from OpenGL.GL import *
138
+ import numpy as np
139
+ import q3dviewer as q3d
140
+ from PySide6.QtWidgets import QLabel, QSpinBox
141
+
142
+ class YourItem(q3d.BaseItem):
143
+ def __init__(self):
144
+ super(YourItem, self).__init__()
145
+ pass # Necessary initialization
146
+
147
+ def add_setting(self, layout):
148
+ # Initialize the settings screen
149
+ label = QLabel("Add your setting:")
150
+ layout.addWidget(label)
151
+ box = QSpinBox()
152
+ layout.addWidget(box)
153
+
154
+ def set_data(self, data):
155
+ pass # Obtain the data you want to visualize
156
+
157
+ def initialize_gl(self):
158
+ pass # OpenGL initialization settings (if needed)
159
+
160
+ def paint(self):
161
+ pass # Visualize 3D objects using OpenGL
162
+ ```
163
+
164
+ Enjoy using `q3dviewer`!
@@ -0,0 +1,5 @@
1
+ from q3dviewer.custom_items import *
2
+ from q3dviewer.glwidget import *
3
+ from q3dviewer.viewer import *
4
+ from q3dviewer.base_item import *
5
+ from q3dviewer.base_glwidget import *
@@ -0,0 +1,261 @@
1
+ """
2
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
3
+ Distributed under MIT license. See LICENSE for more information.
4
+ """
5
+
6
+ from OpenGL.GL import *
7
+ from math import radians, tan
8
+ import numpy as np
9
+ from PySide6 import QtCore, QtGui, QtOpenGLWidgets
10
+ from q3dviewer.utils.maths import frustum, euler_to_matrix, makeT
11
+
12
+
13
+ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
14
+ def __init__(self, parent=None):
15
+ QtOpenGLWidgets.QOpenGLWidget.__init__(self, parent)
16
+ self.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus)
17
+ self.reset()
18
+ self._fov = 60
19
+ self.items = []
20
+ self.keyTimer = QtCore.QTimer()
21
+ self.color = np.array([0, 0, 0, 0])
22
+ self.dist = 40
23
+ self.euler = np.array([np.pi/3, 0, np.pi/4])
24
+ self.center = np.array([0, 0, 0.])
25
+ self.active_keys = set()
26
+ self.show_center = False
27
+ self.enable_show_center = True
28
+
29
+ def keyPressEvent(self, ev: QtGui.QKeyEvent):
30
+ if ev.key() == QtCore.Qt.Key_Up or \
31
+ ev.key() == QtCore.Qt.Key_Down or \
32
+ ev.key() == QtCore.Qt.Key_Left or \
33
+ ev.key() == QtCore.Qt.Key_Right or \
34
+ ev.key() == QtCore.Qt.Key_Z or \
35
+ ev.key() == QtCore.Qt.Key_X or \
36
+ ev.key() == QtCore.Qt.Key_A or \
37
+ ev.key() == QtCore.Qt.Key_D or \
38
+ ev.key() == QtCore.Qt.Key_W or \
39
+ ev.key() == QtCore.Qt.Key_S:
40
+ self.active_keys.add(ev.key())
41
+ self.active_keys.add(ev.key())
42
+
43
+ def keyReleaseEvent(self, ev: QtGui.QKeyEvent):
44
+ self.active_keys.discard(ev.key())
45
+
46
+ def current_width(self):
47
+ """
48
+ Return the current width of the widget.
49
+ """
50
+ return int(self.width() * self.devicePixelRatioF())
51
+
52
+ def current_height(self):
53
+ """
54
+ Return the current height of the widget.
55
+ """
56
+ return int(self.height() * self.devicePixelRatioF())
57
+
58
+ def reset(self):
59
+ pass
60
+
61
+ def add_item(self, item):
62
+ """
63
+ Add the item to the glwidget.
64
+ """
65
+ self.items.append(item)
66
+ item.set_glwidget(self)
67
+
68
+ def remove_item(self, item):
69
+ """
70
+ Remove the item from the glwidget.
71
+ """
72
+ self.items.remove(item)
73
+ item.set_glwidget(None)
74
+
75
+ def clear(self):
76
+ """
77
+ Remove all items from the glwidget.
78
+ """
79
+ for item in self.items:
80
+ item.set_glwidget(None)
81
+ self.items = []
82
+
83
+ def initializeGL(self):
84
+ """
85
+ the method is herted from QOpenGLWidget,
86
+ and it is called when the widget is first shown.
87
+ """
88
+ for item in self.items:
89
+ item.initialize()
90
+
91
+ def mouseReleaseEvent(self, ev):
92
+ if hasattr(self, 'mousePos'):
93
+ delattr(self, 'mousePos')
94
+
95
+ def update_dist(self, delta):
96
+ self.dist += delta
97
+ if self.dist < 0.1:
98
+ self.dist = 0.1
99
+
100
+ def wheelEvent(self, ev):
101
+ delta = ev.angleDelta().x()
102
+ if delta == 0:
103
+ delta = ev.angleDelta().y()
104
+ self.update_dist(-delta * self.dist * 0.001)
105
+ self.show_center = True
106
+
107
+ def mouseMoveEvent(self, ev):
108
+ lpos = ev.localPos()
109
+ if not hasattr(self, 'mousePos'):
110
+ self.mousePos = lpos
111
+ diff = lpos - self.mousePos
112
+ self.mousePos = lpos
113
+ if ev.buttons() == QtCore.Qt.MouseButton.RightButton:
114
+ rot_speed = 0.2
115
+ dyaw = radians(-diff.x() * rot_speed)
116
+ droll = radians(-diff.y() * rot_speed)
117
+ self.rotate(droll, 0, dyaw)
118
+ elif ev.buttons() == QtCore.Qt.MouseButton.LeftButton:
119
+ Rwc = euler_to_matrix(self.euler)
120
+ Kinv = np.linalg.inv(self.get_K())
121
+ dist = max(self.dist, 0.5)
122
+ self.center += Rwc @ Kinv @ np.array([-diff.x(), diff.y(), 0]) * dist
123
+ self.show_center = True
124
+
125
+ def paintGL(self):
126
+ pass
127
+ self.update_model_projection()
128
+ self.update_model_view()
129
+ bgcolor = self.color
130
+ glClearColor(*bgcolor)
131
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)
132
+ for item in self.items:
133
+ if not item.visible():
134
+ continue
135
+ glMatrixMode(GL_MODELVIEW)
136
+ glPushMatrix()
137
+ glPushAttrib(GL_ALL_ATTRIB_BITS)
138
+ try:
139
+ item.paint()
140
+ finally:
141
+ glPopAttrib()
142
+ glMatrixMode(GL_MODELVIEW)
143
+ glPopMatrix()
144
+
145
+ # Show center as a point if updated by mouse move event
146
+ if self.enable_show_center and self.show_center:
147
+ point_size = np.clip((self.get_K()[0, 0] / self.dist), 10, 100)
148
+ glPointSize(point_size)
149
+ glBegin(GL_POINTS)
150
+ glColor3f(1.0, 0.0, 0.0) # Red color for the center point
151
+ glVertex3f(*self.center)
152
+ glEnd()
153
+ self.show_center = False
154
+
155
+ def update_movement(self):
156
+ """
157
+ Update the movement of the camera based on the active keys.
158
+ """
159
+ if not self.active_keys:
160
+ return
161
+ rot_speed = 0.5
162
+ trans_speed = max(self.dist * 0.005, 0.1)
163
+ # Handle rotation keys
164
+ if QtCore.Qt.Key_Up in self.active_keys:
165
+ self.rotate(radians(rot_speed), 0, 0)
166
+ if QtCore.Qt.Key_Down in self.active_keys:
167
+ self.rotate(radians(-rot_speed), 0, 0)
168
+ if QtCore.Qt.Key_Left in self.active_keys:
169
+ self.rotate(0, 0, radians(rot_speed))
170
+ if QtCore.Qt.Key_Right in self.active_keys:
171
+ self.rotate(0, 0, radians(-rot_speed))
172
+ # Handle zoom keys
173
+ xz_keys = {QtCore.Qt.Key_Z, QtCore.Qt.Key_X}
174
+ if self.active_keys & xz_keys:
175
+ Rwc = euler_to_matrix(self.euler)
176
+ if QtCore.Qt.Key_Z in self.active_keys:
177
+ self.center += Rwc @ np.array([0, 0, -trans_speed])
178
+ if QtCore.Qt.Key_X in self.active_keys:
179
+ self.center += Rwc @ np.array([0, 0, trans_speed])
180
+ # Handle translation keys on the z plane
181
+ dir_keys = {QtCore.Qt.Key_W, QtCore.Qt.Key_S, QtCore.Qt.Key_A, QtCore.Qt.Key_D}
182
+ if self.active_keys & dir_keys:
183
+ Rz = euler_to_matrix([0, 0, self.euler[2]])
184
+ if QtCore.Qt.Key_W in self.active_keys:
185
+ self.center += Rz @ np.array([0, trans_speed, 0])
186
+ if QtCore.Qt.Key_S in self.active_keys:
187
+ self.center += Rz @ np.array([0, -trans_speed, 0])
188
+ if QtCore.Qt.Key_A in self.active_keys:
189
+ self.center += Rz @ np.array([-trans_speed, 0, 0])
190
+ if QtCore.Qt.Key_D in self.active_keys:
191
+ self.center += Rz @ np.array([trans_speed, 0, 0])
192
+
193
+ def update_model_view(self):
194
+ m = self.get_view_matrix()
195
+ glMatrixMode(GL_MODELVIEW)
196
+ glLoadMatrixf(m.T)
197
+
198
+ def get_view_matrix(self):
199
+ two = self.center # the origin(center) in the world frame
200
+ tco = np.array([0, 0, self.dist]) # the origin(center) in camera frame
201
+ Rwc = euler_to_matrix(self.euler)
202
+ twc = two + Rwc @ tco
203
+ Rcw = Rwc.T
204
+ tcw = -Rcw @ twc
205
+ Tcw = makeT(Rcw, tcw)
206
+ return Tcw
207
+
208
+ def set_cam_position(self, **kwargs):
209
+ pos = kwargs.get('pos', None)
210
+ distance = kwargs.get('distance', None)
211
+ if pos is not None:
212
+ self.center = pos
213
+ if distance is not None:
214
+ self.dist = distance
215
+
216
+ def set_color(self, color):
217
+ self.color = color
218
+
219
+ def update(self):
220
+ self.update_movement()
221
+ super().update()
222
+
223
+ def update_model_projection(self):
224
+ m = self.get_projection_matrix()
225
+ glMatrixMode(GL_PROJECTION)
226
+ glLoadMatrixf(m.T)
227
+
228
+ def get_projection_matrix(self):
229
+ w, h = self.current_width(), self.current_height()
230
+ dist = self.dist
231
+ near = dist * 0.001
232
+ far = dist * 10000.
233
+ r = near * tan(0.5 * radians(self._fov))
234
+ t = r * h / w
235
+ matrix = frustum(-r, r, -t, t, near, far)
236
+ return matrix
237
+
238
+ def get_K(self):
239
+ project_matrix = self.get_projection_matrix()
240
+ width = self.current_width()
241
+ height = self.current_height()
242
+ fx = project_matrix[0, 0] * width / 2
243
+ fy = project_matrix[1, 1] * height / 2
244
+ cx = width / 2
245
+ cy = height / 2
246
+ K = np.array([
247
+ [fx, 0, cx],
248
+ [0, fy, cy],
249
+ [0, 0, 1]
250
+ ])
251
+ return K
252
+
253
+ def rotate(self, rx=0, ry=0, rz=0):
254
+ # update the euler angles
255
+ self.euler += np.array([rx, ry, rz])
256
+ self.euler[2] = (self.euler[2] + np.pi) % (2 * np.pi) - np.pi
257
+ self.euler[1] = (self.euler[1] + np.pi) % (2 * np.pi) - np.pi
258
+ self.euler[0] = np.clip(self.euler[0], 0, np.pi)
259
+
260
+ def change_show_center(self, state):
261
+ self.enable_show_center = state
@@ -0,0 +1,57 @@
1
+ """
2
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
3
+ Distributed under MIT license. See LICENSE for more information.
4
+ """
5
+
6
+ from PySide6 import QtCore
7
+ import numpy as np
8
+
9
+ class BaseItem(QtCore.QObject):
10
+ _next_id = 0
11
+
12
+ def __init__(self):
13
+ super().__init__()
14
+ self._id = BaseItem._next_id
15
+ BaseItem._next_id += 1
16
+ self._glwidget = None
17
+ self._visible = True
18
+ self._initialized = False
19
+
20
+ def set_glwidget(self, v):
21
+ self._glwidget = v
22
+
23
+ def glwidget(self):
24
+ return self._glwidget
25
+
26
+ def hide(self):
27
+ self._visible = False
28
+
29
+ def show(self):
30
+ self._visible = True
31
+
32
+ def set_visible(self, vis):
33
+ self._visible = vis
34
+
35
+ def visible(self):
36
+ return self._visible
37
+
38
+ def initialize(self):
39
+ if not self._initialized:
40
+ self.initialize_gl()
41
+
42
+ def initialize_gl(self):
43
+ """
44
+ Initialize OpenGL resources for the item.
45
+ This method should be overridden by subclasses to set up any necessary OpenGL resources.
46
+ """
47
+ pass
48
+
49
+ def paint(self):
50
+ """
51
+ Render the item using OpenGL.
52
+ This method should be overridden by subclasses to perform the actual rendering.
53
+ """
54
+ pass
55
+
56
+
57
+
@@ -0,0 +1,9 @@
1
+ from q3dviewer.custom_items.axis_item import *
2
+ from q3dviewer.custom_items.cloud_item import *
3
+ from q3dviewer.custom_items.cloud_io_item import *
4
+ from q3dviewer.custom_items.gaussian_item import *
5
+ from q3dviewer.custom_items.frame_item import *
6
+ from q3dviewer.custom_items.grid_item import *
7
+ from q3dviewer.custom_items.text_item import *
8
+ from q3dviewer.custom_items.image_item import *
9
+ from q3dviewer.custom_items.line_item import *
@@ -0,0 +1,148 @@
1
+ """
2
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
3
+ Distributed under MIT license. See LICENSE for more information.
4
+ """
5
+
6
+ from q3dviewer.base_item import BaseItem
7
+ from q3dviewer.utils import set_uniform
8
+ from OpenGL.GL import *
9
+ import numpy as np
10
+ from OpenGL.GL import shaders
11
+ from PySide6.QtWidgets import QLabel, QDoubleSpinBox
12
+
13
+
14
+ # Vertex and Fragment shader source code
15
+ vertex_shader_source = """
16
+ #version 330 core
17
+ layout(location = 0) in vec3 position;
18
+ layout(location = 1) in vec3 color;
19
+
20
+ out vec3 ourColor;
21
+
22
+ uniform mat4 view_matrix;
23
+ uniform mat4 project_matrix;
24
+ uniform mat4 model_matrix;
25
+ uniform float size;
26
+
27
+ void main()
28
+ {
29
+ vec3 scaled_position = position * size;
30
+ gl_Position = project_matrix * view_matrix * model_matrix * vec4(scaled_position, 1.0);
31
+ ourColor = color;
32
+ }
33
+ """
34
+
35
+ fragment_shader_source = """
36
+ #version 330 core
37
+ in vec3 ourColor;
38
+ out vec4 color;
39
+
40
+ void main()
41
+ {
42
+ color = vec4(ourColor, 1.0);
43
+ }
44
+ """
45
+
46
+
47
+ class AxisItem(BaseItem):
48
+ def __init__(self, size=1.0, width=2):
49
+ super().__init__()
50
+ self.size = size
51
+ self.width = width
52
+ self.model_matrix = np.eye(4, dtype=np.float32)
53
+ self.need_update_setting = True
54
+
55
+ def initialize_gl(self):
56
+ # Axis vertices and colors
57
+ self.vertices = np.array([
58
+ # positions # colors
59
+ [0.0, 0.0, 0.0, 1.0, 0.0, 0.0], # X axis (red)
60
+ [1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
61
+ [0.0, 0.0, 0.0, 0.0, 1.0, 0.0], # Y axis (green)
62
+ [0.0, 1.0, 0.0, 0.0, 1.0, 0.0],
63
+ [0.0, 0.0, 0.0, 0.0, 0.0, 1.0], # Z axis (blue)
64
+ [0.0, 0.0, 1.0, 0.0, 0.0, 1.0],
65
+ ], dtype=np.float32)
66
+
67
+ self.vao = glGenVertexArrays(1)
68
+ vbo = glGenBuffers(1)
69
+
70
+ glBindVertexArray(self.vao)
71
+
72
+ glBindBuffer(GL_ARRAY_BUFFER, vbo)
73
+ glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)
74
+
75
+ # Vertex positions
76
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
77
+ glEnableVertexAttribArray(0)
78
+
79
+ # Vertex colors
80
+ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
81
+ glEnableVertexAttribArray(1)
82
+
83
+ # Compile shaders and create shader program
84
+ self.program = shaders.compileProgram(
85
+ shaders.compileShader(vertex_shader_source, GL_VERTEX_SHADER),
86
+ shaders.compileShader(fragment_shader_source, GL_FRAGMENT_SHADER),
87
+ )
88
+
89
+ glBindVertexArray(0)
90
+
91
+ def add_setting(self, layout):
92
+ label_size = QLabel("Set size:")
93
+ layout.addWidget(label_size)
94
+ spinbox_size = QDoubleSpinBox()
95
+ spinbox_size.setSingleStep(0.1)
96
+ layout.addWidget(spinbox_size)
97
+ spinbox_size.setValue(self.size)
98
+ spinbox_size.valueChanged.connect(self.set_size)
99
+ spinbox_size.setRange(0.0, 100)
100
+
101
+ label_width = QLabel("Set width:")
102
+ layout.addWidget(label_width)
103
+ spinbox_width = QDoubleSpinBox()
104
+ layout.addWidget(spinbox_width)
105
+ spinbox_width.setSingleStep(0.1)
106
+ spinbox_width.setValue(self.width)
107
+ spinbox_width.valueChanged.connect(self.set_width)
108
+ spinbox_width.setRange(0, 1000)
109
+
110
+ def set_size(self, size):
111
+ self.size = size
112
+ self.need_update_setting = True
113
+
114
+ def update_setting(self):
115
+ if not self.need_update_setting:
116
+ return
117
+ glUseProgram(self.program)
118
+ set_uniform(self.program, float(self.size), 'size')
119
+ set_uniform(self.program, self.model_matrix, 'model_matrix')
120
+ glUseProgram(0)
121
+ self.need_update_setting = False
122
+
123
+ def set_width(self, width):
124
+ self.width = width
125
+
126
+ def set_transform(self, transform):
127
+ """
128
+ Set the transformation matrix for the axis item.
129
+ """
130
+ self.model_matrix = transform
131
+ self.need_update_setting = True
132
+
133
+ def paint(self):
134
+ self.update_setting()
135
+ glLineWidth(self.width)
136
+ glUseProgram(self.program)
137
+ glBindVertexArray(self.vao)
138
+
139
+ view_matrix = self.glwidget().get_view_matrix()
140
+ project_matrix = self.glwidget().get_projection_matrix()
141
+ set_uniform(self.program, view_matrix, 'view_matrix')
142
+ set_uniform(self.program, project_matrix, 'project_matrix')
143
+
144
+ glDrawArrays(GL_LINES, 0, 6)
145
+
146
+ glBindVertexArray(0)
147
+ glUseProgram(0)
148
+ glLineWidth(1)