Merge pull request #1724 from j9ac9k/use-math-module-for-single-values
Use math module methods for scalars
This commit is contained in:
commit
6a708dc203
@ -7,7 +7,7 @@ This example uses the isosurface function to convert a scalar field
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.opengl as gl
|
||||
|
||||
@ -22,24 +22,17 @@ g = gl.GLGridItem()
|
||||
g.scale(2,2,1)
|
||||
w.addItem(g)
|
||||
|
||||
import numpy as np
|
||||
|
||||
## Define a scalar field from which we will generate an isosurface
|
||||
def psi(i, j, k, offset=(25, 25, 50)):
|
||||
x = i-offset[0]
|
||||
y = j-offset[1]
|
||||
z = k-offset[2]
|
||||
th = np.arctan2(z, (x**2+y**2)**0.5)
|
||||
phi = np.arctan2(y, x)
|
||||
r = (x**2 + y**2 + z **2)**0.5
|
||||
th = np.arctan2(z, np.hypot(x, y))
|
||||
r = np.sqrt(x**2 + y**2 + z **2)
|
||||
a0 = 1
|
||||
#ps = (1./81.) * (2./np.pi)**0.5 * (1./a0)**(3/2) * (6 - r/a0) * (r/a0) * np.exp(-r/(3*a0)) * np.cos(th)
|
||||
ps = (1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1)
|
||||
|
||||
return ps
|
||||
|
||||
#return ((1./81.) * (1./np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * (r/a0) * np.exp(-r/(3*a0)) * np.sin(th) * np.cos(th) * np.exp(2 * 1j * phi))**2
|
||||
|
||||
|
||||
print("Generating scalar field..")
|
||||
data = np.abs(np.fromfunction(psi, (50,50,100)))
|
||||
|
@ -29,17 +29,14 @@ gz = gl.GLGridItem()
|
||||
gz.translate(0, 0, -10)
|
||||
w.addItem(gz)
|
||||
|
||||
def fn(x, y):
|
||||
return np.cos((x**2 + y**2)**0.5)
|
||||
|
||||
n = 51
|
||||
y = np.linspace(-10,10,n)
|
||||
x = np.linspace(-10,10,100)
|
||||
for i in range(n):
|
||||
yi = np.array([y[i]]*100)
|
||||
d = (x**2 + yi**2)**0.5
|
||||
yi = y[i]
|
||||
d = np.hypot(x, yi)
|
||||
z = 10 * np.cos(d) / (d+1)
|
||||
pts = np.vstack([x,yi,z]).transpose()
|
||||
pts = np.column_stack([x, np.full_like(x, yi), z])
|
||||
plt = gl.GLLinePlotItem(pos=pts, color=pg.glColor((i,n*1.3)), width=(i+1)/10., antialias=True)
|
||||
w.addItem(plt)
|
||||
|
||||
|
@ -8,6 +8,7 @@ Demonstrates use of GLScatterPlotItem with rapidly-updating plots.
|
||||
import initExample
|
||||
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph import functions as fn
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import pyqtgraph.opengl as gl
|
||||
import numpy as np
|
||||
@ -84,10 +85,10 @@ def update():
|
||||
global phase, sp2, d2
|
||||
s = -np.cos(d2*2+phase)
|
||||
color = np.empty((len(d2),4), dtype=np.float32)
|
||||
color[:,3] = np.clip(s * 0.1, 0, 1)
|
||||
color[:,0] = np.clip(s * 3.0, 0, 1)
|
||||
color[:,1] = np.clip(s * 1.0, 0, 1)
|
||||
color[:,2] = np.clip(s ** 3, 0, 1)
|
||||
color[:,3] = fn.clip_array(s * 0.1, 0., 1.)
|
||||
color[:,0] = fn.clip_array(s * 3.0, 0., 1.)
|
||||
color[:,1] = fn.clip_array(s * 1.0, 0., 1.)
|
||||
color[:,2] = fn.clip_array(s ** 3, 0., 1.)
|
||||
sp2.setData(color=color)
|
||||
phase -= 0.1
|
||||
|
||||
|
@ -7,9 +7,10 @@ Demonstrates GLVolumeItem for displaying volumetric data.
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import pyqtgraph.opengl as gl
|
||||
from pyqtgraph import functions as fn
|
||||
|
||||
app = pg.mkQApp("GLVolumeItem Example")
|
||||
w = gl.GLViewWidget()
|
||||
@ -23,27 +24,21 @@ g = gl.GLGridItem()
|
||||
g.scale(10, 10, 1)
|
||||
w.addItem(g)
|
||||
|
||||
import numpy as np
|
||||
## Hydrogen electron probability density
|
||||
def psi(i, j, k, offset=(50,50,100)):
|
||||
x = i-offset[0]
|
||||
y = j-offset[1]
|
||||
z = k-offset[2]
|
||||
th = np.arctan2(z, (x**2+y**2)**0.5)
|
||||
phi = np.arctan2(y, x)
|
||||
r = (x**2 + y**2 + z **2)**0.5
|
||||
th = np.arctan2(z, np.hypot(x, y))
|
||||
r = np.sqrt(x**2 + y**2 + z **2)
|
||||
a0 = 2
|
||||
#ps = (1./81.) * (2./np.pi)**0.5 * (1./a0)**(3/2) * (6 - r/a0) * (r/a0) * np.exp(-r/(3*a0)) * np.cos(th)
|
||||
ps = (1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1)
|
||||
|
||||
return ps
|
||||
|
||||
#return ((1./81.) * (1./np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * (r/a0) * np.exp(-r/(3*a0)) * np.sin(th) * np.cos(th) * np.exp(2 * 1j * phi))**2
|
||||
|
||||
|
||||
data = np.fromfunction(psi, (100,100,200))
|
||||
positive = np.log(np.clip(data, 0, data.max())**2)
|
||||
negative = np.log(np.clip(-data, 0, -data.min())**2)
|
||||
positive = np.log(fn.clip_array(data, np.finfo(data.dtype).eps, data.max())**2)
|
||||
negative = np.log(fn.clip_array(-data, -np.finfo(data.dtype).eps, -data.min())**2)
|
||||
|
||||
d2 = np.empty(data.shape + (4,), dtype=np.ubyte)
|
||||
d2[..., 0] = positive * (255./positive.max())
|
||||
|
@ -9,6 +9,7 @@ used to affect the appearance of a surface.
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
import numpy as np
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.opengl as gl
|
||||
@ -23,9 +24,6 @@ g = gl.GLGridItem()
|
||||
g.scale(2,2,1)
|
||||
w.addItem(g)
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
md = gl.MeshData.sphere(rows=10, cols=20)
|
||||
x = np.linspace(-8, 8, 6)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from math import atan2, asin, sin, cos, degrees, sqrt, hypot
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtGui, QtCore
|
||||
import numpy as np
|
||||
@ -49,7 +50,7 @@ class GlassDB:
|
||||
B = list(map(float, [info['B1'], info['B2'], info['B3']]))
|
||||
C = list(map(float, [info['C1'], info['C2'], info['C3']]))
|
||||
w2 = (wl/1000.)**2
|
||||
n = np.sqrt(1.0 + (B[0]*w2 / (w2-C[0])) + (B[1]*w2 / (w2-C[1])) + (B[2]*w2 / (w2-C[2])))
|
||||
n = sqrt(1.0 + (B[0]*w2 / (w2-C[0])) + (B[1]*w2 / (w2-C[1])) + (B[2]*w2 / (w2-C[2])))
|
||||
cache[wl] = n
|
||||
return cache[wl]
|
||||
|
||||
@ -249,10 +250,14 @@ class Lens(Optic):
|
||||
p1 = surface.mapToItem(ray, p1)
|
||||
|
||||
rd = ray['dir']
|
||||
a1 = np.arctan2(rd[1], rd[0])
|
||||
ar = a1 - ai + np.arcsin((np.sin(ai) * ray['ior'] / ior))
|
||||
|
||||
a1 = atan2(rd[1], rd[0])
|
||||
try:
|
||||
ar = a1 - ai + asin((sin(ai) * ray['ior'] / ior))
|
||||
except ValueError:
|
||||
ar = np.nan
|
||||
ray.setEnd(p1)
|
||||
dp = Point(np.cos(ar), np.sin(ar))
|
||||
dp = Point(cos(ar), sin(ar))
|
||||
ray = Ray(parent=ray, ior=ior, dir=dp)
|
||||
return [ray]
|
||||
|
||||
@ -279,10 +284,10 @@ class Mirror(Optic):
|
||||
if p1 is not None:
|
||||
p1 = surface.mapToItem(ray, p1)
|
||||
rd = ray['dir']
|
||||
a1 = np.arctan2(rd[1], rd[0])
|
||||
a1 = atan2(rd[1], rd[0])
|
||||
ar = a1 + np.pi - 2 * ai
|
||||
ray.setEnd(p1)
|
||||
dp = Point(np.cos(ar), np.sin(ar))
|
||||
dp = Point(cos(ar), sin(ar))
|
||||
ray = Ray(parent=ray, dir=dp)
|
||||
else:
|
||||
ray.setEnd(None)
|
||||
@ -374,7 +379,7 @@ class CircleSurface(pg.GraphicsObject):
|
||||
## half-height of surface can't be larger than radius
|
||||
h2 = min(h2, abs(r))
|
||||
arc = QtCore.QRectF(0, -r, r*2, r*2)
|
||||
a1 = np.arcsin(h2/r) * 180. / np.pi
|
||||
a1 = degrees(asin(h2/r))
|
||||
a2 = -2*a1
|
||||
a1 += 180.
|
||||
self.path.arcMoveTo(arc, a1)
|
||||
@ -406,13 +411,13 @@ class CircleSurface(pg.GraphicsObject):
|
||||
if abs(y) > h:
|
||||
return None, None
|
||||
else:
|
||||
return (Point(0, y), np.arctan2(dir[1], dir[0]))
|
||||
return (Point(0, y), atan2(dir[1], dir[0]))
|
||||
else:
|
||||
#print " curve"
|
||||
## find intersection of circle and line (quadratic formula)
|
||||
dx = dir[0]
|
||||
dy = dir[1]
|
||||
dr = (dx**2 + dy**2) ** 0.5
|
||||
dr = hypot(dx, dy) # length
|
||||
D = p[0] * (p[1]+dy) - (p[0]+dx) * p[1]
|
||||
idr2 = 1.0 / dr**2
|
||||
disc = r**2 * dr**2 - D**2
|
||||
@ -424,7 +429,6 @@ class CircleSurface(pg.GraphicsObject):
|
||||
else:
|
||||
sgn = 1
|
||||
|
||||
|
||||
br = self.path.boundingRect()
|
||||
x1 = (D*dy + sgn*dx*disc2) * idr2
|
||||
y1 = (-D*dx + abs(dy)*disc2) * idr2
|
||||
@ -436,19 +440,12 @@ class CircleSurface(pg.GraphicsObject):
|
||||
pt = Point(x2, y2)
|
||||
if not br.contains(x2+r, y2):
|
||||
return None, None
|
||||
raise Exception("No intersection!")
|
||||
|
||||
norm = np.arctan2(pt[1], pt[0])
|
||||
norm = atan2(pt[1], pt[0])
|
||||
if r < 0:
|
||||
norm += np.pi
|
||||
#print " norm:", norm*180/3.1415
|
||||
dp = p - pt
|
||||
#print " dp:", dp
|
||||
ang = np.arctan2(dp[1], dp[0])
|
||||
#print " ang:", ang*180/3.1415
|
||||
#print " ai:", (ang-norm)*180/3.1415
|
||||
|
||||
#print " intersection:", pt
|
||||
ang = atan2(dp[1], dp[0])
|
||||
return pt + Point(r, 0), ang-norm
|
||||
|
||||
|
||||
|
@ -6,14 +6,8 @@ Distributed under MIT/X11 license. See license.txt for more information.
|
||||
"""
|
||||
|
||||
from .Qt import QtCore
|
||||
import numpy as np
|
||||
from math import atan2, hypot, degrees
|
||||
|
||||
def clip(x, mn, mx):
|
||||
if x > mx:
|
||||
return mx
|
||||
if x < mn:
|
||||
return mn
|
||||
return x
|
||||
|
||||
class Point(QtCore.QPointF):
|
||||
"""Extension of QPointF which adds a few missing methods."""
|
||||
@ -93,25 +87,12 @@ class Point(QtCore.QPointF):
|
||||
return self._math_('__pow__', a)
|
||||
|
||||
def _math_(self, op, x):
|
||||
#print "point math:", op
|
||||
#try:
|
||||
#fn = getattr(QtCore.QPointF, op)
|
||||
#pt = fn(self, x)
|
||||
#print fn, pt, self, x
|
||||
#return Point(pt)
|
||||
#except AttributeError:
|
||||
x = Point(x)
|
||||
return Point(getattr(self[0], op)(x[0]), getattr(self[1], op)(x[1]))
|
||||
|
||||
def length(self):
|
||||
"""Returns the vector length of this Point."""
|
||||
try:
|
||||
return (self[0]**2 + self[1]**2) ** 0.5
|
||||
except OverflowError:
|
||||
try:
|
||||
return self[1] / np.sin(np.arctan2(self[1], self[0]))
|
||||
except OverflowError:
|
||||
return np.inf
|
||||
return hypot(self[0], self[1]) # length
|
||||
|
||||
def norm(self):
|
||||
"""Returns a vector in the same direction with unit length."""
|
||||
@ -119,16 +100,8 @@ class Point(QtCore.QPointF):
|
||||
|
||||
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..
|
||||
ang = np.arccos(clip(self.dot(a) / (n1 * n2), -1.0, 1.0)) ### in radians
|
||||
c = self.cross(a)
|
||||
if c > 0:
|
||||
ang *= -1.
|
||||
return ang * 180. / np.pi
|
||||
rads = atan2(self.y(), self.x()) - atan2(a.y(), a.x())
|
||||
return degrees(rads)
|
||||
|
||||
def dot(self, a):
|
||||
"""Returns the dot product of a and this Point."""
|
||||
@ -147,7 +120,6 @@ class Point(QtCore.QPointF):
|
||||
def __repr__(self):
|
||||
return "Point(%f, %f)" % (self[0], self[1])
|
||||
|
||||
|
||||
def min(self):
|
||||
return min(self[0], self[1])
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from math import atan2, degrees
|
||||
from .Qt import QtCore, QtGui
|
||||
from .Point import Point
|
||||
import numpy as np
|
||||
@ -76,7 +77,7 @@ class SRTTransform(QtGui.QTransform):
|
||||
self._state = {
|
||||
'pos': Point(p1),
|
||||
'scale': Point(dp2.length(), dp3.length() * sy),
|
||||
'angle': (np.arctan2(dp2[1], dp2[0]) * 180. / np.pi) + da
|
||||
'angle': degrees(atan2(dp2[1], dp2[0])) + da
|
||||
}
|
||||
self.update()
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from math import atan2, degrees
|
||||
from .Qt import QtCore, QtGui
|
||||
from .Vector import Vector
|
||||
from .Transform3D import Transform3D
|
||||
@ -164,7 +165,7 @@ class SRTTransform3D(Transform3D):
|
||||
sin = (r-r.T)[rInd] / (2. * sign * axis[axisInd])
|
||||
|
||||
## finally, we get the complete angle from arctan(sin/cos)
|
||||
self._state['angle'] = np.arctan2(sin, cos) * 180 / np.pi
|
||||
self._state['angle'] = degrees(atan2(sin, cos))
|
||||
if self._state['angle'] == 0:
|
||||
self._state['axis'] = (0,0,1)
|
||||
|
||||
|
@ -4,9 +4,9 @@ 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
|
||||
import numpy as np
|
||||
from . import functions as fn
|
||||
|
||||
class Vector(QtGui.QVector3D):
|
||||
"""Extension of QVector3D which adds a few helpful methods."""
|
||||
@ -88,11 +88,11 @@ class Vector(QtGui.QVector3D):
|
||||
if n1 == 0. or n2 == 0.:
|
||||
return None
|
||||
## Probably this should be done with arctan2 instead..
|
||||
ang = np.arccos(np.clip(QtGui.QVector3D.dotProduct(self, a) / (n1 * n2), -1.0, 1.0)) ### in radians
|
||||
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 ang * 180. / np.pi
|
||||
return degrees(rads)
|
||||
|
||||
def __abs__(self):
|
||||
return Vector(abs(self.x()), abs(self.y()), abs(self.z()))
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import numpy as np
|
||||
from ..Qt import QtGui, QtCore, QtSvg, QT_LIB
|
||||
from ..Qt import QtGui, QtCore, QT_LIB
|
||||
from .. import functions as fn
|
||||
from ..graphicsItems.ROI import ROI
|
||||
from .. import SRTTransform, ItemGroup
|
||||
import importlib
|
||||
@ -240,7 +240,7 @@ class CanvasItem(QtCore.QObject):
|
||||
self._graphicsItem.setOpacity(alpha)
|
||||
|
||||
def setAlpha(self, alpha):
|
||||
self.alphaSlider.setValue(int(np.clip(alpha * 1023, 0, 1023)))
|
||||
self.alphaSlider.setValue(int(fn.clip_scalar(alpha * 1023, 0, 1023)))
|
||||
|
||||
def alpha(self):
|
||||
return self.alphaSlider.value() / 1023.
|
||||
|
@ -202,7 +202,7 @@ def findRefPath(startObj, endObj, maxLen=8, restart=True, seen={}, path=None, ig
|
||||
#print prefix+" FRAME"
|
||||
continue
|
||||
try:
|
||||
if any([r is x for x in path]):
|
||||
if any(r is x for x in path):
|
||||
#print prefix+" LOOP", objChainString([r]+path)
|
||||
continue
|
||||
except:
|
||||
@ -282,7 +282,7 @@ def refPathString(chain):
|
||||
o2 = chain[i]
|
||||
cont = False
|
||||
if isinstance(o1, list) or isinstance(o1, tuple):
|
||||
if any([o2 is x for x in o1]):
|
||||
if any(o2 is x for x in o1):
|
||||
s += "[%d]" % o1.index(o2)
|
||||
continue
|
||||
#print " not list"
|
||||
|
@ -126,7 +126,7 @@ class SplitContainer(Container, QtGui.QSplitter):
|
||||
|
||||
def saveState(self):
|
||||
sizes = self.sizes()
|
||||
if all([x == 0 for x in sizes]):
|
||||
if all(x == 0 for x in sizes):
|
||||
sizes = [10] * len(sizes)
|
||||
return {'sizes': sizes}
|
||||
|
||||
|
@ -60,19 +60,15 @@ def siScale(x, minVal=1e-25, allowUnicode=True):
|
||||
|
||||
if isinstance(x, decimal.Decimal):
|
||||
x = float(x)
|
||||
|
||||
try:
|
||||
if np.isnan(x) or np.isinf(x):
|
||||
if not math.isfinite(x):
|
||||
return(1, '')
|
||||
except:
|
||||
print(x, type(x))
|
||||
raise
|
||||
if abs(x) < minVal:
|
||||
m = 0
|
||||
x = 0
|
||||
else:
|
||||
m = int(np.clip(np.floor(np.log(abs(x))/np.log(1000)), -9.0, 9.0))
|
||||
|
||||
m = int(clip_scalar(math.floor(math.log(abs(x))/math.log(1000)), -9.0, 9.0))
|
||||
if m == 0:
|
||||
pref = ''
|
||||
elif m < -8 or m > 8:
|
||||
@ -84,7 +80,6 @@ def siScale(x, minVal=1e-25, allowUnicode=True):
|
||||
pref = SI_PREFIXES_ASCII[m+8]
|
||||
m1 = -3*m
|
||||
p = 10.**m1
|
||||
|
||||
return (p, pref)
|
||||
|
||||
|
||||
@ -268,6 +263,8 @@ def mkColor(*args):
|
||||
g = int(c[2:4], 16)
|
||||
b = int(c[4:6], 16)
|
||||
a = int(c[6:8], 16)
|
||||
else:
|
||||
raise ValueError(f"Unknown how to convert string {c} to color")
|
||||
elif isinstance(args[0], QtGui.QColor):
|
||||
return QtGui.QColor(args[0])
|
||||
elif np.issubdtype(type(args[0]), np.floating):
|
||||
@ -275,10 +272,10 @@ def mkColor(*args):
|
||||
a = 255
|
||||
elif hasattr(args[0], '__len__'):
|
||||
if len(args[0]) == 3:
|
||||
(r, g, b) = args[0]
|
||||
r, g, b = args[0]
|
||||
a = 255
|
||||
elif len(args[0]) == 4:
|
||||
(r, g, b, a) = args[0]
|
||||
r, g, b, a = args[0]
|
||||
elif len(args[0]) == 2:
|
||||
return intColor(*args[0])
|
||||
else:
|
||||
@ -288,16 +285,13 @@ def mkColor(*args):
|
||||
else:
|
||||
raise TypeError(err)
|
||||
elif len(args) == 3:
|
||||
(r, g, b) = args
|
||||
r, g, b = args
|
||||
a = 255
|
||||
elif len(args) == 4:
|
||||
(r, g, b, a) = args
|
||||
r, g, b, a = args
|
||||
else:
|
||||
raise TypeError(err)
|
||||
|
||||
args = [r,g,b,a]
|
||||
args = [0 if np.isnan(a) or np.isinf(a) else a for a in args]
|
||||
args = list(map(int, args))
|
||||
args = [int(a) if np.isfinite(a) else 0 for a in (r, g, b, a)]
|
||||
return QtGui.QColor(*args)
|
||||
|
||||
|
||||
@ -432,16 +426,16 @@ def makeArrowPath(headLen=20, headWidth=None, tipAngle=20, tailLen=20, tailWidth
|
||||
If *tailLen* is None, no tail will be drawn.
|
||||
"""
|
||||
if headWidth is None:
|
||||
headWidth = headLen * np.tan(tipAngle * 0.5 * np.pi/180.)
|
||||
headWidth = headLen * math.tan(math.radians(tipAngle * 0.5))
|
||||
path = QtGui.QPainterPath()
|
||||
path.moveTo(0,0)
|
||||
path.lineTo(headLen, -headWidth)
|
||||
if tailLen is None:
|
||||
innerY = headLen - headWidth * np.tan(baseAngle*np.pi/180.)
|
||||
innerY = headLen - headWidth * math.tan(math.radians(baseAngle))
|
||||
path.lineTo(innerY, 0)
|
||||
else:
|
||||
tailWidth *= 0.5
|
||||
innerY = headLen - (headWidth-tailWidth) * np.tan(baseAngle*np.pi/180.)
|
||||
innerY = headLen - (headWidth-tailWidth) * math.tan(math.radians(baseAngle))
|
||||
path.lineTo(innerY, -tailWidth)
|
||||
path.lineTo(headLen + tailLen, -tailWidth)
|
||||
path.lineTo(headLen + tailLen, tailWidth)
|
||||
@ -458,7 +452,7 @@ def eq(a, b):
|
||||
|
||||
1. Returns True if a IS b, even if a==b still evaluates to False.
|
||||
2. While a is b will catch the case with np.nan values, special handling is done for distinct
|
||||
float('nan') instances using np.isnan.
|
||||
float('nan') instances using math.isnan.
|
||||
3. Tests for equivalence using ==, but silently ignores some common exceptions that can occur
|
||||
(AtrtibuteError, ValueError).
|
||||
4. When comparing arrays, returns False if the array shapes are not the same.
|
||||
@ -472,7 +466,7 @@ def eq(a, b):
|
||||
|
||||
# The above catches np.nan, but not float('nan')
|
||||
if isinstance(a, float) and isinstance(b, float):
|
||||
if np.isnan(a) and np.isnan(b):
|
||||
if math.isnan(a) and math.isnan(b):
|
||||
return True
|
||||
|
||||
# Avoid comparing large arrays against scalars; this is expensive and we know it should return False.
|
||||
|
@ -1,3 +1,4 @@
|
||||
from math import hypot
|
||||
from ..Qt import QtGui, QtCore
|
||||
from .. import functions as fn
|
||||
import numpy as np
|
||||
@ -135,7 +136,7 @@ class ArrowItem(QtGui.QGraphicsPathItem):
|
||||
pad = 0
|
||||
if self.opts['pxMode']:
|
||||
br = self.boundingRect()
|
||||
pad += (br.width()**2 + br.height()**2) ** 0.5
|
||||
pad += hypot(br.width(), br.height())
|
||||
pen = self.pen()
|
||||
if pen.isCosmetic():
|
||||
pad += max(1, pen.width()) * 0.7072
|
||||
|
@ -4,6 +4,7 @@ from ..python2_3 import asUnicode
|
||||
import numpy as np
|
||||
from ..Point import Point
|
||||
from .. import debug as debug
|
||||
from math import ceil, floor, log, log10, isfinite
|
||||
import sys
|
||||
import weakref
|
||||
from .. import functions as fn
|
||||
@ -511,7 +512,7 @@ class AxisItem(GraphicsWidget):
|
||||
def setRange(self, mn, mx):
|
||||
"""Set the range of values displayed by the axis.
|
||||
Usually this is handled automatically by linking the axis to a ViewBox with :func:`linkToView <pyqtgraph.AxisItem.linkToView>`"""
|
||||
if any(np.isinf((mn, mx))) or any(np.isnan((mn, mx))):
|
||||
if not isfinite(mn) or not isfinite(mx):
|
||||
raise Exception("Not setting range to [%s, %s]" % (str(mn), str(mx)))
|
||||
self.range = [mn, mx]
|
||||
if self.autoSIPrefix:
|
||||
@ -680,13 +681,13 @@ class AxisItem(GraphicsWidget):
|
||||
return []
|
||||
|
||||
## decide optimal minor tick spacing in pixels (this is just aesthetics)
|
||||
optimalTickCount = max(2., np.log(size))
|
||||
optimalTickCount = max(2., log(size))
|
||||
|
||||
## optimal minor tick spacing
|
||||
optimalSpacing = dif / optimalTickCount
|
||||
|
||||
## the largest power-of-10 spacing which is smaller than optimal
|
||||
p10unit = 10 ** np.floor(np.log10(optimalSpacing))
|
||||
p10unit = 10 ** floor(log10(optimalSpacing))
|
||||
|
||||
## Determine major/minor tick spacings which flank the optimal spacing.
|
||||
intervals = np.array([1., 2., 10., 20., 100.]) * p10unit
|
||||
@ -758,7 +759,7 @@ class AxisItem(GraphicsWidget):
|
||||
spacing, offset = tickLevels[i]
|
||||
|
||||
## determine starting tick
|
||||
start = (np.ceil((minVal-offset) / spacing) * spacing) + offset
|
||||
start = (ceil((minVal-offset) / spacing) * spacing) + offset
|
||||
|
||||
## determine number of ticks
|
||||
num = int((maxVal-start) / spacing) + 1
|
||||
@ -766,7 +767,7 @@ class AxisItem(GraphicsWidget):
|
||||
## remove any ticks that were present in higher levels
|
||||
## we assume here that if the difference between a tick value and a previously seen tick value
|
||||
## is less than spacing/100, then they are 'equal' and we can ignore the new tick.
|
||||
values = list(filter(lambda x: all(np.abs(allValues-x) > spacing/self.scale*0.01), values))
|
||||
values = list(filter(lambda x: np.all(np.abs(allValues-x) > spacing/self.scale*0.01), values))
|
||||
allValues = np.concatenate([allValues, values])
|
||||
ticks.append((spacing/self.scale, values))
|
||||
|
||||
@ -795,8 +796,8 @@ class AxisItem(GraphicsWidget):
|
||||
ticks.append((spacing, t))
|
||||
|
||||
if len(ticks) < 3:
|
||||
v1 = int(np.floor(minVal))
|
||||
v2 = int(np.ceil(maxVal))
|
||||
v1 = int(floor(minVal))
|
||||
v2 = int(ceil(maxVal))
|
||||
#major = list(range(v1+1, v2))
|
||||
|
||||
minor = []
|
||||
@ -822,7 +823,7 @@ class AxisItem(GraphicsWidget):
|
||||
if self.logMode:
|
||||
return self.logTickStrings(values, scale, spacing)
|
||||
|
||||
places = max(0, np.ceil(-np.log10(spacing*scale)))
|
||||
places = max(0, ceil(-log10(spacing*scale)))
|
||||
strings = []
|
||||
for v in values:
|
||||
vs = v * scale
|
||||
@ -969,7 +970,7 @@ class AxisItem(GraphicsWidget):
|
||||
if lineAlpha is None:
|
||||
lineAlpha = 255 / (i+1)
|
||||
if self.grid is not False:
|
||||
lineAlpha *= self.grid/255. * np.clip((0.05 * lengthInPixels / (len(ticks)+1)), 0., 1.)
|
||||
lineAlpha *= self.grid/255. * fn.clip_scalar((0.05 * lengthInPixels / (len(ticks)+1)), 0., 1.)
|
||||
elif isinstance(lineAlpha, float):
|
||||
lineAlpha *= 255
|
||||
lineAlpha = max(0, int(round(lineAlpha)))
|
||||
|
@ -1,6 +1,7 @@
|
||||
from math import atan2, degrees
|
||||
from ..Qt import QtGui, QtCore
|
||||
from . import ArrowItem
|
||||
import numpy as np
|
||||
from ..functions import clip_scalar
|
||||
from ..Point import Point
|
||||
import weakref
|
||||
from .GraphicsObject import GraphicsObject
|
||||
@ -61,26 +62,26 @@ class CurvePoint(GraphicsObject):
|
||||
pos = self.property('position')
|
||||
if 'QVariant' in repr(pos): ## need to support 2 APIs :(
|
||||
pos = pos.toDouble()[0]
|
||||
index = (len(x)-1) * np.clip(pos, 0.0, 1.0)
|
||||
index = (len(x)-1) * clip_scalar(pos, 0.0, 1.0)
|
||||
|
||||
if index != int(index): ## interpolate floating-point values
|
||||
i1 = int(index)
|
||||
i2 = np.clip(i1+1, 0, len(x)-1)
|
||||
i2 = clip_scalar(i1+1, 0, len(x)-1)
|
||||
s2 = index-i1
|
||||
s1 = 1.0-s2
|
||||
newPos = (x[i1]*s1+x[i2]*s2, y[i1]*s1+y[i2]*s2)
|
||||
else:
|
||||
index = int(index)
|
||||
i1 = np.clip(index-1, 0, len(x)-1)
|
||||
i2 = np.clip(index+1, 0, len(x)-1)
|
||||
i1 = clip_scalar(index-1, 0, len(x)-1)
|
||||
i2 = clip_scalar(index+1, 0, len(x)-1)
|
||||
newPos = (x[index], y[index])
|
||||
|
||||
p1 = self.parentItem().mapToScene(QtCore.QPointF(x[i1], y[i1]))
|
||||
p2 = self.parentItem().mapToScene(QtCore.QPointF(x[i2], y[i2]))
|
||||
ang = np.arctan2(p2.y()-p1.y(), p2.x()-p1.x()) ## returns radians
|
||||
rads = atan2(p2.y()-p1.y(), p2.x()-p1.x()) ## returns radians
|
||||
self.resetTransform()
|
||||
if self._rotate:
|
||||
self.setRotation(180 + np.rad2deg(ang)) ## takes degrees
|
||||
self.setRotation(180 + degrees(rads))
|
||||
QtGui.QGraphicsItem.setPos(self, *newPos)
|
||||
return True
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import warnings
|
||||
from math import hypot
|
||||
from collections import OrderedDict
|
||||
from functools import reduce
|
||||
from ..Qt import QtGui, QtCore, isQObjectAlive
|
||||
@ -308,7 +309,7 @@ class GraphicsItem(object):
|
||||
v = self.pixelVectors()
|
||||
if v == (None, None):
|
||||
return None, None
|
||||
return (v[0].x()**2+v[0].y()**2)**0.5, (v[1].x()**2+v[1].y()**2)**0.5
|
||||
return (hypot(v[0].x(), v[0].y()), hypot(v[1].x(), v[1].y())) # lengths
|
||||
|
||||
def pixelWidth(self):
|
||||
## deprecated
|
||||
|
@ -126,8 +126,7 @@ class GridItem(UIGraphicsItem):
|
||||
for i in range(self.grid_depth - 1, -1, -1):
|
||||
dist = br-ul
|
||||
nlTarget = 10.**i
|
||||
|
||||
d = 10. ** np.floor(np.log10(abs(dist/nlTarget))+0.5)
|
||||
d = 10. ** np.floor(np.log10(np.abs(dist/nlTarget))+0.5)
|
||||
for ax in range(0,2):
|
||||
ts = self.opts['tickSpacing'][ax]
|
||||
try:
|
||||
@ -141,11 +140,6 @@ class GridItem(UIGraphicsItem):
|
||||
br1 = np.ceil(br / d) * d
|
||||
dist = br1-ul1
|
||||
nl = (dist / d) + 0.5
|
||||
#print "level", i
|
||||
#print " dim", dim
|
||||
#print " dist", dist
|
||||
#print " d", d
|
||||
#print " nl", nl
|
||||
for ax in range(0,2): ## Draw grid for both axes
|
||||
if i >= len(self.opts['tickSpacing'][ax]):
|
||||
continue
|
||||
@ -153,7 +147,7 @@ class GridItem(UIGraphicsItem):
|
||||
continue
|
||||
|
||||
ppl = dim[ax] / nl[ax]
|
||||
c = np.clip(5 * (ppl-3), 0., 50.).astype(int)
|
||||
c = int(fn.clip_scalar(5 * (ppl-3), 0, 50))
|
||||
|
||||
linePen = self.opts['pen']
|
||||
lineColor = self.opts['pen'].color()
|
||||
|
@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from math import atan2, degrees
|
||||
from ..Qt import QtGui, QtCore
|
||||
from ..Point import Point
|
||||
from .GraphicsObject import GraphicsObject
|
||||
@ -359,7 +360,7 @@ class InfiniteLine(GraphicsObject):
|
||||
up = tr.map(Point(left, 1))
|
||||
dif = end - start
|
||||
length = Point(dif).length()
|
||||
angle = np.arctan2(dif.y(), dif.x()) * 180 / np.pi
|
||||
angle = degrees(atan2(dif.y(), dif.x()))
|
||||
|
||||
p.translate(start)
|
||||
p.rotate(angle)
|
||||
@ -592,7 +593,7 @@ class InfLineLabel(TextItem):
|
||||
return
|
||||
|
||||
rel = self._posToRel(ev.pos())
|
||||
self.orthoPos = np.clip(self._startPosition + rel - self._cursorOffset, 0, 1)
|
||||
self.orthoPos = fn.clip_scalar(self._startPosition + rel - self._cursorOffset, 0., 1.)
|
||||
self.updatePosition()
|
||||
if ev.isFinish():
|
||||
self._moving = False
|
||||
|
@ -1,4 +1,5 @@
|
||||
from ..Qt import QtGui, QtCore
|
||||
import math
|
||||
import numpy as np
|
||||
from ..colormap import ColorMap
|
||||
from .GraphicsObject import GraphicsObject
|
||||
@ -94,7 +95,7 @@ class NonUniformImage(GraphicsObject):
|
||||
value = 0.0
|
||||
elif np.isposinf(value):
|
||||
value = 1.0
|
||||
elif np.isnan(value):
|
||||
elif math.isnan(value):
|
||||
continue # ignore NaN
|
||||
else:
|
||||
value = (value - mn) / (mx - mn) # normalize
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ..Qt import QtCore, QtGui, QtWidgets
|
||||
HAVE_OPENGL = hasattr(QtWidgets, 'QOpenGLWidget')
|
||||
|
||||
import math
|
||||
import warnings
|
||||
import numpy as np
|
||||
from .GraphicsObject import GraphicsObject
|
||||
@ -131,6 +131,8 @@ class PlotCurveItem(GraphicsObject):
|
||||
elif ax == 1:
|
||||
d = y
|
||||
d2 = x
|
||||
else:
|
||||
raise ValueError("Invalid axis value")
|
||||
|
||||
## If an orthogonal range is specified, mask the data now
|
||||
if orthoRange is not None:
|
||||
@ -149,7 +151,7 @@ class PlotCurveItem(GraphicsObject):
|
||||
# All-NaN data is acceptable; Explicit numpy warning is not needed.
|
||||
warnings.simplefilter("ignore")
|
||||
b = (np.nanmin(d), np.nanmax(d))
|
||||
if any(np.isinf(b)):
|
||||
if math.isinf(b[0]) or math.isinf(b[1]):
|
||||
mask = np.isfinite(d)
|
||||
d = d[mask]
|
||||
if len(d) == 0:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import warnings
|
||||
import math
|
||||
import numpy as np
|
||||
from .. import metaarray as metaarray
|
||||
from ..Qt import QtCore
|
||||
@ -660,7 +661,7 @@ class PlotDataItem(GraphicsObject):
|
||||
eps = np.finfo(y.dtype).eps
|
||||
else:
|
||||
eps = 1
|
||||
y = np.sign(y) * np.log10(np.abs(y)+eps)
|
||||
y = np.copysign(np.log10(np.abs(y)+eps), y)
|
||||
|
||||
ds = self.opts['downsample']
|
||||
if not isinstance(ds, int):
|
||||
@ -799,10 +800,10 @@ class PlotDataItem(GraphicsObject):
|
||||
# All-NaN data is handled by returning None; Explicit numpy warning is not needed.
|
||||
warnings.simplefilter("ignore")
|
||||
ymin = np.nanmin(self.yData)
|
||||
if np.isnan( ymin ):
|
||||
if math.isnan( ymin ):
|
||||
return None # most likely case for all-NaN data
|
||||
xmin = np.nanmin(self.xData)
|
||||
if np.isnan( xmin ):
|
||||
if math.isnan( xmin ):
|
||||
return None # less likely case for all-NaN data
|
||||
ymax = np.nanmax(self.yData)
|
||||
xmax = np.nanmax(self.xData)
|
||||
|
@ -372,7 +372,7 @@ class PlotItem(GraphicsWidget):
|
||||
if y is not None:
|
||||
self.ctrl.yGridCheck.setChecked(y)
|
||||
if alpha is not None:
|
||||
v = np.clip(alpha, 0, 1)*self.ctrl.gridAlphaSlider.maximum()
|
||||
v = fn.clip_scalar(alpha, 0., 1.)*self.ctrl.gridAlphaSlider.maximum()
|
||||
self.ctrl.gridAlphaSlider.setValue(v)
|
||||
|
||||
def close(self):
|
||||
|
@ -15,9 +15,9 @@ of how to build an ROI at the bottom of the file.
|
||||
from ..Qt import QtCore, QtGui
|
||||
import numpy as np
|
||||
#from numpy.linalg import norm
|
||||
from ..Point import *
|
||||
from ..Point import Point
|
||||
from ..SRTTransform import SRTTransform
|
||||
from math import cos, sin
|
||||
from math import atan2, cos, sin, hypot, radians
|
||||
from .. import functions as fn
|
||||
from .GraphicsObject import GraphicsObject
|
||||
from .UIGraphicsItem import UIGraphicsItem
|
||||
@ -141,12 +141,13 @@ class ROI(GraphicsObject):
|
||||
maxBounds=None, snapSize=1.0, scaleSnap=False,
|
||||
translateSnap=False, rotateSnap=False, parent=None, pen=None,
|
||||
hoverPen=None, handlePen=None, handleHoverPen=None,
|
||||
movable=True, rotatable=True, resizable=True, removable=False):
|
||||
movable=True, rotatable=True, resizable=True, removable=False,
|
||||
aspectLocked=False):
|
||||
GraphicsObject.__init__(self, parent)
|
||||
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
|
||||
pos = Point(pos)
|
||||
size = Point(size)
|
||||
self.aspectLocked = False
|
||||
self.aspectLocked = aspectLocked
|
||||
self.translatable = movable
|
||||
self.rotatable = rotatable
|
||||
self.resizable = resizable
|
||||
@ -678,7 +679,7 @@ class ROI(GraphicsObject):
|
||||
|
||||
The format returned is a list of (name, pos) tuples.
|
||||
"""
|
||||
if index == None:
|
||||
if index is None:
|
||||
positions = []
|
||||
for h in self.handles:
|
||||
positions.append((h['name'], h['pos']))
|
||||
@ -691,7 +692,7 @@ class ROI(GraphicsObject):
|
||||
|
||||
The format returned is a list of (name, pos) tuples.
|
||||
"""
|
||||
if index == None:
|
||||
if index is None:
|
||||
positions = []
|
||||
for h in self.handles:
|
||||
positions.append((h['name'], h['item'].scenePos()))
|
||||
@ -1247,8 +1248,8 @@ class ROI(GraphicsObject):
|
||||
vx = img.mapToData(self.mapToItem(img, QtCore.QPointF(1, 0))) - origin
|
||||
vy = img.mapToData(self.mapToItem(img, QtCore.QPointF(0, 1))) - origin
|
||||
|
||||
lvx = np.sqrt(vx.x()**2 + vx.y()**2)
|
||||
lvy = np.sqrt(vy.x()**2 + vy.y()**2)
|
||||
lvx = hypot(vx.x(), vx.y()) # length
|
||||
lvy = hypot(vy.x(), vy.y()) # length
|
||||
##img.width is number of pixels, not width of item.
|
||||
##need pxWidth and pxHeight instead of pxLen ?
|
||||
sx = 1.0 / lvx
|
||||
@ -1298,7 +1299,7 @@ class ROI(GraphicsObject):
|
||||
"""Return global transformation (rotation angle+translation) required to move
|
||||
from relative state to current state. If relative state isn't specified,
|
||||
then we use the state of the ROI when mouse is pressed."""
|
||||
if relativeTo == None:
|
||||
if relativeTo is None:
|
||||
relativeTo = self.preMoveState
|
||||
st = self.getState()
|
||||
|
||||
@ -1425,7 +1426,7 @@ class Handle(UIGraphicsItem):
|
||||
menu = self.scene().addParentContextMenus(self, self.getMenu(), ev)
|
||||
|
||||
## Make sure it is still ok to remove this handle
|
||||
removeAllowed = all([r.checkRemoveHandle(self) for r in self.rois])
|
||||
removeAllowed = all(r.checkRemoveHandle(self) for r in self.rois)
|
||||
self.removeAction.setEnabled(removeAllowed)
|
||||
pos = ev.screenPos()
|
||||
menu.popup(QtCore.QPoint(pos.x(), pos.y()))
|
||||
@ -1518,13 +1519,13 @@ class Handle(UIGraphicsItem):
|
||||
return None
|
||||
|
||||
v = dt.map(QtCore.QPointF(1, 0)) - dt.map(QtCore.QPointF(0, 0))
|
||||
va = np.arctan2(v.y(), v.x())
|
||||
va = atan2(v.y(), v.x())
|
||||
|
||||
dti = fn.invertQTransform(dt)
|
||||
devPos = dt.map(QtCore.QPointF(0,0))
|
||||
tr = QtGui.QTransform()
|
||||
tr.translate(devPos.x(), devPos.y())
|
||||
tr.rotate(va * 180. / 3.1415926)
|
||||
tr.rotateRadians(va)
|
||||
|
||||
return dti.map(tr.map(self.path))
|
||||
|
||||
@ -1663,7 +1664,7 @@ class LineROI(ROI):
|
||||
d = pos2-pos1
|
||||
l = d.length()
|
||||
ang = Point(1, 0).angle(d)
|
||||
ra = ang * np.pi / 180.
|
||||
ra = radians(ang if ang is not None else 0.)
|
||||
c = Point(-width/2. * sin(ra), -width/2. * cos(ra))
|
||||
pos1 = pos1 + c
|
||||
|
||||
@ -1673,9 +1674,6 @@ class LineROI(ROI):
|
||||
self.addScaleHandle([0.5, 1], [0.5, 0.5])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class MultiRectROI(QtGui.QGraphicsObject):
|
||||
r"""
|
||||
Chain of rectangular ROIs connected by handles.
|
||||
@ -1887,7 +1885,7 @@ class EllipseROI(ROI):
|
||||
h = arr.shape[axes[1]]
|
||||
|
||||
## generate an ellipsoidal mask
|
||||
mask = np.fromfunction(lambda x,y: (((x+0.5)/(w/2.)-1)**2+ ((y+0.5)/(h/2.)-1)**2)**0.5 < 1, (w, h))
|
||||
mask = np.fromfunction(lambda x,y: np.hypot(((x+0.5)/(w/2.)-1), ((y+0.5)/(h/2.)-1)) < 1, (w, h))
|
||||
|
||||
# reshape to match array axes
|
||||
if axes[0] > axes[1]:
|
||||
@ -1944,8 +1942,7 @@ class CircleROI(EllipseROI):
|
||||
if radius is None:
|
||||
raise TypeError("Must provide either size or radius.")
|
||||
size = (radius*2, radius*2)
|
||||
EllipseROI.__init__(self, pos, size, **args)
|
||||
self.aspectLocked = True
|
||||
EllipseROI.__init__(self, pos, size, aspectLocked=True, **args)
|
||||
|
||||
def _addHandles(self):
|
||||
self.addScaleHandle([0.5*2.**-0.5 + 0.5, 0.5*2.**-0.5 + 0.5], [0.5, 0.5])
|
||||
@ -2310,16 +2307,15 @@ class CrosshairROI(ROI):
|
||||
"""A crosshair ROI whose position is at the center of the crosshairs. By default, it is scalable, rotatable and translatable."""
|
||||
|
||||
def __init__(self, pos=None, size=None, **kargs):
|
||||
if size == None:
|
||||
if size is None:
|
||||
size=[1,1]
|
||||
if pos == None:
|
||||
if pos is None:
|
||||
pos = [0,0]
|
||||
self._shape = None
|
||||
ROI.__init__(self, pos, size, **kargs)
|
||||
ROI.__init__(self, pos, size, aspectLocked=True, **kargs)
|
||||
|
||||
self.sigRegionChanged.connect(self.invalidate)
|
||||
self.addScaleRotateHandle(Point(1, 0), Point(0, 0))
|
||||
self.aspectLocked = True
|
||||
|
||||
def invalidate(self):
|
||||
self._shape = None
|
||||
@ -2394,8 +2390,7 @@ class TriangleROI(ROI):
|
||||
"""
|
||||
|
||||
def __init__(self, pos, size, **args):
|
||||
ROI.__init__(self, pos, [size, size], **args)
|
||||
self.aspectLocked = True
|
||||
ROI.__init__(self, pos, [size, size], aspectLocked=True, **args)
|
||||
angles = np.linspace(0, np.pi * 4 / 3, 3)
|
||||
verticies = (np.array((np.sin(angles), np.cos(angles))).T + 1.0) / 2.0
|
||||
self.poly = QtGui.QPolygonF()
|
||||
|
@ -6,6 +6,7 @@ try:
|
||||
except ImportError:
|
||||
imap = map
|
||||
import itertools
|
||||
import math
|
||||
import numpy as np
|
||||
import weakref
|
||||
from ..Qt import QtGui, QtCore, QT_LIB
|
||||
@ -116,7 +117,7 @@ def renderSymbol(symbol, size, pen, brush, device=None):
|
||||
for more information).
|
||||
"""
|
||||
## Render a spot with the given parameters to a pixmap
|
||||
penPxWidth = max(np.ceil(pen.widthF()), 1)
|
||||
penPxWidth = max(math.ceil(pen.widthF()), 1)
|
||||
if device is None:
|
||||
device = QtGui.QImage(int(size+penPxWidth), int(size+penPxWidth), QtGui.QImage.Format_ARGB32)
|
||||
device.fill(0)
|
||||
@ -950,6 +951,8 @@ class ScatterPlotItem(GraphicsObject):
|
||||
elif ax == 1:
|
||||
d = self.data['y']
|
||||
d2 = self.data['x']
|
||||
else:
|
||||
raise ValueError("Invalid axis value")
|
||||
|
||||
if orthoRange is not None:
|
||||
mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1])
|
||||
@ -1073,7 +1076,7 @@ class ScatterPlotItem(GraphicsObject):
|
||||
# Map points using painter's world transform so they are drawn with pixel-valued sizes
|
||||
pts = np.vstack([self.data['x'], self.data['y']])
|
||||
pts = fn.transformCoordinates(p.transform(), pts)
|
||||
pts = np.clip(pts, -2 ** 30, 2 ** 30) # prevent Qt segmentation fault.
|
||||
pts = fn.clip_array(pts, -2 ** 30, 2 ** 30) # prevent Qt segmentation fault.
|
||||
p.resetTransform()
|
||||
|
||||
if self.opts['useCache'] and self._exportOpts is False:
|
||||
|
@ -1,5 +1,4 @@
|
||||
from math import atan2, pi
|
||||
|
||||
from math import atan2
|
||||
from ..Qt import QtGui, QtCore
|
||||
from ..Point import Point
|
||||
from .. import functions as fn
|
||||
@ -243,7 +242,7 @@ class TargetItem(UIGraphicsItem):
|
||||
tr = QtGui.QTransform()
|
||||
tr.translate(devPos.x(), devPos.y())
|
||||
va = atan2(v.y(), v.x())
|
||||
tr.rotate(va * 180.0 / pi)
|
||||
tr.rotateRadians(va)
|
||||
tr.scale(self.scale, self.scale)
|
||||
return dti.map(tr.map(self._path))
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import numpy as np
|
||||
from math import atan2, degrees
|
||||
from ..Qt import QtCore, QtGui
|
||||
from ..Point import Point
|
||||
from .. import functions as fn
|
||||
@ -208,7 +208,7 @@ class TextItem(GraphicsObject):
|
||||
angle = -self.angle
|
||||
if self.rotateAxis is not None:
|
||||
d = pt.map(self.rotateAxis) - pt.map(Point(0, 0))
|
||||
a = np.arctan2(d.y(), d.x()) * 180 / np.pi
|
||||
a = degrees(atan2(d.y(), d.x()))
|
||||
angle += a
|
||||
t.rotate(angle)
|
||||
self.setTransform(t)
|
||||
|
@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import weakref
|
||||
import sys
|
||||
import math
|
||||
from copy import deepcopy
|
||||
import numpy as np
|
||||
from ...Qt import QtGui, QtCore
|
||||
@ -549,10 +550,9 @@ class ViewBox(GraphicsWidget):
|
||||
dy = 1
|
||||
mn -= dy*0.5
|
||||
mx += dy*0.5
|
||||
xpad = 0.0
|
||||
|
||||
# Make sure no nan/inf get through
|
||||
if not all(np.isfinite([mn, mx])):
|
||||
if not math.isfinite(mn) or not math.isfinite(mx):
|
||||
raise Exception("Cannot set range [%s, %s]" % (str(mn), str(mx)))
|
||||
|
||||
# Apply padding
|
||||
@ -666,7 +666,7 @@ class ViewBox(GraphicsWidget):
|
||||
def suggestPadding(self, axis):
|
||||
l = self.width() if axis==0 else self.height()
|
||||
if l > 0:
|
||||
padding = np.clip(1./(l**0.5), 0.02, 0.1)
|
||||
padding = fn.clip_scalar(1./(l**0.5), 0.02, 0.1)
|
||||
else:
|
||||
padding = 0.02
|
||||
return padding
|
||||
@ -906,8 +906,8 @@ class ViewBox(GraphicsWidget):
|
||||
# check for and ignore bad ranges
|
||||
for k in ['xRange', 'yRange']:
|
||||
if k in args:
|
||||
if not np.all(np.isfinite(args[k])):
|
||||
r = args.pop(k)
|
||||
if not math.isfinite(args[k][0]) or not math.isfinite(args[k][1]):
|
||||
_ = args.pop(k)
|
||||
#print("Warning: %s is invalid: %s" % (k, str(r))
|
||||
|
||||
if len(args) == 0:
|
||||
@ -1369,10 +1369,20 @@ class ViewBox(GraphicsWidget):
|
||||
xr = item.dataBounds(0, frac=frac[0], orthoRange=orthoRange[0])
|
||||
yr = item.dataBounds(1, frac=frac[1], orthoRange=orthoRange[1])
|
||||
pxPad = 0 if not hasattr(item, 'pixelPadding') else item.pixelPadding()
|
||||
if xr is None or (xr[0] is None and xr[1] is None) or np.isnan(xr).any() or np.isinf(xr).any():
|
||||
if (
|
||||
xr is None or
|
||||
(xr[0] is None and xr[1] is None) or
|
||||
not math.isfinite(xr[0]) or
|
||||
not math.isfinite(xr[1])
|
||||
):
|
||||
useX = False
|
||||
xr = (0,0)
|
||||
if yr is None or (yr[0] is None and yr[1] is None) or np.isnan(yr).any() or np.isinf(yr).any():
|
||||
if (
|
||||
yr is None or
|
||||
(yr[0] is None and yr[1] is None) or
|
||||
not math.isfinite(yr[0]) or
|
||||
not math.isfinite(yr[1])
|
||||
):
|
||||
useY = False
|
||||
yr = (0,0)
|
||||
|
||||
|
@ -12,10 +12,12 @@ Widget used for displaying 2D or 3D data. Features:
|
||||
- ROI plotting
|
||||
- Image normalization through a variety of methods
|
||||
"""
|
||||
import os, sys
|
||||
import os
|
||||
from math import log10
|
||||
import numpy as np
|
||||
|
||||
from ..Qt import QtCore, QtGui, QT_LIB
|
||||
from .. import functions as fn
|
||||
import importlib
|
||||
ui_template = importlib.import_module(
|
||||
f'.ImageViewTemplate_{QT_LIB.lower()}', package=__package__)
|
||||
@ -272,7 +274,7 @@ class ImageView(QtGui.QWidget):
|
||||
|
||||
if not isinstance(img, np.ndarray):
|
||||
required = ['dtype', 'max', 'min', 'ndim', 'shape', 'size']
|
||||
if not all([hasattr(img, attr) for attr in required]):
|
||||
if not all(hasattr(img, attr) for attr in required):
|
||||
raise TypeError("Image must be NumPy array or any object "
|
||||
"that provides compatible attributes/methods:\n"
|
||||
" %s" % str(required))
|
||||
@ -512,7 +514,7 @@ class ImageView(QtGui.QWidget):
|
||||
|
||||
def setCurrentIndex(self, ind):
|
||||
"""Set the currently displayed frame index."""
|
||||
index = np.clip(ind, 0, self.getProcessedImage().shape[self.axes['t']]-1)
|
||||
index = fn.clip_scalar(ind, 0, self.getProcessedImage().shape[self.axes['t']]-1)
|
||||
self.ignorePlaying = True
|
||||
# Implicitly call timeLineChanged
|
||||
self.timeLine.setValue(self.tVals[index])
|
||||
@ -807,7 +809,7 @@ class ImageView(QtGui.QWidget):
|
||||
img = self.getProcessedImage()
|
||||
if self.hasTimeAxis():
|
||||
base, ext = os.path.splitext(fileName)
|
||||
fmt = "%%s%%0%dd%%s" % int(np.log10(img.shape[0])+1)
|
||||
fmt = "%%s%%0%dd%%s" % int(log10(img.shape[0])+1)
|
||||
for i in range(img.shape[0]):
|
||||
self.imageItem.setImage(img[i], autoLevels=False)
|
||||
self.imageItem.save(fmt % (base, i, ext))
|
||||
|
@ -124,7 +124,7 @@ class MetaArray(object):
|
||||
nameTypes = [basestring, tuple]
|
||||
@staticmethod
|
||||
def isNameType(var):
|
||||
return any([isinstance(var, t) for t in MetaArray.nameTypes])
|
||||
return any(isinstance(var, t) for t in MetaArray.nameTypes)
|
||||
|
||||
|
||||
## methods to wrap from embedded ndarray / HDF5
|
||||
|
@ -5,6 +5,7 @@ import numpy as np
|
||||
from .. import Vector
|
||||
from .. import functions as fn
|
||||
import warnings
|
||||
from math import cos, sin, tan, radians
|
||||
##Vector = QtGui.QVector3D
|
||||
|
||||
ShareWidget = None
|
||||
@ -174,7 +175,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
|
||||
nearClip = dist * 0.001
|
||||
farClip = dist * 1000.
|
||||
|
||||
r = nearClip * np.tan(fov * 0.5 * np.pi / 180.)
|
||||
r = nearClip * tan(0.5 * radians(fov))
|
||||
t = r * h / w
|
||||
|
||||
## Note that X0 and width in these equations must be the values used in viewport
|
||||
@ -316,12 +317,12 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
|
||||
pos = center - self.opts['rotation'].rotatedVector( Vector(0,0,dist) )
|
||||
else:
|
||||
# using 'euler' rotation method
|
||||
elev = self.opts['elevation'] * np.pi / 180
|
||||
azim = self.opts['azimuth'] * np.pi / 180
|
||||
elev = radians(self.opts['elevation'])
|
||||
azim = radians(self.opts['azimuth'])
|
||||
pos = Vector(
|
||||
center.x() + dist * np.cos(elev) * np.cos(azim),
|
||||
center.y() + dist * np.cos(elev) * np.sin(azim),
|
||||
center.z() + dist * np.sin(elev)
|
||||
center.x() + dist * cos(elev) * cos(azim),
|
||||
center.y() + dist * cos(elev) * sin(azim),
|
||||
center.z() + dist * sin(elev)
|
||||
)
|
||||
return pos
|
||||
|
||||
@ -335,7 +336,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
|
||||
self.opts['rotation'] = q
|
||||
else: # default euler rotation method
|
||||
self.opts['azimuth'] += azim
|
||||
self.opts['elevation'] = np.clip(self.opts['elevation'] + elev, -90, 90)
|
||||
self.opts['elevation'] = fn.clip_scalar(self.opts['elevation'] + elev, -90., 90.)
|
||||
self.update()
|
||||
|
||||
def pan(self, dx, dy, dz, relative='global'):
|
||||
@ -378,7 +379,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
|
||||
cPos = self.cameraPosition()
|
||||
cVec = self.opts['center'] - cPos
|
||||
dist = cVec.length() ## distance from camera to center
|
||||
xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) ## approx. width of view at distance of center point
|
||||
xDist = dist * 2. * tan(0.5 * radians(self.opts['fov'])) ## approx. width of view at distance of center point
|
||||
xScale = xDist / self.width()
|
||||
zVec = QtGui.QVector3D(0,0,1)
|
||||
xVec = QtGui.QVector3D.crossProduct(zVec, cVec).normalized()
|
||||
@ -399,15 +400,15 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
|
||||
# apply translation
|
||||
self.opts['center'] += scale_factor * (xv*-dx + yv*dy + zv*dz)
|
||||
else: # use default euler rotation method
|
||||
elev = np.radians(self.opts['elevation'])
|
||||
azim = np.radians(self.opts['azimuth'])
|
||||
fov = np.radians(self.opts['fov'])
|
||||
elev = radians(self.opts['elevation'])
|
||||
azim = radians(self.opts['azimuth'])
|
||||
fov = radians(self.opts['fov'])
|
||||
dist = (self.opts['center'] - self.cameraPosition()).length()
|
||||
fov_factor = np.tan(fov / 2) * 2
|
||||
fov_factor = tan(fov / 2) * 2
|
||||
scale_factor = dist * fov_factor / self.width()
|
||||
z = scale_factor * np.cos(elev) * dy
|
||||
x = scale_factor * (np.sin(azim) * dx - np.sin(elev) * np.cos(azim) * dy)
|
||||
y = scale_factor * (np.cos(azim) * dx + np.sin(elev) * np.sin(azim) * dy)
|
||||
z = scale_factor * cos(elev) * dy
|
||||
x = scale_factor * (sin(azim) * dx - sin(elev) * cos(azim) * dy)
|
||||
y = scale_factor * (cos(azim) * dx + sin(elev) * sin(azim) * dy)
|
||||
self.opts['center'] += QtGui.QVector3D(x, -y, z)
|
||||
else:
|
||||
raise ValueError("relative argument must be global, view, or view-upright")
|
||||
@ -425,7 +426,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
|
||||
dist = ((pos-cam)**2).sum(axis=-1)**0.5
|
||||
else:
|
||||
dist = (pos-cam).length()
|
||||
xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.)
|
||||
xDist = dist * 2. * tan(0.5 * radians(self.opts['fov']))
|
||||
return xDist / self.width()
|
||||
|
||||
def mousePressEvent(self, ev):
|
||||
|
@ -3,7 +3,8 @@ from OpenGL.GL import *
|
||||
from OpenGL.arrays import vbo
|
||||
from .. GLGraphicsItem import GLGraphicsItem
|
||||
from .. import shaders
|
||||
from ... import QtGui
|
||||
from ... import functions as fn
|
||||
from ...Qt import QtGui
|
||||
import numpy as np
|
||||
|
||||
__all__ = ['GLScatterPlotItem']
|
||||
@ -61,13 +62,12 @@ class GLScatterPlotItem(GLGraphicsItem):
|
||||
|
||||
## Generate texture for rendering points
|
||||
w = 64
|
||||
def fn(x,y):
|
||||
r = ((x-(w-1)/2.)**2 + (y-(w-1)/2.)**2) ** 0.5
|
||||
return 255 * (w/2. - np.clip(r, w/2.-1.0, w/2.))
|
||||
def genTexture(x,y):
|
||||
r = np.hypot((x-(w-1)/2.), (y-(w-1)/2.))
|
||||
return 255 * (w / 2 - fn.clip_array(r, w / 2 - 1, w / 2))
|
||||
pData = np.empty((w, w, 4))
|
||||
pData[:] = 255
|
||||
pData[:,:,3] = np.fromfunction(fn, pData.shape[:2])
|
||||
#print pData.shape, pData.min(), pData.max()
|
||||
pData[:,:,3] = np.fromfunction(genTexture, pData.shape[:2])
|
||||
pData = pData.astype(np.ubyte)
|
||||
|
||||
if getattr(self, "pointTexture", None) is None:
|
||||
|
@ -1,6 +1,8 @@
|
||||
from collections import OrderedDict
|
||||
import numpy as np
|
||||
import copy
|
||||
from math import log2
|
||||
from .. import functions as fn
|
||||
|
||||
|
||||
class SystemSolver(object):
|
||||
@ -390,15 +392,12 @@ if __name__ == '__main__':
|
||||
sh = self.shutter # this raises RuntimeError if shutter has not
|
||||
# been specified
|
||||
ap = 4.0 * (sh / (1./60.)) * (iso / 100.) * (2 ** exp) * (2 ** light)
|
||||
ap = np.clip(ap, 2.0, 16.0)
|
||||
ap = fn.clip_scalar(ap, 2.0, 16.0)
|
||||
except RuntimeError:
|
||||
# program mode; we can select a suitable shutter
|
||||
# value at the same time.
|
||||
sh = (1./60.)
|
||||
raise
|
||||
|
||||
|
||||
|
||||
return ap
|
||||
|
||||
def _balance(self):
|
||||
@ -406,10 +405,8 @@ if __name__ == '__main__':
|
||||
light = self.lightMeter
|
||||
sh = self.shutter
|
||||
ap = self.aperture
|
||||
fl = self.flash
|
||||
|
||||
bal = (4.0 / ap) * (sh / (1./60.)) * (iso / 100.) * (2 ** light)
|
||||
return np.log2(bal)
|
||||
return log2(bal)
|
||||
|
||||
camera = Camera()
|
||||
|
||||
|
@ -161,7 +161,7 @@ class ColorMapParameter(ptree.types.GroupParameter):
|
||||
elif op == 'Set':
|
||||
colors[mask] = colors2[mask]
|
||||
|
||||
colors = np.clip(colors, 0, 1)
|
||||
colors = fn.clip_array(colors, 0., 1.)
|
||||
if mode == 'byte':
|
||||
colors = (colors * 255).astype(np.ubyte)
|
||||
|
||||
@ -210,11 +210,11 @@ class RangeColorMapItem(ptree.types.SimpleParameter):
|
||||
def map(self, data):
|
||||
data = data[self.fieldName]
|
||||
|
||||
scaled = np.clip((data-self['Min']) / (self['Max']-self['Min']), 0, 1)
|
||||
scaled = fn.clip_array((data-self['Min']) / (self['Max']-self['Min']), 0, 1)
|
||||
cmap = self.value()
|
||||
colors = cmap.map(scaled, mode='float')
|
||||
|
||||
mask = np.isnan(data) | np.isinf(data)
|
||||
mask = np.invert(np.isfinite(data))
|
||||
nanColor = self['NaN']
|
||||
nanColor = (nanColor.red()/255., nanColor.green()/255., nanColor.blue()/255., nanColor.alpha()/255.)
|
||||
colors[mask] = nanColor
|
||||
|
@ -7,10 +7,7 @@ Distributed under MIT/X11 license. See license.txt for more information.
|
||||
|
||||
from ..Qt import QtCore, QtGui, QtWidgets, QT_LIB
|
||||
from ..Point import Point
|
||||
import sys, os
|
||||
from .FileDialog import FileDialog
|
||||
from ..GraphicsScene import GraphicsScene
|
||||
import numpy as np
|
||||
from .. import functions as fn
|
||||
from .. import debug as debug
|
||||
from .. import getConfigOption
|
||||
@ -378,7 +375,7 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
return
|
||||
|
||||
if ev.buttons() == QtCore.Qt.RightButton:
|
||||
delta = Point(np.clip(delta[0], -50, 50), np.clip(-delta[1], -50, 50))
|
||||
delta = Point(fn.clip_scalar(delta[0], -50, 50), fn.clip_scalar(-delta[1], -50, 50))
|
||||
scale = 1.01 ** delta
|
||||
self.scale(scale[0], scale[1], center=self.mapToScene(self.mousePressPos))
|
||||
self.sigDeviceRangeChanged.emit(self, self.range)
|
||||
|
@ -1,4 +1,5 @@
|
||||
from ..Qt import QtGui, QtCore
|
||||
from math import hypot
|
||||
from ..Qt import QtGui, QtCore, mkQApp
|
||||
|
||||
|
||||
__all__ = ['JoystickButton']
|
||||
@ -41,7 +42,7 @@ class JoystickButton(QtGui.QPushButton):
|
||||
|
||||
def setState(self, *xy):
|
||||
xy = list(xy)
|
||||
d = (xy[0]**2 + xy[1]**2)**0.5
|
||||
d = hypot(xy[0], xy[1]) # length
|
||||
nxy = [0, 0]
|
||||
for i in [0,1]:
|
||||
if xy[i] == 0:
|
||||
@ -84,7 +85,7 @@ class JoystickButton(QtGui.QPushButton):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = pg.mkQApp()
|
||||
app = mkQApp()
|
||||
w = QtGui.QMainWindow()
|
||||
b = JoystickButton()
|
||||
w.setCentralWidget(b)
|
||||
|
Loading…
Reference in New Issue
Block a user