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.
@@ -25,6 +25,9 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
25
25
  self.active_keys = set()
26
26
  self.show_center = False
27
27
  self.enable_show_center = True
28
+ self.view_need_update = True
29
+ self.view_matrix = self.get_view_matrix()
30
+ self.projection_matrix = self.get_projection_matrix()
28
31
 
29
32
  def keyPressEvent(self, ev: QtGui.QKeyEvent):
30
33
  if ev.key() == QtCore.Qt.Key_Up or \
@@ -87,15 +90,29 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
87
90
  """
88
91
  for item in self.items:
89
92
  item.initialize()
93
+ # initialize the projection matrix and model view matrix
94
+ self.projection_matrix = self.get_projection_matrix()
95
+ self.update_model_projection()
96
+ self.view_matrix = self.get_view_matrix()
97
+ self.update_model_view()
98
+
99
+ def set_view_matrix(self, view_matrix):
100
+ self.view_matrix = view_matrix
101
+ self.view_need_update = False
90
102
 
91
103
  def mouseReleaseEvent(self, ev):
92
104
  if hasattr(self, 'mousePos'):
93
105
  delattr(self, 'mousePos')
94
106
 
107
+ def set_dist(self, dist):
108
+ self.dist = dist
109
+ self.view_need_update = True
110
+
95
111
  def update_dist(self, delta):
96
112
  self.dist += delta
97
113
  if self.dist < 0.1:
98
114
  self.dist = 0.1
115
+ self.view_need_update = True
99
116
 
100
117
  def wheelEvent(self, ev):
101
118
  delta = ev.angleDelta().x()
@@ -121,17 +138,32 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
121
138
  dist = max(self.dist, 0.5)
122
139
  self.center += Rwc @ Kinv @ np.array([-diff.x(), diff.y(), 0]) * dist
123
140
  self.show_center = True
141
+ self.view_need_update = True
142
+
143
+ def set_center(self, center):
144
+ self.center = center
145
+ self.view_need_update = True
124
146
 
125
147
  def paintGL(self):
126
- pass
127
- self.update_model_projection()
148
+ # if the camera is moved, update the model view matrix.
149
+ if self.view_need_update:
150
+ self.view_matrix = self.get_view_matrix()
151
+ self.view_need_update = False
128
152
  self.update_model_view()
153
+
154
+ # set the background color
129
155
  bgcolor = self.color
130
156
  glClearColor(*bgcolor)
131
157
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)
132
158
  for item in self.items:
133
159
  if not item.visible():
134
160
  continue
161
+ if not item.is_initialized():
162
+ """
163
+ The item may not be initialized if it is added
164
+ after the widget is shown, so we need to initialize it here.
165
+ """
166
+ item.initialize()
135
167
  glMatrixMode(GL_MODELVIEW)
136
168
  glPushMatrix()
137
169
  glPushAttrib(GL_ALL_ATTRIB_BITS)
@@ -189,11 +221,11 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
189
221
  self.center += Rz @ np.array([-trans_speed, 0, 0])
190
222
  if QtCore.Qt.Key_D in self.active_keys:
191
223
  self.center += Rz @ np.array([trans_speed, 0, 0])
224
+ self.view_need_update = True
192
225
 
193
226
  def update_model_view(self):
194
- m = self.get_view_matrix()
195
227
  glMatrixMode(GL_MODELVIEW)
196
- glLoadMatrixf(m.T)
228
+ glLoadMatrixf(self.view_matrix.T)
197
229
 
198
230
  def get_view_matrix(self):
199
231
  two = self.center # the origin(center) in the world frame
@@ -209,9 +241,9 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
209
241
  pos = kwargs.get('pos', None)
210
242
  distance = kwargs.get('distance', None)
211
243
  if pos is not None:
212
- self.center = pos
244
+ self.set_center(pos)
213
245
  if distance is not None:
214
- self.dist = distance
246
+ self.set_dist(distance)
215
247
 
216
248
  def set_color(self, color):
217
249
  self.color = color
@@ -221,9 +253,8 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
221
253
  super().update()
222
254
 
223
255
  def update_model_projection(self):
224
- m = self.get_projection_matrix()
225
256
  glMatrixMode(GL_PROJECTION)
226
- glLoadMatrixf(m.T)
257
+ glLoadMatrixf(self.projection_matrix.T)
227
258
 
228
259
  def get_projection_matrix(self):
229
260
  w, h = self.current_width(), self.current_height()
@@ -259,3 +290,17 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
259
290
 
260
291
  def change_show_center(self, state):
261
292
  self.enable_show_center = state
293
+
294
+ def resizeEvent(self, event):
295
+ super().resizeEvent(event)
296
+ self.projection_matrix = self.get_projection_matrix()
297
+ self.update_model_projection()
298
+
299
+ def capture_frame(self):
300
+ self.makeCurrent() # Ensure the OpenGL context is current
301
+ width = self.current_width()
302
+ height = self.current_height()
303
+ pixels = glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE)
304
+ frame = np.frombuffer(pixels, dtype=np.uint8).reshape(height, width, 3)
305
+ frame = np.flip(frame, 0)
306
+ return frame
q3dviewer/base_item.py CHANGED
@@ -16,6 +16,7 @@ class BaseItem(QtCore.QObject):
16
16
  self._glwidget = None
17
17
  self._visible = True
18
18
  self._initialized = False
19
+ self._disable_setting = False
19
20
 
20
21
  def set_glwidget(self, v):
21
22
  self._glwidget = v
@@ -38,6 +39,17 @@ class BaseItem(QtCore.QObject):
38
39
  def initialize(self):
39
40
  if not self._initialized:
40
41
  self.initialize_gl()
42
+ self._initialized = True
43
+
44
+ def is_initialized(self):
45
+ return self._initialized
46
+
47
+ def add_setting(self, layout):
48
+ """
49
+ Add setting widgets to the layout.
50
+ This method should be overridden by subclasses to add any necessary setting widgets to the layout.
51
+ """
52
+ pass
41
53
 
42
54
  def initialize_gl(self):
43
55
  """
