Implementación de Filtros por Categoría
La visualización selectiva por categorías se logra mediante cuadros de selección. Cada checkobx modifica una máscara booleana que contorla la visibilidad de los puntos:
check_box.stateChanged.connect(
functools.partial(self.actualizar_visibilidad_puntos)
)
Lógica de procesamiento de eventos:
def actualizar_visibilidad_puntos(self):
if self.widgetOpenGL.modo_visualizacion == MODO.CATEGORIA:
filtro = np.ones_like(self.widgetOpenGL.categorias, dtype=bool)
for idx in range(self.listaEtiquetas.count()):
elemento = self.listaEtiquetas.item(idx)
widget = self.listaEtiquetas.itemWidget(elemento)
selector = widget.findChild(QCheckBox, 'selector_categoria')
if not selector.isChecked():
filtro[self.widgetOpenGL.categorias == idx] = False
self.widgetOpenGL.estado_categorias[idx] = False
else:
self.widgetOpenGL.estado_categorias[idx] = True
self.widgetOpenGL.filtro_activo = filtro
self.widgetOpenGL.vertices_actuales = self.widgetOpenGL.nube.xyz[filtro]
self.widgetOpenGL.colores_actuales = self.widgetOpenGL.colores_categoria[filtro]
self.widgetOpenGL.inicializar_buffers()
self.widgetOpenGL.update()
Optimización de Renderizado con OpenGL
Para mejorar el rendimiento, se utilizan Vertex Buffer Objects (VBO) y Vertex Array Objects (VAO):
def inicializar_buffers(self):
self.vao_puntos = glGenVertexArrays(1)
identificadores_vbo = glGenBuffers(2)
# Buffer de vértices
glBindBuffer(GL_ARRAY_BUFFER, identificadores_vbo[0])
glBufferData(GL_ARRAY_BUFFER, self.vertices_actuales.nbytes,
self.vertices_actuales, GL_STATIC_DRAW)
# Buffer de colores
glBindBuffer(GL_ARRAY_BUFFER, identificadores_vbo[1])
glBufferData(GL_ARRAY_BUFFER, self.colores_actuales.nbytes,
self.colores_actuales, GL_STATIC_DRAW)
# Configuración de atributos
glBindVertexArray(self.vao_puntos)
glBindBuffer(GL_ARRAY_BUFFER, identificadores_vbo[0])
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, identificadores_vbo[1])
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(1)
glBindVertexArray(0)
Selección de Puntos en 3D
Al hacer doble clic, se calcula el punto más cercano al rayo de seleción:
def evento_doble_clic(self, evento):
x, y = evento.pos().x(), self.height() - evento.pos().y()
punto = self.seleccionar_punto(x, y)
if punto.size:
self.transformacion.setTranslation(-punto[0], -punto[1], -punto[2])
self.update()
def seleccionar_punto(self, x, y):
origen = QVector3D(x, y, 0).unproject(
self.camara.matriz() * self.transformacion.matriz(),
self.proyeccion,
QRect(0, 0, self.width(), self.height())
)
destino = QVector3D(x, y, 1).unproject(
self.camara.matriz() * self.transformacion.matriz(),
self.proyeccion,
QRect(0, 0, self.width(), self.height())
)
direccion = destino - origen
direccion.normalize()
# Cálculo de distancias
t = np.dot(self.vertices_actuales - origen, direccion) / direccion.lengthSquared()
proyecciones = origen + t * direccion
distancias = np.linalg.norm(self.vertices_actuales - proyecciones, axis=1)
radio = self.radio_seleccion * self.escala_actual
candidatos = distancias < radio
if not np.any(candidatos):
return np.array([])
puntos_cercanos = self.vertices_actuales[candidatos]
idx = np.argmin(np.dot(puntos_cercanos, direccion))
return puntos_cercanos[idx]