Как связать овальную или цилиндрическую форму между двумя обновляемыми 3D-точками, используя pyqtgraph и OpenGL? - PullRequest
0 голосов
/ 09 апреля 2020

Я хотел бы сформировать фигуру между двумя точками обновления в трехмерном пространстве, используя pyqtgraph и OpenGL. Пока я только нашел возможным соединить GLLinePlotItem и GLMeshItem с вершинами и плоскими гранями между двумя точками. Тем не менее, я хотел бы иметь овальную или цилиндрическую форму, связанную между точками, но я не могу найти способ использовать интегрированную сферу и цилиндр MeshData , не вдаваясь в сложные математические приемы, матрицы вращения и тригонометрию. .

Есть ли более простой способ, похожий на GLLinePlotItem или GLMeshItem?

Иллюстрация того, что у меня есть сейчас, и что я хотел бы иметь вместо:

enter image description here

Пример кода:

from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import pyqtgraph.opengl as gl
import numpy as np
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QMainWindow, QApplication
from random import randint

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        w = gl.GLViewWidget()
        w.show()
        w.setCameraPosition(distance=15, azimuth=-90)

        self.timer = QTimer()
        self.timer.start(1000)
        self.timer.timeout.connect(self.start)

        g = gl.GLGridItem()
        g.scale(2, 2, 1)
        w.addItem(g)


        self.md = gl.MeshData.sphere(rows=10, cols=20)
        self.m1 = gl.GLMeshItem(meshdata=self.md,
                                smooth=True,
                                color=(1, 0, 0, 0.2),
                                shader="balloon",
                                glOptions="additive")
        w.addItem(self.m1)

        self.lineMesh = gl.GLLinePlotItem(width=1, antialias=False)
        w.addItem(self.lineMesh)


    def start(self):
        # coordinates

        point1 = np.array([randint(0,25), randint(0,25), 0])
        point2 = np.array([randint(0,25), randint(0,25), 20])

        line = np.array([point1, point2])
        self.lineMesh.setData(pos=line)

        length = (((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2 + (
                point2[2] - point1[2]) ** 2) ** 0.5)*0.5

        center = (point1 + point2) / 2
        #radius = np.linalg.norm(point2 - point1) / 2


        self.md = gl.MeshData.sphere(rows=10, cols=20, radius=[1])
        self.m1.setMeshData(meshdata=self.md)

        self.m1.resetTransform()
        self.m1.scale(1, 1, length)
        self.m1.translate(*center)



if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MainWindow()
    ex.show()
    sys.exit(app.exec_())

1 Ответ

1 голос
/ 16 апреля 2020

Я не думаю, что вы можете избежать небольшой математики, но триггер не так уж и плох:

        v = point2 - point1
        theta = np.arctan2(v[1], v[0])
        phi = np.arctan2(np.linalg.norm(v[:2]), v[2])

        tr = pg.Transform3D()
        tr.translate(*point1)
        tr.rotate(theta * 180 / np.pi, 0, 0, 1)
        tr.rotate(phi * 180 / np.pi, 0, 1, 0)
        tr.scale(1, 1, np.linalg.norm(v) / 2)
        tr.translate(0, 0, 1)

        self.m1.setTransform(tr)

И если вы предпочитаете линейную алгебру, а не тригонометрию, это не так уж и сложно. тоже плохо, хотя и более многословно:

        # pick 4 points on the untransformed sphere
        a = np.array([
            [0., 0., -1.],
            [0., 0., 1.],
            [1., 0., -1.],
            [0., 1., -1.],
        ])

        # and 4 corresponding points on the transformed sphere
        v1 = np.cross(point1-point2, [0., 0., 1.])
        v2 = np.cross(point1-point2, v1)
        b = np.array([
            point1,
            point2,
            point1 + v1 / np.linalg.norm(v1),
            point1 + v2 / np.linalg.norm(v2),
        ])

        # solve the transform mapping from a to b
        tr = pg.solve3DTransform(a, b)

        # make this transform work in opengl's homogeneous coordinate system
        tr[3,3] = 1

        self.m1.setTransform(tr)
...