@@ -53,5 +65,8 @@ class BaseItem(QtCore.QObject):
53
65
  """
54
66
  pass
55
67
 
68
+ def disable_setting(self):
69
+ self._disable_setting = True
70
+
56
71
 
57
72
 
@@ -4,44 +4,9 @@ Distributed under MIT license. See LICENSE for more information.
4
4
  """
5
5
 
6
6
  from q3dviewer.base_item import BaseItem
7
- from q3dviewer.utils import set_uniform
8
7
  from OpenGL.GL import *
9
8
  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
- """
9
+ from PySide6.QtWidgets import QDoubleSpinBox
45
10
 
46
11
 
47
12
  class AxisItem(BaseItem):
@@ -49,76 +14,45 @@ class AxisItem(BaseItem):
49
14
  super().__init__()
50
15
  self.size = size
51
16
  self.width = width
52
- self.model_matrix = np.eye(4, dtype=np.float32)
17
+ self.T = np.eye(4, dtype=np.float32)
53
18
  self.need_update_setting = True
54
19
 
55
20
  def initialize_gl(self):
56
- # Axis vertices and colors
21
+ # Axis vertices
57
22
  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],
23
+ # positions
24
+ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], # X axis
25
+ [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], # Y axis
26
+ [0.0, 0.0, 0.0], [0.0, 0.0, 1.0], # Z axis
65
27
  ], dtype=np.float32)
66
28
 
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
- )
29
+ # Axis colors
30
+ self.colors = np.array([
31
+ [1.0, 0.0, 0.0], [1.0, 0.0, 0.0], # X axis (red)
32
+ [0.0, 1.0, 0.0], [0.0, 1.0, 0.0], # Y axis (green)
33
+ [0.0, 0.0, 1.0], [0.0, 0.0, 1.0], # Z axis (blue)
34
+ ], dtype=np.float32)
88
35
 
89
- glBindVertexArray(0)
90
36
 
91
37
  def add_setting(self, layout):
92
- label_size = QLabel("Set size:")
93
- layout.addWidget(label_size)
94
38
  spinbox_size = QDoubleSpinBox()
39
+ spinbox_size.setPrefix("Size: ")
95
40
  spinbox_size.setSingleStep(0.1)
96
- layout.addWidget(spinbox_size)
97
41
  spinbox_size.setValue(self.size)
98
- spinbox_size.valueChanged.connect(self.set_size)
99
42
  spinbox_size.setRange(0.0, 100)
43
+ spinbox_size.valueChanged.connect(self.set_size)
44
+ layout.addWidget(spinbox_size)
100
45
 
101
- label_width = QLabel("Set width:")
102
- layout.addWidget(label_width)
103
46
  spinbox_width = QDoubleSpinBox()
104
- layout.addWidget(spinbox_width)
47
+ spinbox_width.setPrefix("Width: ")
105
48
  spinbox_width.setSingleStep(0.1)
106
49
  spinbox_width.setValue(self.width)
107
- spinbox_width.valueChanged.connect(self.set_width)
108
50
  spinbox_width.setRange(0, 1000)
51
+ spinbox_width.valueChanged.connect(self.set_width)
52
+ layout.addWidget(spinbox_width)
109
53
 
110
54
  def set_size(self, size):
111
55
  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
56
 
123
57
  def set_width(self, width):
124
58
  self.width = width
@@ -127,22 +61,25 @@ class AxisItem(BaseItem):
127
61
  """
