Commit 8c13a3e7 by Luke Campagnola

### copy from acq4

parent 008ca76d
 ... ... @@ -80,6 +80,12 @@ class Point(QtCore.QPointF): def __div__(self, a): return self._math_('__div__', a) def __truediv__(self, a): return self._math_('__truediv__', a) def __rtruediv__(self, a): return self._math_('__rtruediv__', a) def __rpow__(self, a): return self._math_('__rpow__', a) ... ...
 ... ... @@ -130,11 +130,14 @@ class SRTTransform(QtGui.QTransform): self._state['angle'] = angle self.update() def __div__(self, t): def __truediv__(self, t): """A / B == B^-1 * A""" dt = t.inverted()[0] * self return SRTTransform(dt) def __div__(self, t): return self.__truediv__(t) def __mul__(self, t): return SRTTransform(QtGui.QTransform.__mul__(self, t)) ... ...
 ... ... @@ -123,7 +123,6 @@ class SRTTransform3D(pg.Transform3D): m = self.matrix().reshape(4,4) ## translation is 4th column self._state['pos'] = m[:3,3] ## scale is vector-length of first three columns scale = (m[:3,:3]**2).sum(axis=0)**0.5 ## see whether there is an inversion ... ... @@ -141,18 +140,30 @@ class SRTTransform3D(pg.Transform3D): print("Scale: %s" % str(scale)) print("Original matrix: %s" % str(m)) raise eigIndex = np.argwhere(np.abs(evals-1) < 1e-7) eigIndex = np.argwhere(np.abs(evals-1) < 1e-6) if len(eigIndex) < 1: print("eigenvalues: %s" % str(evals)) print("eigenvectors: %s" % str(evecs)) print("index: %s, %s" % (str(eigIndex), str(evals-1))) raise Exception("Could not determine rotation axis.") axis = evecs[eigIndex[0,0]].real axis = evecs[:,eigIndex[0,0]].real axis /= ((axis**2).sum())**0.5 self._state['axis'] = axis ## trace(r) == 2 cos(angle) + 1, so: self._state['angle'] = np.arccos((r.trace()-1)*0.5) * 180 / np.pi cos = (r.trace()-1)*0.5 ## this only gets us abs(angle) ## The off-diagonal values can be used to correct the angle ambiguity, ## but we need to figure out which element to use: axisInd = np.argmax(np.abs(axis)) rInd,sign = [((1,2), -1), ((0,2), 1), ((0,1), -1)][axisInd] ## Then we have r-r.T = sin(angle) * 2 * sign * axis[axisInd]; ## solve for sin(angle) 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 if self._state['angle'] == 0: self._state['axis'] = (0,0,1) ... ...
 ... ... @@ -28,6 +28,15 @@ def ftrace(func): return rv return w def warnOnException(func): """Decorator which catches/ignores exceptions and prints a stack trace.""" def w(*args, **kwds): try: func(*args, **kwds) except: printExc('Ignored exception:') return w def getExc(indent=4, prefix='| '): tb = traceback.format_exc() lines = [] ... ...
 ... ... @@ -24,7 +24,15 @@ class BinOpNode(Node): }) def process(self, **args): fn = getattr(args['A'], self.fn) if isinstance(self.fn, tuple): for name in self.fn: try: fn = getattr(args['A'], name) break except AttributeError: pass else: fn = getattr(args['A'], self.fn) out = fn(args['B']) if out is NotImplemented: raise Exception("Operation %s not implemented between %s and %s" % (fn, str(type(args['A'])), str(type(args['B'])))) ... ... @@ -60,5 +68,7 @@ class DivideNode(BinOpNode): """Returns A / B. Does not check input types.""" nodeName = 'Divide' def __init__(self, name): BinOpNode.__init__(self, name, '__div__') # try truediv first, followed by div BinOpNode.__init__(self, name, ('__truediv__', '__div__'))
 ... ... @@ -264,6 +264,7 @@ def mkPen(*args, **kargs): color = kargs.get('color', None) width = kargs.get('width', 1) style = kargs.get('style', None) dash = kargs.get('dash', None) cosmetic = kargs.get('cosmetic', True) hsv = kargs.get('hsv', None) ... ... @@ -291,6 +292,8 @@ def mkPen(*args, **kargs): pen.setCosmetic(cosmetic) if style is not None: pen.setStyle(style) if dash is not None: pen.setDashPattern(dash) return pen def hsvColor(hue, sat=1.0, val=1.0, alpha=1.0): ... ... @@ -1948,6 +1951,8 @@ def pseudoScatter(data, spacing=None, shuffle=True, bidir=False): s2 = spacing**2 yvals = np.empty(len(data)) if len(data) == 0: return yvals yvals[0] = 0 for i in range(1,len(data)): x = data[i] # current x value to be placed ... ...
 ... ... @@ -42,12 +42,18 @@ class AxisItem(GraphicsWidget): self.label.rotate(-90) self.style = { 'tickTextOffset': 3, ## spacing between text and axis 'tickTextOffset': (5, 2), ## (horizontal, vertical) spacing between text and axis 'tickTextWidth': 30, ## space reserved for tick text 'tickTextHeight': 18, 'autoExpandTextSpace': True, ## automatically expand text space if needed 'tickFont': None, 'stopAxisAtTick': (False, False), ## whether axis is drawn to edge of box or to last tick 'textFillLimits': [ ## how much of the axis to fill up with tick text, maximally. (0, 0.8), ## never fill more than 80% of the axis (2, 0.6), ## If we already have 2 ticks with text, fill no more than 60% of the axis (4, 0.4), ## If we already have 4 ticks with text, fill no more than 40% of the axis (6, 0.2), ## If we already have 6 ticks with text, fill no more than 20% of the axis ] } self.textWidth = 30 ## Keeps track of maximum width / height of tick text ... ... @@ -209,14 +215,14 @@ class AxisItem(GraphicsWidget): ## to accomodate. if self.orientation in ['left', 'right']: mx = max(self.textWidth, x) if mx > self.textWidth: if mx > self.textWidth or mx < self.textWidth-10: self.textWidth = mx if self.style['autoExpandTextSpace'] is True: self.setWidth() #return True ## size has changed else: mx = max(self.textHeight, x) if mx > self.textHeight: if mx > self.textHeight or mx < self.textHeight-10: self.textHeight = mx if self.style['autoExpandTextSpace'] is True: self.setHeight() ... ... @@ -236,7 +242,7 @@ class AxisItem(GraphicsWidget): h = self.textHeight else: h = self.style['tickTextHeight'] h += max(0, self.tickLength) + self.style['tickTextOffset'] h += max(0, self.tickLength) + self.style['tickTextOffset'][1] if self.label.isVisible(): h += self.label.boundingRect().height() * 0.8 self.setMaximumHeight(h) ... ... @@ -252,7 +258,7 @@ class AxisItem(GraphicsWidget): w = self.textWidth else: w = self.style['tickTextWidth'] w += max(0, self.tickLength) + self.style['tickTextOffset'] w += max(0, self.tickLength) + self.style['tickTextOffset'][0] if self.label.isVisible(): w += self.label.boundingRect().height() * 0.8 ## bounding rect is usually an overestimate self.setMaximumWidth(w) ... ... @@ -430,7 +436,7 @@ class AxisItem(GraphicsWidget): return [] ## decide optimal minor tick spacing in pixels (this is just aesthetics) pixelSpacing = np.log(size+10) * 5 pixelSpacing = size / np.log(size) optimalTickCount = max(2., size / pixelSpacing) ## optimal minor tick spacing ... ... @@ -720,7 +726,7 @@ class AxisItem(GraphicsWidget): textOffset = self.style['tickTextOffset'] ## spacing between axis and text textOffset = self.style['tickTextOffset'][axis] ## spacing between axis and text #if self.style['autoExpandTextSpace'] is True: #textWidth = self.textWidth #textHeight = self.textHeight ... ... @@ -728,7 +734,7 @@ class AxisItem(GraphicsWidget): #textWidth = self.style['tickTextWidth'] ## space allocated for horizontal text #textHeight = self.style['tickTextHeight'] ## space allocated for horizontal text textSize2 = 0 textRects = [] textSpecs = [] ## list of draw for i in range(len(tickLevels)): ... ... @@ -770,9 +776,16 @@ class AxisItem(GraphicsWidget): textSize = np.sum([r.width() for r in textRects]) textSize2 = np.max([r.height() for r in textRects]) ## If the strings are too crowded, stop drawing text now ## If the strings are too crowded, stop drawing text now. ## We use three different crowding limits based on the number ## of texts drawn so far. textFillRatio = float(textSize) / lengthInPixels if textFillRatio > 0.7: finished = False for nTexts, limit in self.style['textFillLimits']: if len(textSpecs) >= nTexts and textFillRatio >= limit: finished = True break if finished: break #spacing, values = tickLevels[best] ... ...
 ... ... @@ -533,6 +533,7 @@ class GraphicsItem(object): def viewTransformChanged(self): """ Called whenever the transformation matrix of the view has changed. (eg, the view range has changed or the view was resized) """ pass ... ...
 ... ... @@ -375,6 +375,7 @@ class PlotCurveItem(GraphicsObject): return QtGui.QPainterPath() return self.path @pg.debug.warnOnException ## raising an exception here causes crash def paint(self, p, opt, widget): prof = debug.Profiler('PlotCurveItem.paint '+str(id(self)), disabled=True) if self.xData is None: ... ...