Equilateral Triangle ROI (#1581)

* feature TriangleROI

Added equilateral  TriangleROI.
getArrayRegion is not working yet

* show off (and implicitly test) our new TriangleROI

* the PolyLineROI.getArrayRegion can be used by TriangleROI

refactor the actual logic to live on ROI

* refactors for readability

* variable names
* lint
* docstring type and purpose

Co-authored-by: Fekete Imre <feketeimre87@gmail.com>
This commit is contained in:
Martin Chase 2021-02-15 06:06:55 -08:00 committed by GitHub
parent a5a41cf8eb
commit 5210c55e67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 74 additions and 29 deletions

View File

@ -58,6 +58,7 @@ rois = []
rois.append(pg.RectROI([20, 20], [20, 20], pen=(0,9)))
rois[-1].addRotateHandle([1,0], [0.5, 0.5])
rois.append(pg.LineROI([0, 60], [20, 80], width=5, pen=(1,9)))
rois.append(pg.TriangleROI([80, 75], 20, pen=(5, 9)))
rois.append(pg.MultiRectROI([[20, 90], [50, 60], [60, 90]], width=5, pen=(2,9)))
rois.append(pg.EllipseROI([60, 10], [30, 20], pen=(3,9)))
rois.append(pg.CircleROI([80, 50], [20, 20], pen=(4,9)))

View File

@ -30,7 +30,7 @@ __all__ = [
'ROI',
'TestROI', 'RectROI', 'EllipseROI', 'CircleROI', 'PolygonROI',
'LineROI', 'MultiLineROI', 'MultiRectROI', 'LineSegmentROI', 'PolyLineROI',
'CrosshairROI',
'CrosshairROI','TriangleROI'
]
@ -1182,6 +1182,35 @@ class ROI(GraphicsObject):
mapped = fn.transformCoordinates(img.transform(), coords)
return result, mapped
def _getArrayRegionForArbitraryShape(self, data, img, axes=(0,1), **kwds):
"""
Return the result of :meth:`~pyqtgraph.ROI.getArrayRegion`, masked by
the shape of the ROI. Values outside the ROI shape are set to 0.
See :meth:`~pyqtgraph.ROI.getArrayRegion` for a description of the
arguments.
Note: ``returnMappedCoords`` is not yet supported for this ROI type.
"""
br = self.boundingRect()
if br.width() > 1000:
raise Exception()
sliced = ROI.getArrayRegion(self, data, img, axes=axes, fromBoundingRect=True, **kwds)
if img.axisOrder == "col-major":
mask = self.renderShapeMask(sliced.shape[axes[0]], sliced.shape[axes[1]])
else:
mask = self.renderShapeMask(sliced.shape[axes[1]], sliced.shape[axes[0]])
mask = mask.T
# reshape mask to ensure it is applied to the correct data axes
shape = [1] * data.ndim
shape[axes[0]] = sliced.shape[axes[0]]
shape[axes[1]] = sliced.shape[axes[1]]
mask = mask.reshape(shape)
return sliced * mask
def getAffineSliceParams(self, data, img, axes=(0,1), fromBoundingRect=False):
"""
Returns the parameters needed to use :func:`affineSlice <pyqtgraph.affineSlice>`
@ -2102,34 +2131,8 @@ class PolyLineROI(ROI):
p.lineTo(self.handles[0]['item'].pos())
return p
def getArrayRegion(self, data, img, axes=(0,1), **kwds):
"""
Return the result of :meth:`~pyqtgraph.ROI.getArrayRegion`, masked by
the shape of the ROI. Values outside the ROI shape are set to 0.
See :meth:`~pyqtgraph.ROI.getArrayRegion` for a description of the
arguments.
Note: ``returnMappedCoords`` is not yet supported for this ROI type.
"""
br = self.boundingRect()
if br.width() > 1000:
raise Exception()
sliced = ROI.getArrayRegion(self, data, img, axes=axes, fromBoundingRect=True, **kwds)
if img.axisOrder == 'col-major':
mask = self.renderShapeMask(sliced.shape[axes[0]], sliced.shape[axes[1]])
else:
mask = self.renderShapeMask(sliced.shape[axes[1]], sliced.shape[axes[0]])
mask = mask.T
# reshape mask to ensure it is applied to the correct data axes
shape = [1] * data.ndim
shape[axes[0]] = sliced.shape[axes[0]]
shape[axes[1]] = sliced.shape[axes[1]]
mask = mask.reshape(shape)
return sliced * mask
def getArrayRegion(self, *args, **kwds):
return self._getArrayRegionForArbitraryShape(*args, **kwds)
def setPen(self, *args, **kwds):
ROI.setPen(self, *args, **kwds)
@ -2344,3 +2347,44 @@ class RulerROI(LineSegmentROI):
return r
pxw = 50 * pxl
return r.adjusted(-50, -50, 50, 50)
class TriangleROI(ROI):
"""
Equilateral triangle ROI subclass with one scale handle and one rotation handle.
Arguments
pos (length-2 sequence) The position of the ROI's origin.
size (float) The length of an edge of the triangle.
\**args All extra keyword arguments are passed to 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)
verticies = (np.array((np.sin(angles), np.cos(angles))).T + 1.0) / 2.0
self.poly = QtGui.QPolygonF()
for pt in verticies:
self.poly.append(QtCore.QPointF(*pt))
self.addRotateHandle(verticies[0], [0.5, 0.5])
self.addScaleHandle(verticies[1], [0.5, 0.5])
def paint(self, p, *args):
r = self.boundingRect()
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.scale(r.width(), r.height())
p.setPen(self.currentPen)
p.drawPolygon(self.poly)
def shape(self):
self.path = QtGui.QPainterPath()
r = self.boundingRect()
# scale the path to match whats on the screen
t = QtGui.QTransform()
t.scale(r.width(), r.height())
self.path.addPolygon(self.poly)
return t.map(self.path)
def getArrayRegion(self, *args, **kwds):
return self._getArrayRegionForArbitraryShape(*args, **kwds)