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