pyqtgraph/pyqtgraph/Vector.py

103 lines
3.8 KiB
Python

# -*- coding: utf-8 -*-
"""
Vector.py - Extension of QVector3D which adds a few missing methods.
Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more information.
"""
from math import acos, degrees
from .Qt import QtGui, QtCore, QT_LIB
from . import functions as fn
class Vector(QtGui.QVector3D):
"""Extension of QVector3D which adds a few helpful methods."""
def __init__(self, *args):
"""
Handle additional constructions of a Vector
============== ================================================================================================
**Arguments:**
*args* Could be any of:
* 3 numerics (x, y, and z)
* 2 numerics (x, y, and `0` assumed for z)
* Either of the previous in a list-like collection
* 1 QSizeF (`0` assumed for z)
* 1 QPointF (`0` assumed for z)
* Any other valid QVector3D init args.
============== ================================================================================================
"""
initArgs = args
if len(args) == 1:
if isinstance(args[0], QtCore.QSizeF):
initArgs = (float(args[0].width()), float(args[0].height()), 0)
elif isinstance(args[0], QtCore.QPoint) or isinstance(args[0], QtCore.QPointF):
initArgs = (float(args[0].x()), float(args[0].y()), 0)
elif hasattr(args[0], '__getitem__') and not isinstance(args[0], QtGui.QVector3D):
vals = list(args[0])
if len(vals) == 2:
vals.append(0)
if len(vals) != 3:
raise Exception('Cannot init Vector with sequence of length %d' % len(args[0]))
initArgs = vals
elif isinstance(args[0], QtGui.QVector3D):
# PySide6 6.1 does not accept initialization from QVector3D
initArgs = args[0].x(), args[0].y(), args[0].z()
elif len(args) == 2:
initArgs = (args[0], args[1], 0)
QtGui.QVector3D.__init__(self, *initArgs)
def __len__(self):
return 3
def __add__(self, b):
# workaround for pyside bug. see https://bugs.launchpad.net/pyqtgraph/+bug/1223173
if QT_LIB == 'PySide' and isinstance(b, QtGui.QVector3D):
b = Vector(b)
return QtGui.QVector3D.__add__(self, b)
#def __reduce__(self):
#return (Point, (self.x(), self.y()))
def __getitem__(self, i):
if i == 0:
return self.x()
elif i == 1:
return self.y()
elif i == 2:
return self.z()
else:
raise IndexError("Point has no index %s" % str(i))
def __setitem__(self, i, x):
if i == 0:
return self.setX(x)
elif i == 1:
return self.setY(x)
elif i == 2:
return self.setZ(x)
else:
raise IndexError("Point has no index %s" % str(i))
def __iter__(self):
yield(self.x())
yield(self.y())
yield(self.z())
def angle(self, a):
"""Returns the angle in degrees between this vector and the vector a."""
n1 = self.length()
n2 = a.length()
if n1 == 0. or n2 == 0.:
return None
## Probably this should be done with arctan2 instead..
rads = acos(fn.clip_scalar(QtGui.QVector3D.dotProduct(self, a) / (n1 * n2), -1.0, 1.0)) ### in radians
# c = self.crossProduct(a)
# if c > 0:
# ang *= -1.
return degrees(rads)
def __abs__(self):
return Vector(abs(self.x()), abs(self.y()), abs(self.z()))