def update_roi(self): axes=(0,1) # self.line_data = self.line_roi.getArrayRegion(self.zdata, self.imageItem, axes=(0,1)) img = self.imageItem imgPts = [self.line_roi.mapToItem(img, h['item'].pos()) for h in self.line_roi.handles] d = Point(imgPts[1] - imgPts[0]) o = Point(imgPts[0]) z = fn.affineSlice(self.zdata, shape=(int(d.length()),), vectors=[Point(d.norm())], origin=o, axes=axes, order=1) x = fn.affineSlice(self.xdata, shape=(int(d.length()),), vectors=[Point(d.norm())], origin=o, axes=axes, order=1) y = fn.affineSlice(self.ydata, shape=(int(d.length()),), vectors=[Point(d.norm())], origin=o, axes=axes, order=1) self.line_data = (x,y,z)
def movePoint(self, handle, pos, modifiers=QtCore.Qt.KeyboardModifier(), finish=True, coords='parent'): ## called by Handles when they are moved. ## pos is the new position of the handle in scene coords, as requested by the handle. newState = self.stateCopy() index = self.indexOfHandle(handle) h = self.handles[index] p0 = self.mapToParent(h['pos'] * self.state['size']) p1 = Point(pos) if coords == 'parent': pass elif coords == 'scene': p1 = self.mapSceneToParent(p1) else: raise Exception( "New point location must be given in either 'parent' or 'scene' coordinates." ) ## transform p0 and p1 into parent's coordinates (same as scene coords if there is no parent). I forget why. #p0 = self.mapSceneToParent(p0) #p1 = self.mapSceneToParent(p1) ## Handles with a 'center' need to know their local position relative to the center point (lp0, lp1) if 'center' in h: c = h['center'] cs = c * self.state['size'] lp0 = self.mapFromParent(p0) - cs lp1 = self.mapFromParent(p1) - cs if h['type'] == 't': snap = True if (modifiers & QtCore.Qt.ControlModifier) else None #if self.translateSnap or (): #snap = Point(self.snapSize, self.snapSize) self.translate(p1 - p0, snap=snap, update=False) elif h['type'] == 'f': newPos = self.mapFromParent(p1) h['item'].setPos(newPos) h['pos'] = newPos self.freeHandleMoved = True #self.sigRegionChanged.emit(self) ## should be taken care of by call to stateChanged() elif h['type'] == 's': ## If a handle and its center have the same x or y value, we can't scale across that axis. if h['center'][0] == h['pos'][0]: lp1[0] = 0 if h['center'][1] == h['pos'][1]: lp1[1] = 0 ## snap if self.scaleSnap or (modifiers & QtCore.Qt.ControlModifier): lp1[0] = round(lp1[0] / self.snapSize) * self.snapSize lp1[1] = round(lp1[1] / self.snapSize) * self.snapSize ## preserve aspect ratio (this can override snapping) if h['lockAspect'] or (modifiers & QtCore.Qt.AltModifier): #arv = Point(self.preMoveState['size']) - lp1 = lp1.proj(lp0) ## determine scale factors and new size of ROI hs = h['pos'] - c if hs[0] == 0: hs[0] = 1 if hs[1] == 0: hs[1] = 1 newSize = lp1 / hs ## Perform some corrections and limit checks if newSize[0] == 0: newSize[0] = newState['size'][0] if newSize[1] == 0: newSize[1] = newState['size'][1] if not self.invertible: if newSize[0] < 0: newSize[0] = newState['size'][0] if newSize[1] < 0: newSize[1] = newState['size'][1] if self.aspectLocked: newSize[0] = newSize[1] ## Move ROI so the center point occupies the same scene location after the scale s0 = c * self.state['size'] s1 = c * newSize cc = self.mapToParent(s0 - s1) - self.mapToParent(Point(0, 0)) ## update state, do more boundary checks newState['size'] = newSize newState['pos'] = newState['pos'] + cc if self.maxBounds is not None: r = self.stateRect(newState) if not self.maxBounds.contains(r): #Order of bounds checking is important ! #1. Bound x and y first, update width and height #if necessary #2. Then bound height and width target_x = newState['pos'][0] target_y = newState['pos'][1] target_width = newState['size'][0] target_height = newState['size'][1] #target x must be more than min_x but less than min__x # + max_width #update newState position values first newState['pos'][0] = min(max(self.maxBounds.x(), target_x), self.maxBounds.right()) newState['pos'][1] = min(max(self.maxBounds.y(), target_y), self.maxBounds.bottom()) #Adjusts width and height to make sure right and bottom #edges are the same despite new newState position values target_width = (target_width + target_x - newState['pos'][0]) target_height = (target_height + target_y - newState['pos'][1]) #Apply bound checking for updated target widths newState['size'][0] = min( target_width, self.maxBounds.right() - newState['pos'][0]) newState['size'][1] = min( target_height, self.maxBounds.bottom() - newState['pos'][1]) """End of edit""" """Start of old code return End of old code""" self.setPos(newState['pos'], update=False) self.setSize(newState['size'], update=False) elif h['type'] in ['r', 'rf']: if h['type'] == 'rf': self.freeHandleMoved = True if not self.rotateAllowed: return ## If the handle is directly over its center point, we can't compute an angle. try: if lp1.length() == 0 or lp0.length() == 0: return except OverflowError: return ## determine new rotation angle, constrained if necessary ang = newState['angle'] - lp0.angle(lp1) if ang is None: ## this should never appen.. return if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier): ang = round(ang / 15.) * 15. ## 180/12 = 15 ## create rotation transform tr = QtGui.QTransform() tr.rotate(ang) ## move ROI so that center point remains stationary after rotate cc = self.mapToParent(cs) - (tr.map(cs) + self.state['pos']) newState['angle'] = ang newState['pos'] = newState['pos'] + cc ## check boundaries, update if self.maxBounds is not None: r = self.stateRect(newState) if not self.maxBounds.contains(r): return #self.setTransform(tr) self.setPos(newState['pos'], update=False) self.setAngle(ang, update=False) #self.state = newState ## If this is a free-rotate handle, its distance from the center may change. if h['type'] == 'rf': h['item'].setPos(self.mapFromScene( p1)) ## changes ROI coordinates of handle elif h['type'] == 'sr': if h['center'][0] == h['pos'][0]: scaleAxis = 1 nonScaleAxis = 0 else: scaleAxis = 0 nonScaleAxis = 1 try: if lp1.length() == 0 or lp0.length() == 0: return except OverflowError: return ang = newState['angle'] - lp0.angle(lp1) if ang is None: return if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier): #ang = round(ang / (np.pi/12.)) * (np.pi/12.) ang = round(ang / 15.) * 15. hs = abs(h['pos'][scaleAxis] - c[scaleAxis]) newState['size'][scaleAxis] = lp1.length() / hs #if self.scaleSnap or (modifiers & QtCore.Qt.ControlModifier): if self.scaleSnap: ## use CTRL only for angular snap here. newState['size'][scaleAxis] = round( newState['size'][scaleAxis] / self.snapSize) * self.snapSize if newState['size'][scaleAxis] == 0: newState['size'][scaleAxis] = 1 if self.aspectLocked: newState['size'][nonScaleAxis] = newState['size'][scaleAxis] c1 = c * newState['size'] tr = QtGui.QTransform() tr.rotate(ang) cc = self.mapToParent(cs) - (tr.map(c1) + self.state['pos']) newState['angle'] = ang newState['pos'] = newState['pos'] + cc if self.maxBounds is not None: r = self.stateRect(newState) if not self.maxBounds.contains(r): return #self.setTransform(tr) #self.setPos(newState['pos'], update=False) #self.prepareGeometryChange() #self.state = newState self.setState(newState, update=False) self.stateChanged(finish=finish)
def scaleBy(self, s=None, center=None, x=None, y=None): if s is not None: scale = Point(s) else: scale = [x, y] affect = [True, True] if scale[0] is None and scale[1] is None: return elif scale[0] is None: affect[0] = False scale[0] = 1.0 elif scale[1] is None: affect[1] = False scale[1] = 1.0 scale = Point(scale) if self.state['aspectLocked'] is not False: scale[0] = scale[1] """Edit""" vr = self.viewRect() """End of Edit""" if center is None: center = Point(vr.center()) else: center = Point(center) tl = center + (vr.topLeft() - center) * scale br = center + (vr.bottomRight() - center) * scale """Addition""" yMax = self.state['limits']['yLimits'][1] xMax = self.state['limits']['xLimits'][1] xMin = self.state['limits']['xLimits'][0] yMin = self.state['limits']['yLimits'][0] scale_limit = [] if yMax is not None and affect[0]: if (br.y() > yMax): scale_limit.append((yMax - center.y()) / (1.0 * vr.bottomRight().y() - center.y())) print 'yMax scale is :' + str(scale_limit[-1]) if xMax is not None: if (br.x() > xMax) and affect[1]: scale_limit.append((xMax - center.x()) / (1.0 * vr.bottomRight().x() - center.x())) print 'xMax scale is :' + str(scale_limit[-1]) if xMin is not None: if (tl.x() < xMin) and affect[1]: scale_limit.append((xMin - center.x()) / (1.0 * vr.topLeft().x() - center.x())) print 'xMin scale is :' + str(scale_limit[-1]) if yMin is not None: if (tl.y() < yMin) and affect[0]: scale_limit.append((yMin - center.y()) / (1.0 * vr.topLeft().y() - center.y())) print 'yMin scale is :' + str(scale_limit[-1]) if self.state['aspectLocked'] is not False and len(scale_limit) > 0: min_scale_limit = min(scale_limit) scale[1] = min_scale_limit scale[0] = scale[1] tl = center + (vr.topLeft() - center) * scale br = center + (vr.bottomRight() - center) * scale """End of Addition""" if not affect[0]: self.setYRange(tl.y(), br.y(), padding=0) elif not affect[1]: self.setXRange(tl.x(), br.x(), padding=0) else: self.setRange(QtCore.QRectF(tl, br), padding=0)
view = w.addViewBox() view.setAspectLocked() #grid = pg.GridItem() #view.addItem(grid) view.setRange(pg.QtCore.QRectF(-50, -30, 100, 100)) optics = [] rays = [] m1 = Mirror(r1=-100, pos=(5, 0), d=5, angle=-15) optics.append(m1) m2 = Mirror(r1=-70, pos=(-40, 30), d=6, angle=180 - 15) optics.append(m2) allRays = [] for y in np.linspace(-10, 10, 21): r = Ray(start=Point(-100, y)) view.addItem(r) allRays.append(r) for o in optics: view.addItem(o) t1 = Tracer(allRays, optics) ### Dispersion demo optics = [] view = w.addViewBox() view.setAspectLocked()
def render(self): # Convert data to QImage for display. profile = debug.Profiler() if self.image is None or self.image.size == 0: return if isinstance(self.lut, collections.Callable): lut = self.lut(self.image) else: lut = self.lut if self.logScale: image = self.image + 1 with np.errstate(invalid="ignore"): image = image.astype(np.float) np.log(image, where=image >= 0, out=image) # map to 0-255 else: image = self.image if self.autoDownsample: # reduce dimensions of image based on screen resolution o = self.mapToDevice(QPointF(0, 0)) x = self.mapToDevice(QPointF(1, 0)) y = self.mapToDevice(QPointF(0, 1)) w = Point(x - o).length() h = Point(y - o).length() if w == 0 or h == 0: self.qimage = None return xds = max(1, int(1.0 / w)) yds = max(1, int(1.0 / h)) axes = [1, 0] if self.axisOrder == "row-major" else [0, 1] image = fn.downsample(image, xds, axis=axes[0]) image = fn.downsample(image, yds, axis=axes[1]) self._lastDownsample = (xds, yds) else: pass # if the image data is a small int, then we can combine levels + lut # into a single lut for better performance levels = self.levels if levels is not None and levels.ndim == 1 and image.dtype in ( np.ubyte, np.uint16): if self._effectiveLut is None: eflsize = 2**(image.itemsize * 8) ind = np.arange(eflsize) minlev, maxlev = levels levdiff = maxlev - minlev levdiff = 1 if levdiff == 0 else levdiff # don't allow division by 0 if lut is None: efflut = fn.rescaleData(ind, scale=255.0 / levdiff, offset=minlev, dtype=np.ubyte) else: lutdtype = np.min_scalar_type(lut.shape[0] - 1) efflut = fn.rescaleData(ind, scale=(lut.shape[0] - 1) / levdiff, offset=minlev, dtype=lutdtype, clip=(0, lut.shape[0] - 1)) efflut = lut[efflut] self._effectiveLut = efflut lut = self._effectiveLut levels = None # Assume images are in column-major order for backward compatibility # (most images are in row-major order) if self.axisOrder == "col-major": image = image.transpose((1, 0, 2)[:image.ndim]) if self.logScale: with np.errstate(invalid="ignore"): levels = np.log(np.add(levels, 1)) levels[0] = np.nanmax([levels[0], 0]) argb, alpha = fn.makeARGB(image, lut=lut, levels=levels) self.qimage = fn.makeQImage(argb, alpha, transpose=False)
def add_ctrl_pts(self, ctrl_pts): for (x, y) in ctrl_pts: self.ctrl_pts.append(Point(x, y)) self._update_spline()
def intersectRay(self, ray): ## return the point of intersection and the angle of incidence #print "intersect ray" h = self.h2 r = self.r p, dir = ray.currentState( relativeTo=self) # position and angle of ray in local coords. #print " ray: ", p, dir p = p - Point(r, 0) ## move position so center of circle is at 0,0 #print " adj: ", p, r if r == 0: #print " flat" if dir[0] == 0: y = 0 else: y = p[1] - p[0] * dir[1] / dir[0] if abs(y) > h: return None, None else: return (Point(0, y), np.arctan2(dir[1], dir[0])) else: #print " curve" ## find intersection of circle and line (quadratic formula) dx = dir[0] dy = dir[1] dr = (dx**2 + dy**2)**0.5 D = p[0] * (p[1] + dy) - (p[0] + dx) * p[1] idr2 = 1.0 / dr**2 disc = r**2 * dr**2 - D**2 if disc < 0: return None, None disc2 = disc**0.5 if dy < 0: sgn = -1 else: sgn = 1 br = self.path.boundingRect() x1 = (D * dy + sgn * dx * disc2) * idr2 y1 = (-D * dx + abs(dy) * disc2) * idr2 if br.contains(x1 + r, y1): pt = Point(x1, y1) else: x2 = (D * dy - sgn * dx * disc2) * idr2 y2 = (-D * dx - abs(dy) * disc2) * idr2 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]) if r < 0: norm += np.pi #print " norm:", norm*180/3.1415 dp = p - pt #print " dp:", dp ang = np.arctan2(dp[1], dp[0]) #print " ang:", ang*180/3.1415 #print " ai:", (ang-norm)*180/3.1415 #print " intersection:", pt return pt + Point(r, 0), ang - norm
def updateTransform(self): self.resetTransform() self.setPos(0, 0) self.translate(Point(self['pos'])) self.rotate(self['angle'])