From b0a3849960325f2fca4b1336d68d936fd4ea7314 Mon Sep 17 00:00:00 2001 From: Ogi Moore Date: Sat, 17 Apr 2021 20:08:57 -0700 Subject: [PATCH] Use math module methods for singular values Using numpy methods that are intended for vectorized operations is substantially slower than using the math module, so when feasible the math module methods should be used. --- examples/GLIsosurface.py | 11 +++----- examples/GLVolumeItem.py | 2 +- examples/GLshaders.py | 4 +-- examples/optics/pyoptic.py | 36 ++++++++++++------------- pyqtgraph/Point.py | 10 +++---- pyqtgraph/SRTTransform.py | 3 ++- pyqtgraph/SRTTransform3D.py | 3 ++- pyqtgraph/graphicsItems/CurvePoint.py | 12 +++++---- pyqtgraph/graphicsItems/InfiniteLine.py | 3 ++- pyqtgraph/graphicsItems/ROI.py | 22 +++++++-------- pyqtgraph/graphicsItems/TextItem.py | 4 +-- 11 files changed, 53 insertions(+), 57 deletions(-) diff --git a/examples/GLIsosurface.py b/examples/GLIsosurface.py index 16045a78..3da8dfa5 100644 --- a/examples/GLIsosurface.py +++ b/examples/GLIsosurface.py @@ -7,6 +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 +import numpy as np from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph as pg import pyqtgraph.opengl as gl @@ -22,23 +23,17 @@ g = gl.GLGridItem() g.scale(2,2,1) w.addItem(g) -import numpy as np - ## Define a scalar field from which we will generate an isosurface def psi(i, j, k, offset=(25, 25, 50)): x = i-offset[0] y = j-offset[1] z = k-offset[2] - th = np.arctan2(z, (x**2+y**2)**0.5) + th = np.arctan2(z, np.sqrt(x**2+y**2)) phi = np.arctan2(y, x) - r = (x**2 + y**2 + z **2)**0.5 + 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..") diff --git a/examples/GLVolumeItem.py b/examples/GLVolumeItem.py index 5d7e6601..601ddfa1 100644 --- a/examples/GLVolumeItem.py +++ b/examples/GLVolumeItem.py @@ -7,6 +7,7 @@ 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 @@ -23,7 +24,6 @@ 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] diff --git a/examples/GLshaders.py b/examples/GLshaders.py index 4e1acc51..38e6988d 100644 --- a/examples/GLshaders.py +++ b/examples/GLshaders.py @@ -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) diff --git a/examples/optics/pyoptic.py b/examples/optics/pyoptic.py index f70edd2a..811ae665 100644 --- a/examples/optics/pyoptic.py +++ b/examples/optics/pyoptic.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, asin, sin, cos, sqrt, pi 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 + 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 = asin(h2/r) * 180. / pi a2 = -2*a1 a1 += 180. self.path.arcMoveTo(arc, a1) @@ -406,7 +411,7 @@ 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) @@ -436,19 +441,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 + norm += pi 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 diff --git a/pyqtgraph/Point.py b/pyqtgraph/Point.py index fea37dda..148178f9 100644 --- a/pyqtgraph/Point.py +++ b/pyqtgraph/Point.py @@ -6,7 +6,7 @@ Distributed under MIT/X11 license. See license.txt for more information. """ from .Qt import QtCore -import numpy as np +from math import sin, acos, atan2, inf, pi def clip(x, mn, mx): if x > mx: @@ -109,9 +109,9 @@ class Point(QtCore.QPointF): return (self[0]**2 + self[1]**2) ** 0.5 except OverflowError: try: - return self[1] / np.sin(np.arctan2(self[1], self[0])) + return self[1] / sin(atan2(self[1], self[0])) except OverflowError: - return np.inf + return inf def norm(self): """Returns a vector in the same direction with unit length.""" @@ -124,11 +124,11 @@ class Point(QtCore.QPointF): 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 + ang = acos(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 + return ang * 180. / pi def dot(self, a): """Returns the dot product of a and this Point.""" diff --git a/pyqtgraph/SRTTransform.py b/pyqtgraph/SRTTransform.py index 35ec0625..81bfb24d 100644 --- a/pyqtgraph/SRTTransform.py +++ b/pyqtgraph/SRTTransform.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, pi 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': (atan2(dp2[1], dp2[0]) * 180. / pi) + da } self.update() diff --git a/pyqtgraph/SRTTransform3D.py b/pyqtgraph/SRTTransform3D.py index 7d458edd..438a410c 100644 --- a/pyqtgraph/SRTTransform3D.py +++ b/pyqtgraph/SRTTransform3D.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, pi 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'] = atan2(sin, cos) * 180 / pi if self._state['angle'] == 0: self._state['axis'] = (0,0,1) diff --git a/pyqtgraph/graphicsItems/CurvePoint.py b/pyqtgraph/graphicsItems/CurvePoint.py index 368a0a82..9e4d3718 100644 --- a/pyqtgraph/graphicsItems/CurvePoint.py +++ b/pyqtgraph/graphicsItems/CurvePoint.py @@ -1,5 +1,7 @@ +from math import atan2, pi from ..Qt import QtGui, QtCore from . import ArrowItem +from ..functions import clip_scalar import numpy as np from ..Point import Point import weakref @@ -65,22 +67,22 @@ class CurvePoint(GraphicsObject): 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 + ang = 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 + ang * (180. / pi)) QtGui.QGraphicsItem.setPos(self, *newPos) return True diff --git a/pyqtgraph/graphicsItems/InfiniteLine.py b/pyqtgraph/graphicsItems/InfiniteLine.py index 74a8da95..ce96d00b 100644 --- a/pyqtgraph/graphicsItems/InfiniteLine.py +++ b/pyqtgraph/graphicsItems/InfiniteLine.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, pi 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 = atan2(dif.y(), dif.x()) * 180 / pi p.translate(start) p.rotate(angle) diff --git a/pyqtgraph/graphicsItems/ROI.py b/pyqtgraph/graphicsItems/ROI.py index c2cb49ed..566651b8 100644 --- a/pyqtgraph/graphicsItems/ROI.py +++ b/pyqtgraph/graphicsItems/ROI.py @@ -17,7 +17,7 @@ import numpy as np #from numpy.linalg import norm from ..Point import * from ..SRTTransform import SRTTransform -from math import cos, sin +from math import atan2, cos, sin, pi, sqrt from .. import functions as fn from .GraphicsObject import GraphicsObject from .UIGraphicsItem import UIGraphicsItem @@ -1247,8 +1247,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 = sqrt(vx.x()**2 + vx.y()**2) + lvy = sqrt(vy.x()**2 + vy.y()**2) ##img.width is number of pixels, not width of item. ##need pxWidth and pxHeight instead of pxLen ? sx = 1.0 / lvx @@ -1332,8 +1332,8 @@ class Handle(UIGraphicsItem): properties of the ROI they are attached to. """ types = { ## defines number of sides, start angle for each handle type - 't': (4, np.pi/4), - 'f': (4, np.pi/4), + 't': (4, pi/4), + 'f': (4, pi/4), 's': (4, 0), 'r': (12, 0), 'sr': (12, 0), @@ -1481,7 +1481,7 @@ class Handle(UIGraphicsItem): size = self.radius self.path = QtGui.QPainterPath() ang = self.startAng - dt = 2*np.pi / self.sides + dt = 2 * pi / self.sides for i in range(0, self.sides+1): x = size * cos(ang) y = size * sin(ang) @@ -1518,13 +1518,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.rotate(va * 180. / pi) return dti.map(tr.map(self.path)) @@ -1663,7 +1663,7 @@ class LineROI(ROI): d = pos2-pos1 l = d.length() ang = Point(1, 0).angle(d) - ra = ang * np.pi / 180. + ra = ang * pi / 180. c = Point(-width/2. * sin(ra), -width/2. * cos(ra)) pos1 = pos1 + c @@ -1914,7 +1914,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 * pi, 24) x = center.x() + r1 * np.cos(theta) y = center.y() + r2 * np.sin(theta) path.moveTo(x[0], y[0]) @@ -2396,7 +2396,7 @@ class TriangleROI(ROI): def __init__(self, pos, size, **args): ROI.__init__(self, pos, [size, size], **args) self.aspectLocked = True - angles = np.linspace(0, np.pi * 4 / 3, 3) + angles = np.linspace(0, pi * 4 / 3, 3) verticies = (np.array((np.sin(angles), np.cos(angles))).T + 1.0) / 2.0 self.poly = QtGui.QPolygonF() for pt in verticies: diff --git a/pyqtgraph/graphicsItems/TextItem.py b/pyqtgraph/graphicsItems/TextItem.py index 8dcb04f6..6b2dfdd5 100644 --- a/pyqtgraph/graphicsItems/TextItem.py +++ b/pyqtgraph/graphicsItems/TextItem.py @@ -1,4 +1,4 @@ -import numpy as np +from math import pi, atan2 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 = atan2(d.y(), d.x()) * 180 / pi angle += a t.rotate(angle) self.setTransform(t)