128
62
  Set the transformation matrix for the axis item.
129
63
  """
130
- self.model_matrix = transform
64
+ self.T = transform
131
65
  self.need_update_setting = True
132
66
 
133
67
  def paint(self):
134
- self.update_setting()
135
68
  glLineWidth(self.width)
136
- glUseProgram(self.program)
137
- glBindVertexArray(self.vao)
138
69
 
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')
70
+ glPushMatrix()
71
+ glMultMatrixf(self.T.T)
72
+
73
+ glEnableClientState(GL_VERTEX_ARRAY)
74
+ glVertexPointer(3, GL_FLOAT, 0, self.vertices * self.size)
75
+
76
+ glEnableClientState(GL_COLOR_ARRAY)
77
+ glColorPointer(3, GL_FLOAT, 0, self.colors)
78
+
79
+ glDrawArrays(GL_LINES, 0, len(self.vertices))
143
80
 
144
- glDrawArrays(GL_LINES, 0, 6)
81
+ glDisableClientState(GL_VERTEX_ARRAY)
82
+ glDisableClientState(GL_COLOR_ARRAY)
83
+ glPopMatrix()
145
84
 
146
- glBindVertexArray(0)
147
- glUseProgram(0)
148
85
  glLineWidth(1)
@@ -20,7 +20,11 @@ from PySide6.QtGui import QRegularExpressionValidator
20
20
 
21
21
  # draw points with color (x, y, z, color)
22
22
  class CloudItem(BaseItem):
23
- def __init__(self, size, alpha, color_mode='I', color='#ffffff', point_type='PIXEL'):
23
+ def __init__(self, size, alpha,
24
+ color_mode='I',
25
+ color='#ffffff',
26
+ point_type='PIXEL',
27
+ depth_test=False):
24
28
  super().__init__()
25
29
  self.STRIDE = 16 # stride of cloud array
26
30
  self.valid_buff_top = 0
@@ -42,11 +46,11 @@ class CloudItem(BaseItem):
42
46
  self.need_update_setting = True
43
47
  self.max_cloud_size = 300000000
44
48
  # Enable depth test when full opaque
45
- self.depth_test_enabled = (alpha == 1)
49
+ self.depth_test = depth_test
46
50
  self.path = os.path.dirname(__file__)
47
51
 
48
52
  def add_setting(self, layout):
49
- label_ptype = QLabel("Set point display type:")
53
+ label_ptype = QLabel("Point Type:")
50
54
  layout.addWidget(label_ptype)
51
55
  combo_ptype = QComboBox()
52
56
  combo_ptype.addItem("pixels")
@@ -55,26 +59,25 @@ class CloudItem(BaseItem):
55
59
  combo_ptype.setCurrentIndex(self.point_type_table[self.point_type])
56
60
  combo_ptype.currentIndexChanged.connect(self._on_point_type_selection)
57
61
  layout.addWidget(combo_ptype)
58
- self.label_size = QLabel("Set size: (pixel)")
59
- layout.addWidget(self.label_size)
62
+
60
63
  self.box_size = QDoubleSpinBox()
64
+ self.box_size.setPrefix("Size: ")
61
65
  self.box_size.setSingleStep(1)
62
66
  self.box_size.setDecimals(0)
63
- layout.addWidget(self.box_size)
64
67
  self.box_size.setValue(self.size)
65
- self.box_size.valueChanged.connect(self.set_size)
66
68
  self.box_size.setRange(0, 100)
69
+ self.box_size.valueChanged.connect(self.set_size)
70
+ layout.addWidget(self.box_size)
67
71
 
68
- label_alpha = QLabel("Set Alpha:")
69
- layout.addWidget(label_alpha)
70
72
  box_alpha = QDoubleSpinBox()
71
- layout.addWidget(box_alpha)
73
+ box_alpha.setPrefix("Alpha: ")
72
74
  box_alpha.setSingleStep(0.01)
73
75
  box_alpha.setValue(self.alpha)
74
- box_alpha.valueChanged.connect(self.set_alpha)
75
76
  box_alpha.setRange(0, 1)
77
+ box_alpha.valueChanged.connect(self.set_alpha)
78
+ layout.addWidget(box_alpha)
76
79
 
77
- label_color = QLabel("Set ColorMode:")
80
+ label_color = QLabel("Color Mode:")
78
81
  layout.addWidget(label_color)
79
82
  self.combo_color = QComboBox()
80
83
  self.combo_color.addItem("flat color")
@@ -84,6 +87,8 @@ class CloudItem(BaseItem):
84
87
  self.combo_color.currentIndexChanged.connect(self._on_color_mode)
85
88
  layout.addWidget(self.combo_color)
86
89
 
90
+ label_rgb = QLabel("Color:")
91
+ layout.addWidget(label_rgb)
87
92
  self.edit_rgb = QLineEdit()
88
93
  self.edit_rgb.setToolTip("Hex number, i.e. #FF4500")
89
94
  self.edit_rgb.setText(f"#{self.flat_rgb:06x}")
@@ -96,14 +101,11 @@ class CloudItem(BaseItem):
96
101
  self.slider_v = RangeSlider()
97
102
  self.slider_v.setRange(0, 255)
98
103
  self.slider_v.rangeChanged.connect(self._on_range)
99
-
100
104
  layout.addWidget(self.slider_v)
101
- self.combo_color.setCurrentIndex(self.color_mode)
102
105
 
103
- # Add a checkbox for enabling/disabling depth test
104
106
  self.checkbox_depth_test = QCheckBox(
105
107
  "Show front points first (Depth Test)")
106
- self.checkbox_depth_test.setChecked(self.depth_test_enabled)
108
+ self.checkbox_depth_test.setChecked(self.depth_test)
107
109
  self.checkbox_depth_test.stateChanged.connect(self.set_depthtest)
108
110
  layout.addWidget(self.checkbox_depth_test)
109
111
 
@@ -124,20 +126,25 @@ class CloudItem(BaseItem):
124
126
 
125
127
  def set_color_mode(self, color_mode):
126
128
  if color_mode in {'FLAT', 'RGB', 'I'}:
127
- self.combo_color.setCurrentIndex(self.mode_table[color_mode])
129
+ try:
130
+ self.combo_color.setCurrentIndex(self.mode_table[color_mode])
131
+ except RuntimeError:
132
+ pass
133
+ except ValueError:
134
+ pass
128
135
  else:
129
136
  print(f"Invalid color mode: {color_mode}")
130
137
 
131
138
  def _on_point_type_selection(self, index):
132
139
  self.point_type = list(self.point_type_table.keys())[index]
133
140
  if self.point_type == 'PIXEL':
134
- self.label_size.setText("Set size: (pixel)")
141
+ self.box_size.setPrefix("Set size (pixel): ")
135
142
  self.box_size.setDecimals(0)
136
143
  self.box_size.setSingleStep(1)
137
144
  self.box_size.setValue(1)
138
145
  self.size = 1
139
146
  else:
140
- self.label_size.setText("Set size: (meter)")
147
+ self.box_size.setPrefix("Set size (meter): ")
141
148
  self.box_size.setDecimals(2)
142
149
  self.box_size.setSingleStep(0.01)
143
150
  self.box_size.setValue(0.01)
@@ -167,7 +174,7 @@ class CloudItem(BaseItem):
167
174
  self.need_update_setting = True
168
175
 
169
176
  def set_depthtest(self, state):
170
- self.depth_test_enabled = state
177
+ self.depth_test = state
171
178
 
172
179
  def clear(self):
173
180
  data = np.empty((0), self.data_type)
@@ -236,13 +243,17 @@ class CloudItem(BaseItem):
236
243
  self.buff = new_buff
237
244
 
238
245
  # if exceed the maximum cloud size, randomly select half of the points
239
- while self.buff.shape[0] >= self.max_cloud_size:
240
- print("[Cloud Item] Exceed maximum cloud size, reduce to half")
241
- new_buff = np.empty((self.buff.shape[0] // 2), self.data_type)
242
- indices = np.random.choice(new_buff_top, new_buff_top // 2, replace=False)
243
- new_buff = self.buff[indices]
244
- new_buff_top = new_buff_top // 2
245
- self.buff = new_buff
246
+ exceed_flag = False
247
+ while new_buff.shape[0] > self.max_cloud_size:
248
+ exceed_flag = True
249
+ new_buff_half = new_buff[:new_buff_top:2]
250
+ new_buff_top = new_buff_half.shape[0]
251
+ new_buff = new_buff_half
252
+ if exceed_flag:
253
+ print("[Cloud Item] Exceed maximum cloud size %d, reduce the data size" % self.max_cloud_size)
254
+ self.buff = self.buff[:self.max_cloud_size]
255
+ self.buff[:new_buff_top] = new_buff
256
+
246
257
 
247
258
  glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
248
259
  glBufferData(GL_ARRAY_BUFFER, self.buff.nbytes,
@@ -276,7 +287,7 @@ class CloudItem(BaseItem):
276
287
  glEnable(GL_BLEND)
277
288
  glEnable(GL_PROGRAM_POINT_SIZE)
278
289
  glEnable(GL_POINT_SPRITE)
279
- if self.depth_test_enabled:
290
+ if self.depth_test:
280
291
  glEnable(GL_DEPTH_TEST)
281
292
  else:
282
293
  glDisable(GL_DEPTH_TEST)
@@ -291,9 +302,9 @@ class CloudItem(BaseItem):
291
302
  glEnableVertexAttribArray(0)
292
303
  glEnableVertexAttribArray(1)
293
304
 
294
- view_matrix = self.glwidget().get_view_matrix()
305
+ view_matrix = self.glwidget().view_matrix
295
306
  set_uniform(self.program, view_matrix, 'view_matrix')
296
- project_matrix = self.glwidget().get_projection_matrix()
307
+ project_matrix = self.glwidget().projection_matrix
297
308
  set_uniform(self.program, project_matrix, 'projection_matrix')
298
309
  width = self.glwidget().current_width()
299
310
  focal = project_matrix[0, 0] * width / 2
@@ -309,5 +320,5 @@ class CloudItem(BaseItem):
309
320
  glDisable(GL_POINT_SPRITE)
310
321
  glDisable(GL_PROGRAM_POINT_SIZE)
311
322
  glDisable(GL_BLEND)
312
- if self.depth_test_enabled:
323
+ if self.depth_test:
313
324
  glDisable(GL_DEPTH_TEST) # Disable depth testing if it was enabled
@@ -42,7 +42,7 @@ void main()
42
42
 
43
43
 
44
44
  class FrameItem(BaseItem):
45
- def __init__(self, T=np.eye(4), size=(1, 0.8), width=3, img=None):
45
+ def __init__(self, T=np.eye(4), size=(1, 0.8), width=3, img=None, color='#0000FF'):
46
46
  BaseItem.__init__(self)
47
47
  self.w, self.h = size
48
48
  self.width = width
@@ -50,6 +50,7 @@ class FrameItem(BaseItem):
50
50
  self.img = img
51
51
  self.texture = None
52
52
  self.need_updating = False
53
+ self.set_color(color)
53
54
 
54
55
  def initialize_gl(self):
55
56
  # Rectangle vertices and texture coordinates
@@ -57,11 +58,11 @@ class FrameItem(BaseItem):
57
58
  hh = self.h / 2
58
59
  self.vertices = np.array([
59
60
  # positions # texture coords
60
- [-hw, -hh, 0.0, 0.0, 0.0], # bottom-left
61
- [hw, -hh, 0.0, 1.0, 0.0], # bottom-right
62
- [hw, hh, 0.0, 1.0, 1.0], # top-right
63
- [-hw, hh, 0.0, 0.0, 1.0], # top-left
64
- [0, 0, -hh * 0.66, 0.0, 0.0], # top-left
61
+ [-hw, hh, 0.0, 0.0, 0.0], # bottom-left
62
+ [ hw, hh, 0.0, 1.0, 0.0], # bottom-right
63
+ [ hw, -hh, 0.0, 1.0, 1.0], # top-right
64
+ [-hw, -hh, 0.0, 0.0, 1.0], # top-left
65
+ [ 0.0, 0.0, hh * 0.66, 0.0, 0.0], # center -Z is the front.
65
66
  ], dtype=np.float32)
66
67
 
67
68
  self.indices = np.array([
@@ -123,23 +124,28 @@ class FrameItem(BaseItem):
123
124
 
124
125
  glBindVertexArray(0)
125
126
 
126
- def set_transform(self, T):
127
- self.T = T
128
-
129
- def set_data(self, img=None, transform=None):
127
+ def set_transform(self, Twc, is_opencv_coord=False):
128
+ if is_opencv_coord:
129
+ # convert the opencv camera coordinate to the opengl camera coordinate
130
+ M_conv = np.array([
131
+ [1, 0, 0, 0],
132
+ [0, -1, 0, 0],
133
+ [0, 0, -1, 0],
134
+ [0, 0, 0, 1]
135
+ ])
136
+ Twc = Twc @ M_conv
137
+ self.Twc = Twc
138
+
139
+ def set_data(self, img=None, transform=None, is_opencv_coord=False):
130
140
  if transform is not None:
131
- self.T = transform
141
+ self.set_transform(transform, is_opencv_coord)
132
142
  self.img = img
133
143
  self.need_updating = True
134
144
 
135
145
  def update_img_buffer(self):
136
- if self.img is not None and self.need_updating:
146
+ if self.need_updating:
137
147
  self.texture = glGenTextures(1)
138
148
  glBindTexture(GL_TEXTURE_2D, self.texture)
139
- # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
140
- # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
141
- # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
142
- # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
143
149
  if self.img.ndim == 2:
144
150
  # Convert grayscale to RGBA
145
151
  self.img = np.stack((self.img,) * 3 + (np.ones_like(self.img) * 255,), axis=-1)
@@ -147,44 +153,57 @@ class FrameItem(BaseItem):
147
153
  # Add an alpha channel
148
154
  alpha_channel = np.ones((self.img.shape[0], self.img.shape[1], 1), dtype=np.uint8) * 255
149
155
  self.img = np.concatenate((self.img, alpha_channel), axis=2)
150
-
151
156
  img = np.ascontiguousarray(self.img, dtype=np.uint8)
152
-
153
157
  # Load image
154
158
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.shape[1],
155
159
  img.shape[0], 0, GL_RGBA, GL_UNSIGNED_BYTE, img)
156
160
  glGenerateMipmap(GL_TEXTURE_2D)
157
161
  glBindTexture(GL_TEXTURE_2D, 0)
158
162
  self.need_updating = False
163
+
164
+ def set_color(self, color):
165
+ if isinstance(color, str):
166
+ self.rgba = hex_to_rgba(color)
167
+ elif isinstance(color, list):
168
+ self.rgba = color
169
+ elif isinstance(color, tuple):
170
+ self.rgba = list(color)
171
+ else:
172
+ raise ValueError("Invalid color format")
173
+
174
+ def set_line_width(self, width):
175
+ self.width = width
159
176
 
160
177
  def paint(self):
161
- self.view_matrix = self.glwidget().get_view_matrix()
162
- project_matrix = self.glwidget().get_projection_matrix()
163
- self.update_img_buffer()
178
+ self.view_matrix = self.glwidget().view_matrix
179
+ project_matrix = self.glwidget().projection_matrix
164
180
 
165
181
  glEnable(GL_DEPTH_TEST)
166
182
  glEnable(GL_BLEND)
167
183
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
168
184
 
169
- glUseProgram(self.program)
170
- set_uniform(self.program, self.view_matrix, 'view_matrix')
171
- set_uniform(self.program, project_matrix, 'project_matrix')
172
- set_uniform(self.program, self.T, 'model_matrix')
173
- glBindVertexArray(self.vao)
174
-
175
- if self.texture is not None:
176
- glBindTexture(GL_TEXTURE_2D, self.texture)
177
- glMultMatrixf(self.T.T) # Apply the transformation matrix
178
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
179
- glBindTexture(GL_TEXTURE_2D, 0)
185
+ if self.img is not None:
186
+ self.update_img_buffer()
187
+ glUseProgram(self.program)
188
+ set_uniform(self.program, self.view_matrix, 'view_matrix')
189
+ set_uniform(self.program, project_matrix, 'project_matrix')
190
+ set_uniform(self.program, self.T, 'model_matrix')
191
+ glBindVertexArray(self.vao)
192
+ glBindVertexArray(0)
193
+ glUseProgram(0)
194
+
195
+ # if self.texture is not None:
196
+ # glBindTexture(GL_TEXTURE_2D, self.texture)
197
+ # glMultMatrixf(self.T.T) # Apply the transformation matrix
198
+ # glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
199
+ # glBindTexture(GL_TEXTURE_2D, 0)
180
200
 
181
- glBindVertexArray(0)
182
- glUseProgram(0)
183
-
184
201
  glLineWidth(self.width)
185
- glColor4f(1, 1, 1, 1) # Set the color before drawing the lines
202
+ # set color to bule
203
+ glColor4f(*self.rgba)
186
204
  glBindBuffer(GL_ARRAY_BUFFER, self.line_vbo)
187
205
  glEnableClientState(GL_VERTEX_ARRAY)
206
+ glMultMatrixf(self.T.T)
188
207
  glVertexPointer(3, GL_FLOAT, 0, None)
189
208
  glDrawArrays(GL_LINES, 0, len(self.line_vertices))
190
209
  glDisableClientState(GL_VERTEX_ARRAY)
@@ -192,3 +211,4 @@ class FrameItem(BaseItem):
192
211
 
193
212
  glDisable(GL_DEPTH_TEST)
194
213
  glDisable(GL_BLEND)
214
+