Merge pull request #1724 from j9ac9k/use-math-module-for-single-values

Use math module methods for scalars
This commit is contained in:
Ogi Moore 2021-04-23 23:10:17 -07:00 committed by GitHub
commit 6a708dc203
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 201 additions and 244 deletions

View File

@ -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,23 +22,16 @@ 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..")

View File

@ -29,19 +29,16 @@ 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)
if __name__ == '__main__':
pg.mkQApp().exec_()

View File

@ -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

View File

@ -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())

View File

@ -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)

View File

@ -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])
ar = a1 + np.pi - 2*ai
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
@ -423,8 +428,7 @@ class CircleSurface(pg.GraphicsObject):
sgn = -1
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

View File

@ -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,42 +87,21 @@ 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."""
return self / self.length()
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."""
@ -146,8 +119,7 @@ class Point(QtCore.QPointF):
def __repr__(self):
return "Point(%f, %f)" % (self[0], self[1])
def min(self):
return min(self[0], self[1])

View File

@ -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()

View File

@ -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)

View File

@ -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()))

View File

@ -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.

View File

@ -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"

View File

@ -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}

View File

@ -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.

View File

@ -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

View File

@ -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)))

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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):

View File

@ -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()))
@ -1481,7 +1482,7 @@ class Handle(UIGraphicsItem):
size = self.radius
self.path = QtGui.QPainterPath()
ang = self.startAng
dt = 2*np.pi / self.sides
dt = 2 * np.pi / self.sides
for i in range(0, self.sides+1):
x = size * cos(ang)
y = size * sin(ang)
@ -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
@ -1671,9 +1672,6 @@ class LineROI(ROI):
self.addScaleRotateHandle([0, 0.5], [1, 0.5])
self.addScaleRotateHandle([1, 0.5], [0, 0.5])
self.addScaleHandle([0.5, 1], [0.5, 0.5])
class MultiRectROI(QtGui.QGraphicsObject):
@ -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]:
@ -1914,7 +1912,7 @@ class EllipseROI(ROI):
center = br.center()
r1 = br.width() / 2.
r2 = br.height() / 2.
theta = np.linspace(0, 2*np.pi, 24)
theta = np.linspace(0, 2 * np.pi, 24)
x = center.x() + r1 * np.cos(theta)
y = center.y() + r2 * np.sin(theta)
path.moveTo(x[0], y[0])
@ -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()

View File

@ -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:

View File

@ -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))

View File

@ -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)

View File

@ -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
@ -903,11 +903,11 @@ class ViewBox(GraphicsWidget):
targetRect[ax] = childRange[ax]
args['xRange' if ax == 0 else 'yRange'] = targetRect[ax]
# check for and ignore bad ranges
# 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)

View File

@ -12,14 +12,16 @@ 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__)
from ..graphicsItems.ImageItem import *
from ..graphicsItems.ROI import *
from ..graphicsItems.LinearRegionItem import *
@ -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))

View File

@ -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

View File

@ -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):

View File

@ -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:

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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)