Dado un segmento de línea de AAA a BBB, y una escala de su vista del espacio sx, sysx, sysx, sy, ¿cómo conserva el ancho de la línea?

Así que estoy usando PyQt5 para hacer renderizado 2D para una aplicación creadora de espectáculos de luces LED. Los espectáculos de luces son gráficos donde el Y -eje es el nivel de brillo del LED y el X El eje suele ser el tiempo.

Tengo una función de zoom en mi clase de "vista" que acerca o aleja toda la escena según la entrada del usuario. Cuando haces esto, por defecto, todo en la escena se encoge o explota como si estuvieras acercándote o alejándote de algunas estructuras en tierra y estás a cierta distancia vertical en el aire justo encima de las estructuras.

La función de zoom es necesaria en caso de que haya espectáculos de luces extra largos en el tiempo y el usuario quiera ver el gráfico completo de una vez, por ejemplo.

ya he conseguido el X y Y -axes para escalar correctamente, y esto fue más fácil que mi problema actual, ya que, por supuesto, el X , Y -los ejes están en paralelo con el sistema de coordenadas 2D utilizado.

No era una simple cuestión de llamar axesItem.scale(1/sx, 1/sy)("ingenuo") a los ejes donde s X , s y es la escala del puerto de visualización en las direcciones respectivas. Tuve que abordar individualmente cada objeto que componía el algoritmo de dibujo de los ejes y realizar una escala personalizada a cada uno, para que el resultado final fuera el correcto.

Probé nuevamente la escala ingenua en el elemento de polilínea (lo que forma el gráfico), y eso tampoco funciona. Así que necesito escalar la variable polyline.lineWidth que se usa en el algoritmo de pintura, en su lugar. Sin embargo, los segmentos de polilínea pueden estar en varios ángulos diferentes de 0 o 90 grados Entonces, dada una escala de vista de s X , s y , ¿cuál es la fórmula para escalar el ancho de un segmento de línea, de modo que solo el ancho se conserve perfectamente a los ojos del usuario?

Cómo debería verse con zoom 1:1 (predeterminado):Cómo debería verse con zoom 1:1 (predeterminado)

Cómo se ve con una escala ingenua, alejada 9 veces:Cómo se ve con una escala ingenua, alejada 9 veces.

Respuestas (1)

Deje que un segmento de la polilínea corra desde PAG a q . Entonces nuestro vector ancho w ¯ tiene componente vertical w pecado θ y componente horizontal w porque θ . Querremos aplicar la escala por componentes a w ¯ de modo que s w ¯ = ( w s X porque θ ,   w s y pecado θ ) . El largo de w ¯ es igual al ancho de línea que debemos dibujar PAG q con = w = ( w s X porque θ ) 2 + ( w s y pecado θ ) 2 .

Por lo tanto, podemos almacenar una matriz de anchos de segmento de línea y actualizar los anchos con esta fórmula o recalcular esta fórmula cada vez que dibujamos cada segmento de línea. Codificaré el segundo, ya que Qt solo llama a repintar cuando tiene que mover un segmento de línea, en cuyo caso tendríamos que volver a calcular de todos modos.

Imagen de la situación:

ingrese la descripción de la imagen aquí

θ = atan2 ( PAG q y / PAG q X )

PAG q q PAG


EDITAR:

Tengo los componentes de la escala invertidos en la fórmula, s X debe aplicarse a la Y -componente de w ¯ , y viceversa.


El código relevante es ahora:

def paint(self, painter, item, widget):
    GraphicsItem.paint(self, painter, item, widget)
    painter.setPen(QPen(QBrush(self.lineColor), self.lineWidth))
    sx = self.scalex
    sy = self.scaley
    for k in range(0, len(self.offspring)-1):
        P = self.offspring[k].centerPos
        Q = self.offspring[k+1].centerPos
        PQ = Q - P
        t = atan2(PQ.y() / PQ.x())
        w = self.lineWidth
        w = sqrt((w*cos(t)/sy)**2 + (w*sin(t)/sx)**2)
        pen = painter.pen()
        pen.setWidth(w)
        painter.setPen(pen)
        painter.drawLine(P, Q)

def viewScaleXY(self, sx, sy):
    self.scalex *= sx
    self.scaley *= sy